# HG changeset patch # User Marcin Kuzminski # Date 2016-07-06 22:50:39 # Node ID 2ba4c17163d05b0f0eabc62b370ffbca490b0956 # Parent 52964495770b171bdf99ebb01c719d69b4e2bd6d readme/markup: improved order of generating readme files. Fixes #4050 - we now use order based on default system renderer - having multiple readme files will pick correct one as set renderer diff --git a/rhodecode/config/conf.py b/rhodecode/config/conf.py --- a/rhodecode/config/conf.py +++ b/rhodecode/config/conf.py @@ -30,36 +30,6 @@ from rhodecode.lib.utils2 import __get_l # extensions will index it's content LANGUAGES_EXTENSIONS_MAP = __get_lem() -# list of readme files to search in file tree and display in summary -# attached weights defines the search order lower is first -ALL_READMES = [ - ('readme', 0), ('README', 0), ('Readme', 0), - ('doc/readme', 1), ('doc/README', 1), ('doc/Readme', 1), - ('Docs/readme', 2), ('Docs/README', 2), ('Docs/Readme', 2), - ('DOCS/readme', 2), ('DOCS/README', 2), ('DOCS/Readme', 2), - ('docs/readme', 2), ('docs/README', 2), ('docs/Readme', 2), -] - -# extension together with weights to search lower is first -RST_EXTS = [ - ('', 0), ('.rst', 1), ('.rest', 1), - ('.RST', 2), ('.REST', 2) -] - -MARKDOWN_EXTS = [ - ('.md', 1), ('.MD', 1), - ('.mkdn', 2), ('.MKDN', 2), - ('.mdown', 3), ('.MDOWN', 3), - ('.markdown', 4), ('.MARKDOWN', 4) -] - -PLAIN_EXTS = [ - ('.text', 2), ('.TEXT', 2), - ('.txt', 3), ('.TXT', 3) -] - -ALL_EXTS = MARKDOWN_EXTS + RST_EXTS + PLAIN_EXTS - DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S" DATE_FORMAT = "%Y-%m-%d" diff --git a/rhodecode/controllers/summary.py b/rhodecode/controllers/summary.py --- a/rhodecode/controllers/summary.py +++ b/rhodecode/controllers/summary.py @@ -24,14 +24,12 @@ Summary controller for RhodeCode Enterpr import logging from string import lower -from itertools import product from pylons import tmpl_context as c, request from pylons.i18n.translation import _ from beaker.cache import cache_region, region_invalidate -from rhodecode.config.conf import ( - ALL_READMES, ALL_EXTS, LANGUAGES_EXTENSIONS_MAP) +from rhodecode.config.conf import (LANGUAGES_EXTENSIONS_MAP) from rhodecode.controllers import utils from rhodecode.controllers.changelog import _load_changelog_summary from rhodecode.lib import caches, helpers as h @@ -49,10 +47,6 @@ from rhodecode.model.db import Statistic log = logging.getLogger(__name__) -README_FILES = [''.join([x[0][0], x[1][0]]) - for x in sorted(list(product(ALL_READMES, ALL_EXTS)), - key=lambda y:y[0][1] + y[1][1])] - class SummaryController(BaseRepoController): @@ -62,6 +56,7 @@ class SummaryController(BaseRepoControll def __get_readme_data(self, db_repo): repo_name = db_repo.repo_name log.debug('Looking for README file') + default_renderer = c.visual.default_renderer @cache_region('long_term') def _generate_readme(cache_key): @@ -73,7 +68,7 @@ class SummaryController(BaseRepoControll if isinstance(commit, EmptyCommit): raise EmptyRepositoryError() renderer = MarkupRenderer() - for f in README_FILES: + for f in renderer.pick_readme_order(default_renderer): try: node = commit.get_node(f) except NodeDoesNotExistError: diff --git a/rhodecode/lib/helpers.py b/rhodecode/lib/helpers.py --- a/rhodecode/lib/helpers.py +++ b/rhodecode/lib/helpers.py @@ -1726,17 +1726,7 @@ def markdown(source, mentions=False): mentions=mentions)) def renderer_from_filename(filename, exclude=None): - from rhodecode.config.conf import MARKDOWN_EXTS, RST_EXTS - - def _filter(elements): - if isinstance(exclude, (list, tuple)): - return [x for x in elements if x not in exclude] - return elements - - if filename.endswith(tuple(_filter([x[0] for x in MARKDOWN_EXTS if x[0]]))): - return 'markdown' - if filename.endswith(tuple(_filter([x[0] for x in RST_EXTS if x[0]]))): - return 'rst' + return MarkupRenderer.renderer_from_filename(filename, exclude=exclude) def render(source, renderer='rst', mentions=False): diff --git a/rhodecode/lib/markup_renderer.py b/rhodecode/lib/markup_renderer.py --- a/rhodecode/lib/markup_renderer.py +++ b/rhodecode/lib/markup_renderer.py @@ -23,10 +23,11 @@ Renderer for markup languages with ability to parse using rst or markdown """ - import re import os import logging +import itertools + from mako.lookup import TemplateLookup from docutils.core import publish_parts @@ -50,6 +51,37 @@ class MarkupRenderer(object): RST_PAT = re.compile(r'\.re?st$', re.IGNORECASE) PLAIN_PAT = re.compile(r'^readme$', re.IGNORECASE) + # list of readme files to search in file tree and display in summary + # attached weights defines the search order lower is first + ALL_READMES = [ + ('readme', 0), ('README', 0), ('Readme', 0), + ('doc/readme', 1), ('doc/README', 1), ('doc/Readme', 1), + ('Docs/readme', 2), ('Docs/README', 2), ('Docs/Readme', 2), + ('DOCS/readme', 2), ('DOCS/README', 2), ('DOCS/Readme', 2), + ('docs/readme', 2), ('docs/README', 2), ('docs/Readme', 2), + ] + # extension together with weights. Lower is first means we control how + # extensions are attached to readme names with those. + PLAIN_EXTS = [ + ('', 0), # special case that renders READMES names without extension + ('.text', 2), ('.TEXT', 2), + ('.txt', 3), ('.TXT', 3) + ] + + RST_EXTS = [ + ('.rst', 1), ('.rest', 1), + ('.RST', 2), ('.REST', 2) + ] + + MARKDOWN_EXTS = [ + ('.md', 1), ('.MD', 1), + ('.mkdn', 2), ('.MKDN', 2), + ('.mdown', 3), ('.MDOWN', 3), + ('.markdown', 4), ('.MARKDOWN', 4) + ] + + ALL_EXTS = PLAIN_EXTS + MARKDOWN_EXTS + RST_EXTS + def _detect_renderer(self, source, filename=None): """ runs detection of what renderer should be used for generating html @@ -72,6 +104,48 @@ class MarkupRenderer(object): return getattr(MarkupRenderer, detected_renderer) + @classmethod + def renderer_from_filename(cls, filename, exclude): + """ + Detect renderer from filename and optionally use exlcude list to + remove some options. This is mostly used in helpers + """ + def _filter(elements): + if isinstance(exclude, (list, tuple)): + return [x for x in elements if x not in exclude] + return elements + + if filename.endswith( + tuple(_filter([x[0] for x in cls.MARKDOWN_EXTS if x[0]]))): + return 'markdown' + if filename.endswith(tuple(_filter([x[0] for x in cls.RST_EXTS if x[0]]))): + return 'rst' + + return 'plain' + + @classmethod + def generate_readmes(cls, all_readmes, extensions): + combined = itertools.product(all_readmes, extensions) + # sort by filename weight(y[0][1]) + extensions weight(y[1][1]) + prioritized_readmes = sorted(combined, key=lambda y: y[0][1] + y[1][1]) + # filename, extension + return [''.join([x[0][0], x[1][0]]) for x in prioritized_readmes] + + def pick_readme_order(self, default_renderer): + + if default_renderer == 'markdown': + markdown = self.generate_readmes(self.ALL_READMES, self.MARKDOWN_EXTS) + readme_order = markdown + self.generate_readmes( + self.ALL_READMES, self.RST_EXTS + self.PLAIN_EXTS) + elif default_renderer == 'rst': + markdown = self.generate_readmes(self.ALL_READMES, self.RST_EXTS) + readme_order = markdown + self.generate_readmes( + self.ALL_READMES, self.MARKDOWN_EXTS + self.PLAIN_EXTS) + else: + readme_order = self.generate_readmes(self.ALL_READMES, self.ALL_EXTS) + + return readme_order + def render(self, source, filename=None): """ Renders a given filename using detected renderer diff --git a/rhodecode/tests/lib/test_markup_renderer.py b/rhodecode/tests/lib/test_markup_renderer.py --- a/rhodecode/tests/lib/test_markup_renderer.py +++ b/rhodecode/tests/lib/test_markup_renderer.py @@ -177,3 +177,37 @@ Auto status change to |new_status| renderer = RstTemplateRenderer() rendered = renderer.render('auto_status_change.mako', **params) assert expected == rendered + + +@pytest.mark.parametrize( + "readmes, exts, order", + [ + ([], [], []), + + ([('readme1', 0), ('text1', 1)], [('.ext', 0), ('.txt', 1)], + ['readme1.ext', 'readme1.txt', 'text1.ext', 'text1.txt']), + + ([('readme2', 0), ('text2', 1)], [('.ext', 2), ('.txt', 1)], + ['readme2.txt', 'readme2.ext', 'text2.txt', 'text2.ext']), + + ([('readme3', 0), ('text3', 1)], [('.XXX', 1)], + ['readme3.XXX', 'text3.XXX']), + ]) +def test_generate_readmes(readmes, exts, order): + assert order == MarkupRenderer.generate_readmes(readmes, exts) + + +@pytest.mark.parametrize( + "renderer, expected_order", + [ + ('plain', ['readme', 'README', 'Readme']), + ('text', ['readme', 'README', 'Readme']), + ('markdown', MarkupRenderer.generate_readmes( + MarkupRenderer.ALL_READMES, MarkupRenderer.MARKDOWN_EXTS)), + ('rst', MarkupRenderer.generate_readmes( + MarkupRenderer.ALL_READMES, MarkupRenderer.RST_EXTS)), + ]) +def test_order_of_readme_generation(renderer, expected_order): + mkd_renderer = MarkupRenderer() + assert expected_order == mkd_renderer.pick_readme_order( + renderer)[:len(expected_order)]