Source code for fixtures.nelson

import os
import re
import base64
from textwrap import dedent
from types import FunctionType

from six import iteritems
from sphinxcontrib.napoleon import _skip_member, Config
from sphinxcontrib.napoleon import docstring
from sphinxcontrib.napoleon.docstring import NumpyDocstring
import sphinx
import yaml

from utils.log import get_rel_path, logger

config = Config(napoleon_use_param=True, napoleon_use_rtype=True)


[docs]def get_meta(obj): doc = getattr(obj, '__doc__') or '' p = GoogleDocstring(stripper(doc), config) return p.metadata
[docs]def pytest_collection_modifyitems(items): output = {} for item in items: item_class = item.location[0] item_class = item_class[:item_class.rfind('.')].replace('/', '.') item_name = item.location[2] item_param = re.findall('\.*(\[.*\])', item_name) if item_param: item_name = item_name.replace(item_param[0], '') node_name = '{}.{}'.format(item_class, item_name) output[node_name] = {} output[node_name]['docstring'] = base64.b64encode(getattr(item.function, '__doc__') or '') output[node_name]['name'] = item_name # This is necessary to convert AttrDict in metadata, or even metadict(previously) # into serializable data as builtin doesn't contain instancemethod and gives us issues. doc_meta = {k: v for k, v in item._metadata.get('from_docs', {}).items()} output[node_name]['metadata'] = {'from_docs': doc_meta} with open('doc_data.yaml', 'w') as f: def dice_representer(dumper, data): return dumper.represent_scalar("chew", "me") import lya from yaml.representer import SafeRepresenter yaml.add_representer(lya.lya.AttrDict, SafeRepresenter.represent_dict) yaml.dump(output, f)
[docs]def pytest_pycollect_makeitem(collector, name, obj): """pytest hook that adds docstring metadata (if found) to a test's meta mark""" if not isinstance(obj, FunctionType) and not hasattr(obj, 'meta'): # This relies on the meta mark having already been applied to # all test functions before this hook is called return # __doc__ can be empty or nonexistent, make sure it's an empty string in that case metadata = get_meta(obj) if not hasattr(obj.meta, 'kwargs'): obj.meta.kwargs = dict() obj.meta.kwargs.update({ 'from_docs': metadata }) if metadata: test_path = get_rel_path(collector.fspath) logger.debug('Parsed docstring metadata on {} in {}'.format(name, test_path)) logger.trace('{} doc metadata: {}'.format(name, str(metadata)))
[docs]def stripper(docstring): """Slightly smarter :func:`dedent <python:textwrap.dedent>` It strips a docstring's first line indentation and dedents the rest """ if docstring: lines = docstring.splitlines() return os.linesep.join([ lines[0].strip(), dedent("\n".join(lines[1:])) ]) else: # If docstring is a null string, GoogleDocstring will expect an iterable type return ''
[docs]class GoogleDocstring(docstring.GoogleDocstring): """Custom version of napoleon's GoogleDocstring that adds some special cases""" def __init__(self, *args, **kwargs): self.metadata = {} super(GoogleDocstring, self).__init__(*args, **kwargs) self._sections['usage'] = self._parse_usage_section self._sections['metadata'] = self._parse_metadata_section super(GoogleDocstring, self)._parse() def _parse(self): pass def _consume_usage_section(self): lines = self._dedent(self._consume_to_next_section()) return lines def _consume_metadata_section(self): lines = self._dedent(self._consume_to_next_section()) return lines def _parse_usage_section(self, section): b = ['.. rubric:: Usage:', ''] c = ['.. code-block:: python', ''] lines = self._consume_usage_section() lines = self._indent(lines, 3) return b + c + lines + [''] def _parse_metadata_section(self, section): lines = self._consume_metadata_section() if lines: self.metadata = yaml.load("\n".join(lines)) return ['']
[docs]def setup(app): """Sphinx extension setup function. See Also: http://sphinx-doc.org/extensions.html """ from sphinx.application import Sphinx if not isinstance(app, Sphinx): return # probably called by tests app.connect('autodoc-process-docstring', _process_docstring) app.connect('autodoc-skip-member', _skip_member) for name, (default, rebuild) in iteritems(Config._config_values): app.add_config_value(name, default, rebuild) return {'version': sphinx.__version__, 'parallel_read_safe': True}
def _process_docstring(app, what, name, obj, options, lines): result_lines = lines if app.config.napoleon_numpy_docstring: docstring = NumpyDocstring(result_lines, app.config, app, what, name, obj, options) result_lines = docstring.lines() if app.config.napoleon_google_docstring: docstring = GoogleDocstring(result_lines, app.config, app, what, name, obj, options) result_lines = docstring.lines() lines[:] = result_lines[:]