From a8f1881d9b2f65764caf289c6b0962642c3b5100 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Thu, 31 Aug 2017 09:20:37 +0200 Subject: [PATCH 1/4] add 'docstrings-with-pygments' feature without packages checks and tests --- rest_framework/templatetags/rest_framework.py | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index a2ee5ccddf..0f355ac0d0 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -66,11 +66,45 @@ def form_for_link(link): return mark_safe(coreschema.render_to_form(schema)) +from markdown.preprocessors import Preprocessor +from pygments.formatters import HtmlFormatter +from pygments.lexers import get_lexer_by_name, TextLexer +from pygments import highlight +import pygments +import re + +# starting from this blogpost and modified to support current markdown extensions API +# https://zerokspot.com/weblog/2008/06/18/syntax-highlighting-in-markdown-with-pygments/ +class CodeBlockPreprocessor(Preprocessor): + pattern = re.compile( + r'^\s*@@ (.+?) @@\s*(.+?)^\s*@@', re.M|re.S) + + formatter = HtmlFormatter() + + def run(self, lines): + def repl(m): + try: + lexer = get_lexer_by_name(m.group(1)) + except (ValueError, NameError): + lexer = TextLexer() + code = m.group(2).replace('\t',' ') + code = pygments.highlight(code, lexer, self.formatter) + code = code.replace('\n\n', '\n \n').replace('\n', '
').replace('\\@','@') + return '\n\n%s\n\n' % code + # import ipdb ; ipdb.set_trace() + ret = self.pattern.sub(repl, "\n".join(lines)) + return ret.split("\n") + + @register.simple_tag def render_markdown(markdown_text): if not markdown: return markdown_text - return mark_safe(markdown.markdown(markdown_text)) + md = markdown.Markdown() + md.preprocessors.add('highlight', CodeBlockPreprocessor(), "_begin") + + a = md.convert(markdown_text) + return mark_safe(md.convert(markdown_text)) @register.simple_tag From cf759a6fe8b8b17dbcac706636daee300205d892 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Thu, 28 Sep 2017 16:56:54 +0200 Subject: [PATCH 2/4] move syntax_highlight doc filter in compatibility module and define it conditionally --- rest_framework/compat.py | 27 +++++++++++++ rest_framework/templatetags/rest_framework.py | 38 +++---------------- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index e0f718cedc..115011fd37 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -273,6 +273,32 @@ def pygments_highlight(text, lang, style): def pygments_css(style): return None +if markdown is not None and pygments is not None: + class CodeBlockPreprocessor(Preprocessor): + pattern = re.compile( + r'^\s*@@ (.+?) @@\s*(.+?)^\s*@@', re.M|re.S) + + formatter = HtmlFormatter() + + def run(self, lines): + def repl(m): + try: + lexer = get_lexer_by_name(m.group(1)) + except (ValueError, NameError): + lexer = TextLexer() + code = m.group(2).replace('\t',' ') + code = pygments.highlight(code, lexer, self.formatter) + code = code.replace('\n\n', '\n \n').replace('\n', '
').replace('\\@','@') + return '\n\n%s\n\n' % code + ret = self.pattern.sub(repl, "\n".join(lines)) + return ret.split("\n") + + def md_filter_add_syntax_highlight(md): + md.preprocessors.add('highlight', CodeBlockPreprocessor(), "_begin") + return True +else: + def md_filter_add_syntax_highlight(md): + return False try: import pytz @@ -374,3 +400,4 @@ def include(module, namespace=None, app_name=None): return include(module, namespace, app_name) else: return include((module, app_name), namespace) + diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index 0f355ac0d0..7002f0b9f8 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -11,7 +11,8 @@ from django.utils.safestring import SafeData, mark_safe from rest_framework.compat import ( - NoReverseMatch, markdown, pygments_highlight, reverse, template_render + NoReverseMatch, markdown, pygments_highlight, reverse, template_render, + md_filter_add_syntax_highlight, ) from rest_framework.renderers import HTMLFormRenderer from rest_framework.utils.urls import replace_query_param @@ -66,44 +67,15 @@ def form_for_link(link): return mark_safe(coreschema.render_to_form(schema)) -from markdown.preprocessors import Preprocessor -from pygments.formatters import HtmlFormatter -from pygments.lexers import get_lexer_by_name, TextLexer -from pygments import highlight -import pygments -import re - -# starting from this blogpost and modified to support current markdown extensions API -# https://zerokspot.com/weblog/2008/06/18/syntax-highlighting-in-markdown-with-pygments/ -class CodeBlockPreprocessor(Preprocessor): - pattern = re.compile( - r'^\s*@@ (.+?) @@\s*(.+?)^\s*@@', re.M|re.S) - - formatter = HtmlFormatter() - - def run(self, lines): - def repl(m): - try: - lexer = get_lexer_by_name(m.group(1)) - except (ValueError, NameError): - lexer = TextLexer() - code = m.group(2).replace('\t',' ') - code = pygments.highlight(code, lexer, self.formatter) - code = code.replace('\n\n', '\n \n').replace('\n', '
').replace('\\@','@') - return '\n\n%s\n\n' % code - # import ipdb ; ipdb.set_trace() - ret = self.pattern.sub(repl, "\n".join(lines)) - return ret.split("\n") - - @register.simple_tag def render_markdown(markdown_text): if not markdown: return markdown_text md = markdown.Markdown() - md.preprocessors.add('highlight', CodeBlockPreprocessor(), "_begin") - a = md.convert(markdown_text) + # add pygments syntax highlight if pygments package is available + md_filter_add_syntax_highlight(md) + return mark_safe(md.convert(markdown_text)) From 1884c66cdb297a4c6c10f645e5b7d22de2a8c37b Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Thu, 28 Sep 2017 17:00:42 +0200 Subject: [PATCH 3/4] typo fixed --- rest_framework/compat.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 115011fd37..b4b67ba177 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -400,4 +400,3 @@ def include(module, namespace=None, app_name=None): return include(module, namespace, app_name) else: return include((module, app_name), namespace) - From d8db8da4ff52f89bdc3b24bf38648cede0409647 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Mon, 2 Oct 2017 11:22:18 +0200 Subject: [PATCH 4/4] add test for optional code highlight ('pygments' and 'markdown' packages must be installed) --- rest_framework/compat.py | 7 ++++++ tests/test_description.py | 52 ++++++++++++++++++++++++++++++++++----- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index b4b67ba177..3b341a6565 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -244,6 +244,7 @@ def apply_markdown(text): md = markdown.Markdown( extensions=extensions, extension_configs=extension_configs ) + md_filter_add_syntax_highlight(md) return md.convert(text) except ImportError: apply_markdown = None @@ -274,6 +275,12 @@ def pygments_css(style): return None if markdown is not None and pygments is not None: + # starting from this blogpost and modified to support current markdown extensions API + # https://zerokspot.com/weblog/2008/06/18/syntax-highlighting-in-markdown-with-pygments/ + + from markdown.preprocessors import Preprocessor + import re + class CodeBlockPreprocessor(Preprocessor): pattern = re.compile( r'^\s*@@ (.+?) @@\s*(.+?)^\s*@@', re.M|re.S) diff --git a/tests/test_description.py b/tests/test_description.py index 4df14ac557..a97550ed8d 100644 --- a/tests/test_description.py +++ b/tests/test_description.py @@ -24,11 +24,36 @@ indented -# hash style header #""" +# hash style header # + +@@ json @@ +[{ + "alpha": 1, + "beta: "this is a string" +}] +@@""" # If markdown is installed we also test it's working # (and that our wrapped forces '=' to h2 and '-' to h3) +MARKED_DOWN_HILITE = """ +
[{
"alpha": 1,
\ + "beta: "this\ + is a \ +string"
}]
+ +


