def __init__(self, docstring, config=None, app=None, what='', name='', obj=None, options=None): # type: (Union[unicode, List[unicode]], SphinxConfig, Sphinx, unicode, unicode, Any, Any) -> None # NOQA self._config = config self._app = app if not self._config: from sphinx.ext.napoleon import Config self._config = self._app and self._app.config or Config( ) # type: ignore if not what: if inspect.isclass(obj): what = 'class' elif inspect.ismodule(obj): what = 'module' elif isinstance(obj, collections.Callable): # type: ignore what = 'function' else: what = 'object' self._what = what self._name = name self._obj = obj self._opt = options if isinstance(docstring, string_types): docstring = docstring.splitlines() self._lines = docstring self._line_iter = modify_iter(docstring, modifier=lambda s: s.rstrip()) self._parsed_lines = [] # type: List[unicode] self._is_in_section = False self._section_indent = 0 if not hasattr(self, '_directive_sections'): self._directive_sections = [] # type: List[unicode] if not hasattr(self, '_sections'): self._sections = { 'args': self._parse_parameters_section, 'arguments': self._parse_parameters_section, 'attributes': self._parse_attributes_section, 'example': self._parse_examples_section, 'examples': self._parse_examples_section, 'keyword args': self._parse_keyword_arguments_section, 'keyword arguments': self._parse_keyword_arguments_section, 'methods': self._parse_methods_section, 'note': self._parse_note_section, 'notes': self._parse_notes_section, 'other parameters': self._parse_other_parameters_section, 'parameters': self._parse_parameters_section, 'return': self._parse_returns_section, 'returns': self._parse_returns_section, 'raises': self._parse_raises_section, 'references': self._parse_references_section, 'see also': self._parse_see_also_section, 'todo': self._parse_todo_section, 'warning': self._parse_warning_section, 'warnings': self._parse_warning_section, 'warns': self._parse_warns_section, 'yield': self._parse_yields_section, 'yields': self._parse_yields_section, } # type: Dict[unicode, Callable] self._parse()
def test_docstrings(self): config = Config(napoleon_use_param=False, napoleon_use_rtype=False) for docstring, expected in self.docstrings: actual = str(NumpyDocstring(dedent(docstring), config)) expected = dedent(expected) self.assertEqual(expected, actual)
def test_raises_types(self): docstrings = [ (""" Example Function Raises ------ RuntimeError A setting wasn't specified, or was invalid. ValueError Something something value error. """, """ Example Function :raises: * :exc:`RuntimeError` -- A setting wasn't specified, or was invalid. * :exc:`ValueError` -- Something something value error. """), ################################ (""" Example Function Raises ------ InvalidDimensionsError """, """ Example Function :raises: :exc:`InvalidDimensionsError` """), ################################ (""" Example Function Raises ------ Invalid Dimensions Error """, """ Example Function :raises: Invalid Dimensions Error """), ################################ (""" Example Function Raises ------ Invalid Dimensions Error With description """, """ Example Function :raises: *Invalid Dimensions Error* -- With description """), ################################ (""" Example Function Raises ------ InvalidDimensionsError If the dimensions couldn't be parsed. """, """ Example Function :raises: :exc:`InvalidDimensionsError` -- If the dimensions couldn't be parsed. """), ################################ (""" Example Function Raises ------ Invalid Dimensions Error If the dimensions couldn't be parsed. """, """ Example Function :raises: *Invalid Dimensions Error* -- If the dimensions couldn't be parsed. """), ################################ (""" Example Function Raises ------ If the dimensions couldn't be parsed. """, """ Example Function :raises: If the dimensions couldn't be parsed. """), ################################ (""" Example Function Raises ------ :class:`exc.InvalidDimensionsError` """, """ Example Function :raises: :class:`exc.InvalidDimensionsError` """), ################################ (""" Example Function Raises ------ :class:`exc.InvalidDimensionsError` If the dimensions couldn't be parsed. """, """ Example Function :raises: :class:`exc.InvalidDimensionsError` -- If the dimensions couldn't """ """be parsed. """), ################################ (""" Example Function Raises ------ :class:`exc.InvalidDimensionsError` If the dimensions couldn't be parsed, then a :class:`exc.InvalidDimensionsError` will be raised. """, """ Example Function :raises: :class:`exc.InvalidDimensionsError` -- If the dimensions couldn't """ """be parsed, then a :class:`exc.InvalidDimensionsError` will be raised. """), ################################ (""" Example Function Raises ------ :class:`exc.InvalidDimensionsError` If the dimensions couldn't be parsed. :class:`exc.InvalidArgumentsError` If the arguments are invalid. """, """ Example Function :raises: * :class:`exc.InvalidDimensionsError` -- If the dimensions """ """couldn't be parsed. * :class:`exc.InvalidArgumentsError` -- If the arguments """ """are invalid. """), ################################ (""" Example Function Raises ------ :class:`exc.InvalidDimensionsError` :class:`exc.InvalidArgumentsError` """, """ Example Function :raises: * :class:`exc.InvalidDimensionsError` * :class:`exc.InvalidArgumentsError` """) ] for docstring, expected in docstrings: config = Config() app = mock.Mock() actual = str(NumpyDocstring(docstring, config, app, "method")) self.assertEqual(expected, actual)
def __init__(self, docstring: Union[str, List[str]], config: SphinxConfig = None, app: Sphinx = None, what: str = '', name: str = '', obj: Any = None, options: Any = None) -> None: self._config = config self._app = app if not self._config: from sphinx.ext.napoleon import Config self._config = self._app.config if self._app else Config( ) # type: ignore if not what: if inspect.isclass(obj): what = 'class' elif inspect.ismodule(obj): what = 'module' elif callable(obj): what = 'function' else: what = 'object' self._what = what self._name = name self._obj = obj self._opt = options if isinstance(docstring, str): lines = docstring.splitlines() else: lines = docstring self._line_iter = modify_iter(lines, modifier=lambda s: s.rstrip()) self._parsed_lines = [] # type: List[str] self._is_in_section = False self._section_indent = 0 if not hasattr(self, '_directive_sections'): self._directive_sections = [] # type: List[str] if not hasattr(self, '_sections'): self._sections = { 'args': self._parse_parameters_section, 'arguments': self._parse_parameters_section, 'attention': partial(self._parse_admonition, 'attention'), 'attributes': self._parse_attributes_section, 'caution': partial(self._parse_admonition, 'caution'), 'danger': partial(self._parse_admonition, 'danger'), 'error': partial(self._parse_admonition, 'error'), 'example': self._parse_examples_section, 'examples': self._parse_examples_section, 'hint': partial(self._parse_admonition, 'hint'), 'important': partial(self._parse_admonition, 'important'), 'keyword args': self._parse_keyword_arguments_section, 'keyword arguments': self._parse_keyword_arguments_section, 'methods': self._parse_methods_section, 'note': partial(self._parse_admonition, 'note'), 'notes': self._parse_notes_section, 'other parameters': self._parse_other_parameters_section, 'parameters': self._parse_parameters_section, 'return': self._parse_returns_section, 'returns': self._parse_returns_section, 'raises': self._parse_raises_section, 'references': self._parse_references_section, 'see also': self._parse_see_also_section, 'tip': partial(self._parse_admonition, 'tip'), 'todo': partial(self._parse_admonition, 'todo'), 'warning': partial(self._parse_admonition, 'warning'), 'warnings': partial(self._parse_admonition, 'warning'), 'warns': self._parse_warns_section, 'yield': self._parse_yields_section, 'yields': self._parse_yields_section, } # type: Dict[str, Callable] self._load_custom_sections() self._parse()
def test_list_in_parameter_description(self): docstring = """One line summary. Parameters: no_list (int): one_bullet_empty (int): * one_bullet_single_line (int): - first line one_bullet_two_lines (int): + first line continued two_bullets_single_line (int): - first line - second line two_bullets_two_lines (int): * first line continued * second line continued one_enumeration_single_line (int): 1. first line one_enumeration_two_lines (int): 1) first line continued two_enumerations_one_line (int): (iii) first line (iv) second line two_enumerations_two_lines (int): a. first line continued b. second line continued one_definition_one_line (int): item 1 first line one_definition_two_lines (int): item 1 first line continued two_definitions_one_line (int): item 1 first line item 2 second line two_definitions_two_lines (int): item 1 first line continued item 2 second line continued one_definition_blank_line (int): item 1 first line extra first line two_definitions_blank_lines (int): item 1 first line extra first line item 2 second line extra second line definition_after_inline_text (int): text line item 1 first line definition_after_normal_text (int): text line item 1 first line """ expected = """One line summary. :param no_list: :type no_list: int :param one_bullet_empty: * :type one_bullet_empty: int :param one_bullet_single_line: - first line :type one_bullet_single_line: int :param one_bullet_two_lines: + first line continued :type one_bullet_two_lines: int :param two_bullets_single_line: - first line - second line :type two_bullets_single_line: int :param two_bullets_two_lines: * first line continued * second line continued :type two_bullets_two_lines: int :param one_enumeration_single_line: 1. first line :type one_enumeration_single_line: int :param one_enumeration_two_lines: 1) first line continued :type one_enumeration_two_lines: int :param two_enumerations_one_line: (iii) first line (iv) second line :type two_enumerations_one_line: int :param two_enumerations_two_lines: a. first line continued b. second line continued :type two_enumerations_two_lines: int :param one_definition_one_line: item 1 first line :type one_definition_one_line: int :param one_definition_two_lines: item 1 first line continued :type one_definition_two_lines: int :param two_definitions_one_line: item 1 first line item 2 second line :type two_definitions_one_line: int :param two_definitions_two_lines: item 1 first line continued item 2 second line continued :type two_definitions_two_lines: int :param one_definition_blank_line: item 1 first line extra first line :type one_definition_blank_line: int :param two_definitions_blank_lines: item 1 first line extra first line item 2 second line extra second line :type two_definitions_blank_lines: int :param definition_after_inline_text: text line item 1 first line :type definition_after_inline_text: int :param definition_after_normal_text: text line item 1 first line :type definition_after_normal_text: int """ config = Config(napoleon_use_param=True) actual = str(GoogleDocstring(docstring, config)) self.assertEqual(expected, actual) expected = """One line summary. :Parameters: * **no_list** (*int*) * **one_bullet_empty** (*int*) -- * * **one_bullet_single_line** (*int*) -- - first line * **one_bullet_two_lines** (*int*) -- + first line continued * **two_bullets_single_line** (*int*) -- - first line - second line * **two_bullets_two_lines** (*int*) -- * first line continued * second line continued * **one_enumeration_single_line** (*int*) -- 1. first line * **one_enumeration_two_lines** (*int*) -- 1) first line continued * **two_enumerations_one_line** (*int*) -- (iii) first line (iv) second line * **two_enumerations_two_lines** (*int*) -- a. first line continued b. second line continued * **one_definition_one_line** (*int*) -- item 1 first line * **one_definition_two_lines** (*int*) -- item 1 first line continued * **two_definitions_one_line** (*int*) -- item 1 first line item 2 second line * **two_definitions_two_lines** (*int*) -- item 1 first line continued item 2 second line continued * **one_definition_blank_line** (*int*) -- item 1 first line extra first line * **two_definitions_blank_lines** (*int*) -- item 1 first line extra first line item 2 second line extra second line * **definition_after_inline_text** (*int*) -- text line item 1 first line * **definition_after_normal_text** (*int*) -- text line item 1 first line """ config = Config(napoleon_use_param=False) actual = str(GoogleDocstring(docstring, config)) self.assertEqual(expected, actual)
def inspect_routes(self, app): """Inspects the views of Flask. :param app: The Flask application. :returns: 4-tuple like ``(method, paths, view_func, view_doc)`` """ if self.endpoints: routes = itertools.chain(*[ get_routes(app, endpoint, self.order) for endpoint in self.endpoints ]) else: routes = get_routes(app, order=self.order) for method, paths, endpoint in routes: try: blueprint, _, endpoint_internal = endpoint.rpartition('.') if self.blueprints and blueprint not in self.blueprints: continue if blueprint in self.undoc_blueprints: continue except ValueError: pass # endpoint is not within a blueprint if endpoint in self.undoc_endpoints: continue try: static_url_path = app.static_url_path # Flask 0.7 or higher except AttributeError: static_url_path = app.static_path # Flask 0.6 or under if ('undoc-static' in self.options and endpoint == 'static' and static_url_path + '/(path:filename)' in paths): continue view = app.view_functions[endpoint] if self.modules and view.__module__ not in self.modules: continue if self.undoc_modules and view.__module__ in self.modules: continue view_class = getattr(view, 'view_class', None) if view_class is None: view_func = view else: view_func = getattr(view_class, method.lower(), None) view_doc = view.__doc__ or '' if view_func and view_func.__doc__: view_doc = view_func.__doc__ if not isinstance(view_doc, six.text_type): analyzer = ModuleAnalyzer.for_module(view.__module__) view_doc = force_decode(view_doc, analyzer.encoding) if not view_doc and 'include-empty-docstring' not in self.options: continue # select section (delimited by http:<method>) corresponding to method view_doc = filter_by_method(view_doc, method) # convert numpy-style docstrings into RST via napoleon from sphinx.ext.napoleon import Config, NumpyDocstring view_doc = str( NumpyDocstring( '\n'.join( NumpyDocstring('')._dedent(view_doc.split('\n'))), Config())) yield (method, paths, view_func, view_doc)
import os import re import base64 from textwrap import dedent from types import FunctionType from six import iteritems from sphinx.ext.napoleon import _skip_member, Config from sphinx.ext.napoleon import docstring from sphinx.ext.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) def get_meta(obj): doc = getattr(obj, '__doc__') or '' p = GoogleDocstring(stripper(doc), config) return p.metadata 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)
def command_doc(module): """ Document command classes """ mod = importlib.import_module(module) print(".. automodule:: {}".format(module)) config = Config() cls_str = """.. py:function:: {}{} {} .. rubric:: Available entries: {} .. rubric:: Available events: {} """ cmd_str = """.. py:function:: {}{} {} """ cls_str = inspect.cleandoc(cls_str) cmd_str = inspect.cleandoc(cmd_str) def indent_text(txt, num=4): return "\n".join((num * " ") + i for i in txt) def doc_process(docstr, obj, retval=True, indent=4, config=config, docstring=docstring, inspect=inspect, process_docstring=process_docstring, indent_text=indent_text): docstr = docstring.GoogleDocstring(inspect.cleandoc(docstr), config, obj=obj) docslines = str(docstr).splitlines() process_docstring(None, '', '', obj, config, docslines) if docslines: r = docslines.pop(0) # put rtype last if retval: docslines.append(r) # indent if indent: docstr = indent_text(docslines, indent) else: docstr = "\n".join(docslines) return docstr try: for name, obj in inspect.getmembers(mod, inspect.isclass): if not obj.__name__.startswith( '_') and obj.__module__ == mod.__name__ and issubclass( obj, command.CoreCommand): if getattr(obj, 'main', False): objfunc = obj.main else: objfunc = obj.__init__ sig = sinspect.Signature(objfunc, bound_method=True) obj._get_commands() entries = [] events = [] e_objfunc = None for x, e in sorted(obj._entries.items()): esig = sinspect.Signature(objfunc) esig.signature = e.signature ex_local = { 'happypanda': happypanda, 'collections': collections } ex_local.update(mod.__dict__) ex_local.update(globals()) exec("def e_objfunc{}:None".format(esig.signature), ex_local, ex_local) e_objfunc = ex_local['e_objfunc'] entries.append(" - {}".format( cmd_str.format( x, esig.format_args(), str( doc_process(e.__doc__, e_objfunc, False, indent=8))))) for x, e in sorted(obj._events.items()): esig = sinspect.Signature(objfunc) esig.signature = e.signature ex_local = { 'happypanda': happypanda, 'collections': collections } ex_local.update(mod.__dict__) ex_local.update(globals()) exec("def e_objfunc{}:None".format(esig.signature), ex_local, ex_local) e_objfunc = ex_local['e_objfunc'] events.append(" - {}".format( cmd_str.format( x, esig.format_args(), str( doc_process(e.__doc__, e_objfunc, False, indent=8))))) retval = sig.signature.return_annotation sig.signature = sig.signature.replace( return_annotation=inspect.Signature.empty) docstr = doc_process(obj.__doc__, objfunc) print( cls_str.format(name, sig.format_args(), str(docstr), '\n'.join(entries), '\n'.join(events))) except Exception: w = """ .. error:: {} """.format(module) print(inspect.cleandoc(w))