def _class_doc_markdown(cls, reference, members=None, outstream=sys.stdout, indent=''): """ Parameters ---------- cls reference members outstream Returns ------- """ doc = ClassDoc(cls) print(f'{indent}### class {reference}\n', file=outstream) indent = indent + '' if doc['Summary']: print(indent + ' '.join(doc['Summary']), file=outstream) if doc['Extended Summary']: print(indent + ' '.join(doc['Extended Summary']) + '\n', file=outstream) print('', file=outstream) print(f"{indent}**{doc['Signature']}**\n", file=outstream) print(f'{indent}**Public API Methods:**\n', file=outstream) for p in doc['Methods']: if members is not None and p.name in members: ref = '.'.join((reference, p.name)) print(f'{indent}=== "{p.name}"\n', file=outstream) _function_doc_markdown(getattr(cls, p.name), ref, outstream=outstream, indent=indent + " ", method=True) for key in doc.keys(): print(key, file=outstream)
def test_class_members_doc(): doc = ClassDoc(None, class_doc_txt) non_blank_line_by_line_compare( str(doc), """ Foo Parameters ---------- f : callable ``f(t, y, *f_args)`` Aaa. jac : callable ``jac(t, y, *jac_args)`` Bbb. Examples -------- For usage examples, see `ode`. Attributes ---------- t : float Current time. y : ndarray Current variable values. Methods ------- a : b : c : .. index:: """)
def model_params(model_key): """Return a custom form with the parameters of the given model. Parameters ---------- model_key : Key of the model stored in available models. Returns ------- Custom form. """ doc = ClassDoc(_available_models[model_key]) params = doc['Parameters'] params = list(map(list, params)) # Because previous line returns tuples for param in params: # Returns a list with description, each element is a line description_list = param[2] try: break_index = description_list.index('') except ValueError: break_index = len(description_list) description = ' '.join(description_list[0:break_index]) param[2] = description session['model'] = model_key return render_template('model_parameters.html', params=params)
def numpydoc_type_desc(thing): if inspect.isfunction(thing) or inspect.ismethod(thing): docs = FunctionDoc(thing) elif inspect.isclass(thing): docs = ClassDoc(thing) else: raise RuntimeError("Don't know how to handle " + repr(thing)) npdoc_params = docs["Parameters"] types = [p.type for p in npdoc_params] descs = [" ".join(p.desc) for p in npdoc_params] return types, descs
def test_docstring(layer): name = layer.__name__ method_name = f'add_{camel_to_snake(name)}' method = getattr(Viewer, method_name) method_doc = FunctionDoc(method) layer_doc = ClassDoc(layer) # check summary section method_summary = ' '.join(method_doc['Summary']) # join multi-line summary summary_format = 'Add an? .+? layer to the layers list.' assert re.match( summary_format, method_summary ), f"improper 'Summary' section of '{method_name}'" # check parameters section method_params = method_doc['Parameters'] layer_params = layer_doc['Parameters'] try: assert len(method_params) == len(layer_params) for method_param, layer_param in zip(method_params, layer_params): m_name, m_type, m_description = method_param l_name, l_type, l_description = layer_param # descriptions are treated as lists where each line is an element m_description = ' '.join(m_description) l_description = ' '.join(l_description) assert m_name == l_name, 'different parameter names or order' assert m_type == l_type, f"type mismatch of parameter '{m_name}'" assert ( m_description == l_description ), f"description mismatch of parameter '{m_name}'" except AssertionError as e: raise AssertionError(f"docstrings don't match for class {name}") from e # check returns section method_returns, = method_doc[ 'Returns' ] # only one thing should be returned description = ' '.join(method_returns[-1]) # join multi-line description method_returns = *method_returns[:-1], description assert method_returns == ( 'layer', f':class:`napari.layers.{name}`', f'The newly-created {name.lower()} layer.', ), f"improper 'Returns' section of '{method_name}'"
def parse_class(class_object): markdown = "" # using NumpyDoc to parse the class doc = ClassDoc(class_object) markdown += heading(CLASS_TITLE_HEADING, class_object.__name__) + lb markdown += parse_summary(doc) markdown += parse_signature(class_object.__init__) markdown += parse_parameters(doc, class_object.__init__) markdown += parse_methods(class_object, doc) return markdown
def _get_help_for_estimator(prefix, estimator, defaults=None): """Yield help lines for the given estimator and prefix.""" from numpydoc.docscrape import ClassDoc defaults = defaults or {} estimator = _extract_estimator_cls(estimator) yield "<{}> options:".format(estimator.__name__) doc = ClassDoc(estimator) yield from _get_help_for_params( doc['Parameters'], prefix=prefix, defaults=defaults, ) yield '' # add a newline line between estimators
def _get_numpydoc_obj(py_obj:Any): # special case where __doc__ is None e.g. with non overwritten dunder methods # such as "__dict__" if hasattr(py_obj, '__doc__') and getattr(py_obj, '__doc__') is None: return NumpyDocString('') # "normal" cases if inspect.isclass(py_obj): doc = ClassDoc(py_obj) elif inspect.isfunction(py_obj) or inspect.ismethod(py_obj): doc = FunctionDoc(py_obj) elif hasattr(py_obj, '__doc__'): doc = NumpyDocString(py_obj.__doc__) else: # pragma: no cover raise TypeError(f'The object {py_obj} is not a class, function, method ' 'or any other Python object that has a __doc__ attribute') return doc
def test_class_members_doc(): doc = ClassDoc(None, class_doc_txt) non_blank_line_by_line_compare(str(doc), """ Foo Parameters ---------- f : callable ``f(t, y, *f_args)`` Aaa. jac : callable ``jac(t, y, *jac_args)`` Bbb. Examples -------- For usage examples, see `ode`. Attributes ---------- t : float Current time. y : ndarray Current variable values. * hello * world an_attribute : float The docstring is printed instead no_docstring : str But a description no_docstring2 : str multiline_sentence midword_period no_period Methods ------- a b c .. index:: """)
def _to_dict(cls, skip_checking=False): if not skip_checking: _check_cycling(cls) result = {"@type": "Class", "@id": cls.__name__} if cls.__base__.__name__ != "DocumentTemplate": # result["@inherits"] = cls.__base__.__name__ parents = list(map(lambda x: x.__name__, cls.__mro__)) result["@inherits"] = parents[1:parents.index("DocumentTemplate")] elif cls.__base__.__name__ == "TaggedUnion": result["@type"] = "TaggedUnion" if cls.__doc__: doc_obj = ClassDoc(cls) prop_doc = {} for thing in doc_obj["Attributes"]: if thing.desc: prop_doc[thing.name] = "\n".join(thing.desc) result["@documentation"] = { "@comment": "\n".join(doc_obj["Summary"] + doc_obj["Extended Summary"]), "@properties": prop_doc, } if hasattr(cls, "_base"): result["@base"] = cls._base if hasattr(cls, "_subdocument"): result["@subdocument"] = cls._subdocument result["@key"] = {"@type": "Random"} if hasattr(cls, "_abstract") and cls._abstract is not None: result["@abstract"] = cls._abstract if hasattr(cls, "_key") and not hasattr(cls, "_subdocument"): if hasattr(cls._key, "_keys"): result["@key"] = { "@type": cls._key.__class__.at_type, "@fields": cls._key._keys, } else: result["@key"] = {"@type": cls._key.__class__.at_type} if hasattr(cls, "_annotations"): for attr, attr_type in cls._annotations.items(): result[attr] = wt.to_woql_type(attr_type) return result
def docstring_class(pyobj): """Get the docstring dictionary of a class Parameters ---------- pyobj : function name or class name Any object in Python for which you want the docstring Returns ------- ClassDoc If pyobj is a class A dictionary of the formatted numpy docstring can be accessed by :code:`return_val._parsed_data` Keys: 'Signature': '', 'Summary': [''], 'Extended Summary': [], 'Parameters': [], 'Returns': [], 'Raises': [], 'Warns': [], 'Other Parameters': [], 'Attributes': [], 'Methods': [], 'See Also': [], 'Notes': [], 'Warnings': [], 'References': '', 'Examples': '', 'index': {} Taken from: https://github.com/numpy/numpydoc/blob/master/numpydoc/docscrape.py#L94 """ if inspect.isclass(pyobj): return ClassDoc(pyobj) else: raise ValueError("The pyobj input parameter is not a class." "Your parameter returned {0} from " "type(pyobj)".format(type(pyobj)))
def test_docstring(layer): name = layer.__name__ method_name = f'add_{camel_to_snake(name)}' method = getattr(Viewer, method_name) method_doc = FunctionDoc(method) layer_doc = ClassDoc(layer) # check summary section method_summary = ' '.join(method_doc['Summary']) # join multi-line summary summary_format = 'Add an? .+? layer to the layer list.' assert re.match( summary_format, method_summary ), f"improper 'Summary' section of '{method_name}'" # check parameters section method_params = method_doc['Parameters'] layer_params = layer_doc['Parameters'] # Remove path parameter from viewer method if it exists method_params = [m for m in method_params if m.name != 'path'] if name == 'Image': # For Image just test arguments that are in layer are in method named_method_params = [m.name for m in method_params] for layer_param in layer_params: l_name, l_type, l_description = layer_param assert l_name in named_method_params else: try: assert len(method_params) == len(layer_params) for method_param, layer_param in zip(method_params, layer_params): m_name, m_type, m_description = method_param l_name, l_type, l_description = layer_param # descriptions are treated as lists where each line is an # element m_description = ' '.join(m_description) l_description = ' '.join(l_description) assert m_name == l_name, 'different parameter names or order' assert ( m_type == l_type ), f"type mismatch of parameter '{m_name}'" assert ( m_description == l_description ), f"description mismatch of parameter '{m_name}'" except AssertionError as e: raise AssertionError( f"docstrings don't match for class {name}" ) from e # check returns section (method_returns,) = method_doc[ 'Returns' ] # only one thing should be returned description = ' '.join(method_returns[-1]) # join multi-line description method_returns = *method_returns[:-1], description if name == 'Image': assert method_returns == ( 'layer', f':class:`napari.layers.{name}` or list', f'The newly-created {name.lower()} layer or list of {name.lower()} layers.', # noqa: E501 ), f"improper 'Returns' section of '{method_name}'" else: assert method_returns == ( 'layer', f':class:`napari.layers.{name}`', f'The newly-created {name.lower()} layer.', ), f"improper 'Returns' section of '{method_name}'"
def numpy_cls_parser(cls): data = ClassDoc(cls) # print(data) return numpy_doc_parser(data)
def print_docstring(obj, file, depth): """Prints a classes's docstring to a file.""" doc = ClassDoc(obj) if inspect.isclass(obj) else FunctionDoc(obj) printf = functools.partial(print, file=file) printf(h1(obj.__name__)) printf( linkifier.linkify_fences(paragraph(concat_lines(doc["Summary"])), depth)) printf( linkifier.linkify_fences( paragraph(concat_lines(doc["Extended Summary"])), depth)) # We infer the type annotations from the signatures, and therefore rely on the signature # instead of the docstring for documenting parameters try: signature = inspect.signature(obj) except ValueError: signature = ( inspect.Signature() ) # TODO: this is necessary for Cython classes, but it's not correct params_desc = { param.name: " ".join(param.desc) for param in doc["Parameters"] } # Parameters if signature.parameters: printf(h2("Parameters")) for param in signature.parameters.values(): # Name printf(f"- **{param.name}**", end="") # Type annotation if param.annotation is not param.empty: anno = inspect.formatannotation(param.annotation) anno = linkifier.linkify_dotted(anno, depth) printf(f" (*{anno}*)", end="") # Default value if param.default is not param.empty: printf(f" – defaults to `{param.default}`", end="") printf("\n", file=file) # Description desc = params_desc[param.name] if desc: printf(f" {desc}\n") printf("") # Attributes if doc["Attributes"]: printf(h2("Attributes")) for attr in doc["Attributes"]: # Name printf(f"- **{attr.name}**", end="") # Type annotation if attr.type: printf(f" (*{attr.type}*)", end="") printf("\n", file=file) # Description desc = " ".join(attr.desc) if desc: printf(f" {desc}\n") printf("") # Examples if doc["Examples"]: printf(h2("Examples")) in_code = False after_space = False for line in inspect.cleandoc("\n".join(doc["Examples"])).splitlines(): if (in_code and after_space and line and not line.startswith(">>>") and not line.startswith("...")): printf("```\n") in_code = False after_space = False if not in_code and line.startswith(">>>"): printf("```python") in_code = True after_space = False if not line: after_space = True printf(line) if in_code: printf("```") printf("") # Methods if inspect.isclass(obj) and doc["Methods"]: printf(h2("Methods")) printf_indent = lambda x, **kwargs: printf(f" {x}", **kwargs) for meth in doc["Methods"]: printf(paragraph(f'???- note "{meth.name}"')) # Parse method docstring docstring = inherit_docstring(c=obj, meth=meth.name) if not docstring: continue meth_doc = FunctionDoc(func=None, doc=docstring) printf_indent(paragraph(" ".join(meth_doc["Summary"]))) if meth_doc["Extended Summary"]: printf_indent(paragraph(" ".join( meth_doc["Extended Summary"]))) # We infer the type annotations from the signatures, and therefore rely on the signature # instead of the docstring for documenting parameters signature = inherit_signature(obj, meth.name) params_desc = { param.name: " ".join(param.desc) for param in doc["Parameters"] } # Parameters if len(signature.parameters ) > 1: # signature is never empty, but self doesn't count printf_indent("**Parameters**\n") for param in signature.parameters.values(): if param.name == "self": continue # Name printf_indent(f"- **{param.name}**", end="") # Type annotation if param.annotation is not param.empty: printf_indent( f" (*{inspect.formatannotation(param.annotation)}*)", end="") # Default value if param.default is not param.empty: printf_indent(f" – defaults to `{param.default}`", end="") printf_indent("", file=file) # Description desc = params_desc.get(param.name) if desc: printf_indent(f" {desc}") printf_indent("") # Returns if meth_doc["Returns"]: printf_indent("**Returns**\n") return_val = meth_doc["Returns"][0] if signature.return_annotation is not inspect._empty: if inspect.isclass(signature.return_annotation): printf_indent( f"*{signature.return_annotation.__name__}*: ", end="") else: printf_indent(f"*{signature.return_annotation}*: ", end="") printf_indent(return_val.type) printf_indent("") # Notes if doc["Notes"]: printf(h2("Notes")) printf(paragraph("\n".join(doc["Notes"]))) # References if doc["References"]: printf(h2("References")) printf(paragraph("\n".join(doc["References"])))
output_file = Path(__file__).parent / 'models.md' # Define templates table_doc_template = """### `{table}` {desc} {cols} """ bullet_point_template = "- `{type}` `{name}` - {desc}\n" # Parse the models output = '## Tables\n\n' for model in all_models: doc = ClassDoc(model) cols = '' for parameter in doc['Attributes']: cols += bullet_point_template.format(type=parameter.type, name=parameter.name, desc=' '.join(parameter.desc)) output += table_doc_template.format( table=model.__tablename__, desc=' '.join(doc['Summary']), cols=cols, ) output_file.write_text(output)
def class_doc_attrs(kls: Type) -> Dict[str, Parameter]: docs = {p.name: " ".join(p.desc) for p in ClassDoc(kls).get('Attributes')} docs.update( {p.name: " ".join(p.desc) for p in ClassDoc(kls).get('Parameters')}) return docs