""" + +MARKED_DOWN_NOT_HILITE = """ +

@@ json @@ +[{ + "alpha": 1, + "beta: "this is a string" +}] +@@

""" + # We support markdown < 2.1 and markdown >= 2.1 MARKED_DOWN_lt_21 = """

an example docstring

    @@ -39,7 +64,7 @@
    code block
     

    indented

    -

    hash style header

    """ +

    hash style header

    %s""" MARKED_DOWN_gte_21 = """

    an example docstring

      @@ -50,7 +75,7 @@
      code block
       

      indented

      -

      hash style header

      """ +

      hash style header

      %s""" class TestViewNamesAndDescriptions(TestCase): @@ -78,7 +103,14 @@ class MockView(APIView): indented - # hash style header #""" + # hash style header # + + @@ json @@ + [{ + "alpha": 1, + "beta: "this is a string" + }] + @@""" assert MockView().get_view_description() == DESCRIPTION @@ -118,8 +150,16 @@ def test_markdown(self): Ensure markdown to HTML works as expected. """ if apply_markdown: - gte_21_match = apply_markdown(DESCRIPTION) == MARKED_DOWN_gte_21 - lt_21_match = apply_markdown(DESCRIPTION) == MARKED_DOWN_lt_21 + gte_21_match = ( + apply_markdown(DESCRIPTION) == ( + MARKED_DOWN_gte_21 % MARKED_DOWN_HILITE) or + apply_markdown(DESCRIPTION) == ( + MARKED_DOWN_gte_21 % MARKED_DOWN_NOT_HILITE)) + lt_21_match = ( + apply_markdown(DESCRIPTION) == ( + MARKED_DOWN_lt_21 % MARKED_DOWN_HILITE) or + apply_markdown(DESCRIPTION) == ( + MARKED_DOWN_lt_21 % MARKED_DOWN_NOT_HILITE)) assert gte_21_match or lt_21_match