Skip to content

Commit 063534a

Browse files
nastasi-oqCarlton Gibson
authored andcommitted
Docstrings highlighting with pygments (#5462)
* add 'docstrings-with-pygments' feature without packages checks and tests * move syntax_highlight doc filter in compatibility module and define it conditionally * typo fixed * add test for optional code highlight ('pygments' and 'markdown' packages must be installed)
1 parent 62ecbf2 commit 063534a

File tree

2 files changed

+79
-6
lines changed

2 files changed

+79
-6
lines changed

rest_framework/compat.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ def apply_markdown(text):
244244
md = markdown.Markdown(
245245
extensions=extensions, extension_configs=extension_configs
246246
)
247+
md_filter_add_syntax_highlight(md)
247248
return md.convert(text)
248249
except ImportError:
249250
apply_markdown = None
@@ -273,6 +274,38 @@ def pygments_highlight(text, lang, style):
273274
def pygments_css(style):
274275
return None
275276

277+
if markdown is not None and pygments is not None:
278+
# starting from this blogpost and modified to support current markdown extensions API
279+
# https://zerokspot.com/weblog/2008/06/18/syntax-highlighting-in-markdown-with-pygments/
280+
281+
from markdown.preprocessors import Preprocessor
282+
import re
283+
284+
class CodeBlockPreprocessor(Preprocessor):
285+
pattern = re.compile(
286+
r'^\s*@@ (.+?) @@\s*(.+?)^\s*@@', re.M|re.S)
287+
288+
formatter = HtmlFormatter()
289+
290+
def run(self, lines):
291+
def repl(m):
292+
try:
293+
lexer = get_lexer_by_name(m.group(1))
294+
except (ValueError, NameError):
295+
lexer = TextLexer()
296+
code = m.group(2).replace('\t',' ')
297+
code = pygments.highlight(code, lexer, self.formatter)
298+
code = code.replace('\n\n', '\n&nbsp;\n').replace('\n', '<br />').replace('\\@','@')
299+
return '\n\n%s\n\n' % code
300+
ret = self.pattern.sub(repl, "\n".join(lines))
301+
return ret.split("\n")
302+
303+
def md_filter_add_syntax_highlight(md):
304+
md.preprocessors.add('highlight', CodeBlockPreprocessor(), "_begin")
305+
return True
306+
else:
307+
def md_filter_add_syntax_highlight(md):
308+
return False
276309

277310
try:
278311
import pytz

tests/test_description.py

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,36 @@
2424
2525
indented
2626
27-
# hash style header #"""
27+
# hash style header #
28+
29+
@@ json @@
30+
[{
31+
"alpha": 1,
32+
"beta: "this is a string"
33+
}]
34+
@@"""
2835

2936
# If markdown is installed we also test it's working
3037
# (and that our wrapped forces '=' to h2 and '-' to h3)
3138

39+
MARKED_DOWN_HILITE = """
40+
<div class="highlight"><pre><span></span><span \
41+
class="p">[{</span><br /> <span class="nt">&quot;alpha&quot;</span><span\
42+
class="p">:</span> <span class="mi">1</span><span class="p">,</span><br />\
43+
<span class="nt">&quot;beta: &quot;</span><span class="err">this</span>\
44+
<span class="err">is</span> <span class="err">a</span> <span class="err">\
45+
string&quot;</span><br /><span class="p">}]</span><br /></pre></div>
46+
47+
<p><br /></p>"""
48+
49+
MARKED_DOWN_NOT_HILITE = """
50+
<p>@@ json @@
51+
[{
52+
"alpha": 1,
53+
"beta: "this is a string"
54+
}]
55+
@@</p>"""
56+
3257
# We support markdown < 2.1 and markdown >= 2.1
3358
MARKED_DOWN_lt_21 = """<h2>an example docstring</h2>
3459
<ul>
@@ -39,7 +64,7 @@
3964
<pre><code>code block
4065
</code></pre>
4166
<p>indented</p>
42-
<h2 id="hash_style_header">hash style header</h2>"""
67+
<h2 id="hash_style_header">hash style header</h2>%s"""
4368

4469
MARKED_DOWN_gte_21 = """<h2 id="an-example-docstring">an example docstring</h2>
4570
<ul>
@@ -50,7 +75,7 @@
5075
<pre><code>code block
5176
</code></pre>
5277
<p>indented</p>
53-
<h2 id="hash-style-header">hash style header</h2>"""
78+
<h2 id="hash-style-header">hash style header</h2>%s"""
5479

5580

5681
class TestViewNamesAndDescriptions(TestCase):
@@ -78,7 +103,14 @@ class MockView(APIView):
78103
79104
indented
80105
81-
# hash style header #"""
106+
# hash style header #
107+
108+
@@ json @@
109+
[{
110+
"alpha": 1,
111+
"beta: "this is a string"
112+
}]
113+
@@"""
82114

83115
assert MockView().get_view_description() == DESCRIPTION
84116

@@ -118,8 +150,16 @@ def test_markdown(self):
118150
Ensure markdown to HTML works as expected.
119151
"""
120152
if apply_markdown:
121-
gte_21_match = apply_markdown(DESCRIPTION) == MARKED_DOWN_gte_21
122-
lt_21_match = apply_markdown(DESCRIPTION) == MARKED_DOWN_lt_21
153+
gte_21_match = (
154+
apply_markdown(DESCRIPTION) == (
155+
MARKED_DOWN_gte_21 % MARKED_DOWN_HILITE) or
156+
apply_markdown(DESCRIPTION) == (
157+
MARKED_DOWN_gte_21 % MARKED_DOWN_NOT_HILITE))
158+
lt_21_match = (
159+
apply_markdown(DESCRIPTION) == (
160+
MARKED_DOWN_lt_21 % MARKED_DOWN_HILITE) or
161+
apply_markdown(DESCRIPTION) == (
162+
MARKED_DOWN_lt_21 % MARKED_DOWN_NOT_HILITE))
123163
assert gte_21_match or lt_21_match
124164

125165

0 commit comments

Comments
 (0)