mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	api docs: Add Markdown extension for generating examples/fixtures.
This commit adds a Markdown extension that allows the following
syntax,
{generate_code_example|<md_file_name>|<fixture or method>}
to generate code examples and fixtures found in tools/lib/api_tests.py
and templates/zerver/api/fixtures.json, respectively.
			
			
This commit is contained in:
		
							
								
								
									
										116
									
								
								zerver/lib/bugdown/api_code_examples.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								zerver/lib/bugdown/api_code_examples.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
ZULIP_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
 | 
			
		||||
TOOLS_DIR = os.path.join(ZULIP_DIR, 'tools', 'lib')
 | 
			
		||||
sys.path.insert(0, TOOLS_DIR)
 | 
			
		||||
 | 
			
		||||
import api_tests
 | 
			
		||||
 | 
			
		||||
REGEXP = re.compile(r'\{generate_code_example\|\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")
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
 | 
			
		||||
                    if key == 'fixture':
 | 
			
		||||
                        text = self.render_fixture(function)
 | 
			
		||||
                    elif key == 'method':
 | 
			
		||||
                        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) -> List[str]:
 | 
			
		||||
        fixture = []
 | 
			
		||||
 | 
			
		||||
        fixture_dict = api_tests.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) -> List[str]:
 | 
			
		||||
        method = api_tests.TEST_FUNCTIONS[function]
 | 
			
		||||
        function_source_lines = inspect.getsourcelines(method)[0]
 | 
			
		||||
        ce_regex = re.compile(r'\# \{code_example\|\s*(.+?)\s*\}')
 | 
			
		||||
 | 
			
		||||
        start = 0
 | 
			
		||||
        end = 0
 | 
			
		||||
        for line in function_source_lines:
 | 
			
		||||
            match = ce_regex.search(line)
 | 
			
		||||
            if match:
 | 
			
		||||
                if match.group(1) == 'start':
 | 
			
		||||
                    start = function_source_lines.index(line)
 | 
			
		||||
                elif match.group(1) == 'end':
 | 
			
		||||
                    end = function_source_lines.index(line)
 | 
			
		||||
 | 
			
		||||
        config = PYTHON_CLIENT_CONFIG_LINES.splitlines()
 | 
			
		||||
        snippet = function_source_lines[start + 1: end]
 | 
			
		||||
 | 
			
		||||
        code_example = []
 | 
			
		||||
        code_example.append('```')
 | 
			
		||||
        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('print(result)')
 | 
			
		||||
        code_example.append('```')
 | 
			
		||||
 | 
			
		||||
        return code_example
 | 
			
		||||
 | 
			
		||||
def makeExtension(*args: Any, **kwargs: str) -> APICodeExamplesGenerator:
 | 
			
		||||
    return APICodeExamplesGenerator(kwargs)
 | 
			
		||||
		Reference in New Issue
	
	Block a user