Files
zulip/zerver/lib/bugdown/api_code_examples.py
Eeshan Garg 09721e9ec3 bugdown/api_code_examples: Support multiple fixtures per doc.
This commit adds support for passing in an argument to the macro
"call" to explicitly specify a fixture to render, like so:

{generate_code_example|doc_name|fixture(stream_message_with_args)}
2018-02-07 15:58:57 -08:00

150 lines
4.8 KiB
Python

import re
import os
import sys
import ujson
import inspect
from markdown.extensions import Extension
from markdown.preprocessors import Preprocessor
from typing import Any, Dict, Optional, List
import markdown
import zerver.lib.api_test_helpers
REGEXP = re.compile(r'\{generate_code_example\|\s*(.+?)\s*\|\s*(.+?)\s*(\(\s*(.+?)\s*\))?\}')
PYTHON_CLIENT_CONFIG_LINES = """
#!/usr/bin/env python3
import zulip
# Download ~/zuliprc-dev from your dev server
client = zulip.Client(config_file="~/zuliprc-dev")
"""
PYTHON_CLIENT_ADMIN_CONFIG = """
#!/usr/bin/env python
import zulip
# You need a zuliprc-admin with administrator credentials
client = zulip.Client(config_file="~/zuliprc-admin")
"""
class APICodeExamplesGenerator(Extension):
def extendMarkdown(self, md: markdown.Markdown, md_globals: Dict[str, Any]) -> None:
md.preprocessors.add(
'generate_code_example', APICodeExamplesPreprocessor(md, self.getConfigs()), '_begin'
)
class APICodeExamplesPreprocessor(Preprocessor):
def __init__(self, md: markdown.Markdown, config: Dict[str, Any]) -> None:
super(APICodeExamplesPreprocessor, self).__init__(md)
def run(self, lines: List[str]) -> List[str]:
done = False
while not done:
for line in lines:
loc = lines.index(line)
match = REGEXP.search(line)
if match:
function = match.group(1)
key = match.group(2)
argument = match.group(4)
if key == 'fixture':
if argument:
text = self.render_fixture(function, name=argument)
else:
text = self.render_fixture(function)
elif key == 'example':
if argument == 'admin_config=True':
text = self.render_code_example(function, admin_config=True)
else:
text = self.render_code_example(function)
# The line that contains the directive to include the macro
# may be preceded or followed by text or tags, in that case
# we need to make sure that any preceding or following text
# stays the same.
line_split = REGEXP.split(line, maxsplit=0)
preceding = line_split[0]
following = line_split[-1]
text = [preceding] + text + [following]
lines = lines[:loc] + text + lines[loc+1:]
break
else:
done = True
return lines
def render_fixture(self, function: str, name: Optional[str]=None) -> List[str]:
fixture = []
if name:
fixture_dict = zerver.lib.api_test_helpers.FIXTURES[function][name]
else:
fixture_dict = zerver.lib.api_test_helpers.FIXTURES[function]
fixture_json = ujson.dumps(fixture_dict, indent=4, sort_keys=True)
fixture.append('```')
fixture.extend(fixture_json.splitlines())
fixture.append('```')
return fixture
def render_code_example(self, function: str, admin_config: Optional[bool]=False) -> List[str]:
method = zerver.lib.api_test_helpers.TEST_FUNCTIONS[function]
function_source_lines = inspect.getsourcelines(method)[0]
if admin_config:
config = PYTHON_CLIENT_ADMIN_CONFIG.splitlines()
else:
config = PYTHON_CLIENT_CONFIG_LINES.splitlines()
snippet = self.extractCodeExample(function_source_lines, [])
code_example = []
code_example.append('```python')
code_example.extend(config)
for line in snippet:
# Remove one level of indentation and strip newlines
code_example.append(line[4:].rstrip())
code_example.append('```')
return code_example
def extractCodeExample(self, source: List[str], snippet: List[str]) -> List[str]:
ce_regex = re.compile(r'\# \{code_example\|\s*(.+?)\s*\}')
start = -1
end = -1
for line in source:
match = ce_regex.search(line)
if match:
if match.group(1) == 'start':
start = source.index(line)
elif match.group(1) == 'end':
end = source.index(line)
break
if (start == -1 and end == -1):
return snippet
snippet.extend(source[start + 1: end])
snippet.append(' print(result)')
snippet.append('\n')
source = source[end + 1:]
return self.extractCodeExample(source, snippet)
def makeExtension(*args: Any, **kwargs: str) -> APICodeExamplesGenerator:
return APICodeExamplesGenerator(kwargs)