././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1643049415.0578768 numpydoc-1.2/0000775002342000234200000000000000000000000012535 5ustar00jarrodjarrod././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1641610654.0 numpydoc-1.2/LICENSE.txt0000664002342000234200000000241500000000000014362 0ustar00jarrodjarrodCopyright (C) 2008 Stefan van der Walt , Pauli Virtanen Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1641610654.0 numpydoc-1.2/MANIFEST.in0000664002342000234200000000051100000000000014270 0ustar00jarrodjarrodinclude MANIFEST.in recursive-include numpydoc * include *.txt include *.rst recursive-include doc * # Exclude what we don't want to include prune */__pycache__ exclude .circleci/config.yml exclude .coveragerc exclude codecov.yml exclude doc/scipy-sphinx-theme/.git exclude .gitmodules global-exclude *.pyc *~ *.bak *.swp *.pyo ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1643049415.0578768 numpydoc-1.2/PKG-INFO0000664002342000234200000000374300000000000013641 0ustar00jarrodjarrodMetadata-Version: 2.1 Name: numpydoc Version: 1.2 Summary: Sphinx extension to support docstrings in Numpy format Home-page: https://numpydoc.readthedocs.io Author: Pauli Virtanen and others Author-email: pav@iki.fi License: BSD Keywords: sphinx numpy Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Plugins Classifier: License :: OSI Approved :: BSD License Classifier: Topic :: Documentation Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Requires-Python: >=3.7 Provides-Extra: testing License-File: LICENSE.txt ===================================== numpydoc -- Numpy's Sphinx extensions ===================================== .. image:: https://readthedocs.org/projects/numpydoc/badge/?version=latest :alt: Documentation Status :scale: 100% :target: https://numpydoc.readthedocs.io/en/latest/ .. image:: https://codecov.io/gh/numpy/numpydoc/branch/main/graph/badge.svg :target: https://app.codecov.io/gh/numpy/numpydoc/branch/main .. image:: https://github.com/numpy/numpydoc/actions/workflows/test.yml/badge.svg?branch=main :target: https://github.com/numpy/numpydoc/actions/workflows/test.yml This package provides the ``numpydoc`` Sphinx extension for handling docstrings formatted according to the NumPy documentation format. The extension also adds the code description directives ``np:function``, ``np-c:function``, etc. numpydoc requires Python 3.7+ and sphinx 1.8+. For usage information, please refer to the `documentation `_. The `numpydoc docstring guide `_ explains how to write docs formatted for this extension, and the `user guide `_ explains how to use it with Sphinx. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1641793423.0 numpydoc-1.2/README.rst0000664002342000234200000000230100000000000014220 0ustar00jarrodjarrod===================================== numpydoc -- Numpy's Sphinx extensions ===================================== .. image:: https://readthedocs.org/projects/numpydoc/badge/?version=latest :alt: Documentation Status :scale: 100% :target: https://numpydoc.readthedocs.io/en/latest/ .. image:: https://codecov.io/gh/numpy/numpydoc/branch/main/graph/badge.svg :target: https://app.codecov.io/gh/numpy/numpydoc/branch/main .. image:: https://github.com/numpy/numpydoc/actions/workflows/test.yml/badge.svg?branch=main :target: https://github.com/numpy/numpydoc/actions/workflows/test.yml This package provides the ``numpydoc`` Sphinx extension for handling docstrings formatted according to the NumPy documentation format. The extension also adds the code description directives ``np:function``, ``np-c:function``, etc. numpydoc requires Python 3.7+ and sphinx 1.8+. For usage information, please refer to the `documentation `_. The `numpydoc docstring guide `_ explains how to write docs formatted for this extension, and the `user guide `_ explains how to use it with Sphinx. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643049380.0 numpydoc-1.2/RELEASE.rst0000664002342000234200000000276600000000000014362 0ustar00jarrodjarrodRelease process for ``numpydoc`` ================================ Introduction ------------ Example ``__version__`` - 1.8.dev0 # development version of 1.8 (release candidate 1) - 1.8rc1 # 1.8 release candidate 1 - 1.8rc2.dev0 # development version of 1.8 (release candidate 2) - 1.8 # 1.8 release - 1.9.dev0 # development version of 1.9 (release candidate 1) Test release candidates on numpy, scipy, matplotlib, scikit-image, and networkx. Process ------- - Review and update ``doc/release_notes.rst``. - Update ``__version__`` in ``numpydoc/__init__.py``. - Commit changes:: git add numpydoc/__init__.py doc/release_notes.rst git commit -m 'Designate release' - Add the version number as a tag in git:: git tag -s [-u ] numpydoc- -m 'signed tag' If you do not have a gpg key, use -u instead; it is important for Debian packaging that the tags are annotated - Push the new meta-data to github:: git push --tags origin main where ``origin`` is the name of the ``github.com:numpy/numpydoc`` repository - Review the github release page:: https://github.com/numpy/numpydoc/releases - Publish on PyPi:: git clean -fxd pip install build wheel twine python -m build --sdist --wheel python setup.py sdist bdist_wheel twine upload -s dist/* - Update ``__version__`` in ``numpydoc/__init__.py``. - Commit changes:: git add numpydoc/__init__.py git commit -m 'Bump version' git push origin main ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1643049415.0538766 numpydoc-1.2/doc/0000775002342000234200000000000000000000000013302 5ustar00jarrodjarrod././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1641610654.0 numpydoc-1.2/doc/Makefile0000664002342000234200000001605500000000000014751 0ustar00jarrodjarrod# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = -nWT --keep-going SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " show to show the generated HTML" clean: -rm -rf $(BUILDDIR)/* -rm -rf auto_examples/ -rm -rf generated/* -rm -rf modules/generated/* html: # These two lines make the build a bit more lengthy, and the # the embedding of images more robust rm -rf $(BUILDDIR)/html/_images #rm -rf _build/doctrees/ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/project-template.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/project-template.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/project-template" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/project-template" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." show: @python -c "import webbrowser; webbrowser.open_new_tab('file://$(PWD)/$(BUILDDIR)/html/index.html')" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1642724375.0 numpydoc-1.2/doc/conf.py0000664002342000234200000002001000000000000014572 0ustar00jarrodjarrod# -*- coding: utf-8 -*- # # project-template documentation build configuration file, created by # sphinx-quickstart on Mon Jan 18 14:44:12 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os import re # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # for example.py sys.path.insert(0, os.path.abspath('.')) # project root sys.path.insert(0, os.path.abspath('..')) os.environ['MPLBACKEND'] = 'Agg' # avoid tkinter import errors on rtfd.io # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'numpydoc', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', ] try: import sphinx.ext.imgmath # noqa except ImportError: extensions.append('sphinx.ext.pngmath') else: extensions.append('sphinx.ext.imgmath') # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The root toctree document master_doc = 'index' # NOTE: will be changed to `root_doc` in sphinx 4 # General information about the project. project = u'numpydoc' copyright = u'2019, numpydoc maintainers' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. import numpydoc # version = .__version__ # The full version, including alpha/beta/rc tags. release = numpydoc.__version__ version = re.sub(r'(\d+\.\d+)\.\d+(.*)', r'\1\2', numpydoc.__version__) version = re.sub(r'(\.dev\d+).*?$', r'\1', version) numpydoc_xref_param_type = True numpydoc_xref_ignore = {'optional', 'type_without_description', 'BadException'} # Run docstring validation as part of build process numpydoc_validation_checks = {"all", "GL01", "SA04", "RT03"} # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = "pydata_sphinx_theme" html_theme_options = { "github_url": "https://github.com/numpy/numpydoc", "show_prev_next": False, "navbar_end": ["search-field.html", "navbar-icon-links.html"], } html_sidebars = { "**": [], } html_title = f"{project} v{version} Manual" html_last_updated_fmt = '%b %d, %Y' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] # ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'project-templatedoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'numpydoc.tex', u'numpydoc Documentation', u'Numpydoc maintainers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { 'python': ('https://docs.python.org/3/', None), 'numpy': ('https://numpy.org/devdocs/', None), 'sklearn': ('https://scikit-learn.org/stable/', None), } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1642724375.0 numpydoc-1.2/doc/example.py0000664002342000234200000001011600000000000015306 0ustar00jarrodjarrod"""Docstring for the example.py module. Modules names should have short, all-lowercase names. The module name may have underscores if this improves readability. Every module should have a docstring at the very top of the file. The module's docstring may extend over multiple lines. If your docstring does extend over multiple lines, the closing three quotation marks must be on a line by itself, preferably preceded by a blank line. """ from __future__ import division, absolute_import, print_function import os # standard library imports first # Do NOT import using *, e.g. from numpy import * # # Import the module using # # import numpy # # instead or import individual functions as needed, e.g # # from numpy import array, zeros # # If you prefer the use of abbreviated module names, we suggest the # convention used by NumPy itself:: import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt # These abbreviated names are not to be used in docstrings; users must # be able to paste and execute docstrings after importing only the # numpy module itself, unabbreviated. def foo(var1, var2, *args, long_var_name='hi', only_seldom_used_keyword=0, **kwargs): r"""Summarize the function in one line. Several sentences providing an extended description. Refer to variables using back-ticks, e.g. `var`. Parameters ---------- var1 : array_like Array_like means all those objects -- lists, nested lists, etc. -- that can be converted to an array. We can also refer to variables like `var1`. var2 : int The type above can either refer to an actual Python type (e.g. ``int``), or describe the type of the variable in more detail, e.g. ``(N,) ndarray`` or ``array_like``. *args : iterable Other arguments. long_var_name : {'hi', 'ho'}, optional Choices in brackets, default first when optional. Returns ------- type Explanation of anonymous return value of type ``type``. describe : type Explanation of return value named `describe`. out : type Explanation of `out`. type_without_description Other Parameters ---------------- only_seldom_used_keyword : int, optional Infrequently used parameters can be described under this optional section to prevent cluttering the Parameters section. **kwargs : dict Other infrequently used keyword arguments. Note that all keyword arguments appearing after the first parameter specified under the Other Parameters section, should also be described under this section. Raises ------ BadException Because you shouldn't have done that. See Also -------- numpy.array : Relationship (optional). numpy.ndarray : Relationship (optional), which could be fairly long, in which case the line wraps here. numpy.dot, numpy.linalg.norm, numpy.eye Notes ----- Notes about the implementation algorithm (if needed). This can have multiple paragraphs. You may include some math: .. math:: X(e^{j\omega } ) = x(n)e^{ - j\omega n} And even use a Greek symbol like :math:`\omega` inline. References ---------- Cite the relevant literature, e.g. [1]_. You may also cite these references in the notes section above. .. [1] O. McNoleg, "The integration of GIS, remote sensing, expert systems and adaptive co-kriging for environmental habitat modelling of the Highland Haggis using object-oriented, fuzzy-logic and neural-network techniques," Computers & Geosciences, vol. 22, pp. 585-588, 1996. Examples -------- These are written in doctest format, and should illustrate how to use the function. >>> a = [1, 2, 3] >>> print([x + 3 for x in a]) [4, 5, 6] >>> print("a\nb") a b """ # After closing class docstring, there should be one blank line to # separate following codes (according to PEP257). # But for function, method and module, there should be no blank lines # after closing the docstring. pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1641610654.0 numpydoc-1.2/doc/example.rst0000664002342000234200000000037300000000000015472 0ustar00jarrodjarrod.. _example: ======= Example ======= Source ====== .. literalinclude:: example.py :caption: `example.py `_ :language: python Rendered ======== .. automodule:: example :members: ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1641610654.0 numpydoc-1.2/doc/format.rst0000664002342000234200000005665500000000000015345 0ustar00jarrodjarrod.. _format: =========== Style guide =========== This document describes the syntax and best practices for docstrings used with the numpydoc extension for Sphinx_. .. Note:: For an accompanying example, see :ref:`example.py `. Some features described in this document require a recent version of ``numpydoc``. For example, the :ref:`Yields ` section was added in ``numpydoc`` 0.6. Overview -------- We mostly follow the standard Python style conventions as described here: * `Style Guide for C Code `_ * `Style Guide for Python Code `_ * `Docstring Conventions `_ Additional PEPs of interest regarding documentation of code: * `Docstring Processing Framework `_ * `Docutils Design Specification `_ Use a code checker: * pylint_: a Python static code analysis tool. * pyflakes_: a tool to check Python code for errors by parsing the source file instead of importing it. * pycodestyle_: (formerly ``pep8``) a tool to check Python code against some of the style conventions in PEP 8. * flake8_: a tool that glues together ``pycodestyle``, ``pyflakes``, ``mccabe`` to check the style and quality of Python code. * vim-flake8_: a ``flake8`` plugin for Vim. Import conventions ------------------ The following import conventions are used throughout the NumPy source and documentation:: import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt Do not abbreviate ``scipy``. There is no motivating use case to abbreviate it in the real world, so we avoid it in the documentation to avoid confusion. Docstring Standard ------------------ A documentation string (docstring) is a string that describes a module, function, class, or method definition. The docstring is a special attribute of the object (``object.__doc__``) and, for consistency, is surrounded by triple double quotes, i.e.:: """This is the form of a docstring. It can be spread over several lines. """ NumPy, SciPy_, and the scikits follow a common convention for docstrings that provides for consistency, while also allowing our toolchain to produce well-formatted reference guides. This document describes the current community consensus for such a standard. If you have suggestions for improvements, post them on the `numpy-discussion list`_. Our docstring standard uses `re-structured text (reST) `_ syntax and is rendered using Sphinx_ (a pre-processor that understands the particular documentation style we are using). While a rich set of markup is available, we limit ourselves to a very basic subset, in order to provide docstrings that are easy to read on text-only terminals. A guiding principle is that human readers of the text are given precedence over contorting docstrings so our tools produce nice output. Rather than sacrificing the readability of the docstrings, we have written pre-processors to assist Sphinx_ in its task. The length of docstring lines should be kept to 75 characters to facilitate reading the docstrings in text terminals. Sections -------- The docstring consists of a number of sections separated by headings (except for the deprecation warning). Each heading should be underlined in hyphens, and the section ordering should be consistent with the description below. The sections of a function's docstring are: 1. Short summary ```````````````` A one-line summary that does not use variable names or the function name, e.g. :: def add(a, b): """The sum of two numbers. """ The function signature is normally found by introspection and displayed by the help function. For some functions (notably those written in C) the signature is not available, so we have to specify it as the first line of the docstring:: """ add(a, b) The sum of two numbers. """ .. highlight:: rst 2. Deprecation warning `````````````````````` A section (use if applicable) to warn users that the object is deprecated. Section contents should include: * In what NumPy version the object was deprecated, and when it will be removed. * Reason for deprecation if this is useful information (e.g., object is superseded, duplicates functionality found elsewhere, etc.). * New recommended way of obtaining the same functionality. This section should use the ``deprecated`` Sphinx directive instead of an underlined section header. :: .. deprecated:: 1.6.0 `ndobj_old` will be removed in NumPy 2.0.0, it is replaced by `ndobj_new` because the latter works also with array subclasses. .. _extended_summary: 3. Extended Summary ``````````````````` A few sentences giving an extended description. This section should be used to clarify *functionality*, not to discuss implementation detail or background theory, which should rather be explored in the :ref:`Notes ` section below. You may refer to the parameters and the function name, but parameter descriptions still belong in the :ref:`Parameters ` section. .. _params: 4. Parameters ````````````` Description of the function arguments, keywords and their respective types. :: Parameters ---------- x : type Description of parameter `x`. y Description of parameter `y` (with type not specified). Enclose variables in single backticks. The colon must be preceded by a space, or omitted if the type is absent. For the parameter types, be as precise as possible. Below are a few examples of parameters and their types. :: Parameters ---------- filename : str copy : bool dtype : data-type iterable : iterable object shape : int or tuple of int files : list of str If it is not necessary to specify a keyword argument, use ``optional``:: x : int, optional Optional keyword parameters have default values, which are displayed as part of the function signature. They can also be detailed in the description:: Description of parameter `x` (the default is -1, which implies summation over all axes). or as part of the type, instead of ``optional``. If the default value would not be used as a value, ``optional`` is preferred. These are all equivalent:: copy : bool, default True copy : bool, default=True copy : bool, default: True When a parameter can only assume one of a fixed set of values, those values can be listed in braces, with the default appearing first:: order : {'C', 'F', 'A'} Description of `order`. When two or more input parameters have exactly the same type, shape and description, they can be combined:: x1, x2 : array_like Input arrays, description of `x1`, `x2`. When documenting variable length positional, or keyword arguments, leave the leading star(s) in front of the name:: *args : tuple Additional arguments should be passed as keyword arguments **kwargs : dict, optional Extra arguments to `metric`: refer to each metric documentation for a list of all possible arguments. .. above example is from scipy.spatial.distance.pdist .. _returns: 5. Returns `````````` Explanation of the returned values and their types. Similar to the :ref:`Parameters ` section, except the name of each return value is optional. The type of each return value is always required:: Returns ------- int Description of anonymous integer return value. If both the name and type are specified, the :ref:`Returns ` section takes the same form as the :ref:`Parameters ` section:: Returns ------- err_code : int Non-zero value indicates error code, or zero on success. err_msg : str or None Human readable error message, or None on success. .. _yields: 6. Yields ````````` Explanation of the yielded values and their types. This is relevant to generators only. Similar to the :ref:`Returns ` section in that the name of each value is optional, but the type of each value is always required:: Yields ------ int Description of the anonymous integer return value. If both the name and type are specified, the :ref:`Yields ` section takes the same form as the :ref:`Returns ` section:: Yields ------ err_code : int Non-zero value indicates error code, or zero on success. err_msg : str or None Human readable error message, or None on success. Support for the :ref:`Yields ` section was added in `numpydoc `_ version 0.6. 7. Receives ``````````` Explanation of parameters passed to a generator's ``.send()`` method, formatted as for Parameters, above. Since, like for Yields and Returns, a single object is always passed to the method, this may describe either the single parameter, or positional arguments passed as a tuple. If a docstring includes Receives it must also include Yields. 8. Other Parameters ``````````````````` An optional section used to describe infrequently used parameters. It should only be used if a function has a large number of keyword parameters, to prevent cluttering the :ref:`Parameters ` section. 9. Raises ````````` An optional section detailing which errors get raised and under what conditions:: Raises ------ LinAlgException If the matrix is not numerically invertible. This section should be used judiciously, i.e., only for errors that are non-obvious or have a large chance of getting raised. 10. Warns ````````` An optional section detailing which warnings get raised and under what conditions, formatted similarly to Raises. 11. Warnings ```````````` An optional section with cautions to the user in free text/reST. .. _seealso: 12. See Also ```````````` An optional section used to refer to related code. This section can be very useful, but should be used judiciously. The goal is to direct users to other functions they may not be aware of, or have easy means of discovering (by looking at the module docstring, for example). Routines whose docstrings further explain parameters used by this function are good candidates. As an example, for ``numpy.mean`` we would have:: See Also -------- average : Weighted average. When referring to functions in the same sub-module, no prefix is needed, and the tree is searched upwards for a match. Prefix functions from other sub-modules appropriately. E.g., whilst documenting the ``random`` module, refer to a function in ``fft`` by :: fft.fft2 : 2-D fast discrete Fourier transform. When referring to an entirely different module:: scipy.random.norm : Random variates, PDFs, etc. Functions may be listed without descriptions, and this is preferable if the functionality is clear from the function name:: See Also -------- func_a : Function a with its description. func_b, func_c_, func_d func_e If the combination of the function name and the description creates a line that is too long, the entry may be written as two lines, with the function name and colon on the first line, and the description on the next line, indented four spaces:: See Also -------- package.module.submodule.func_a : A somewhat long description of the function. .. _notes: 13. Notes ````````` An optional section that provides additional information about the code, possibly including a discussion of the algorithm. This section may include mathematical equations, written in `LaTeX `_ format:: Notes ----- The FFT is a fast implementation of the discrete Fourier transform: .. math:: X(e^{j\omega } ) = x(n)e^{ - j\omega n} Equations can also be typeset underneath the math directive:: The discrete-time Fourier time-convolution property states that .. math:: x(n) * y(n) \Leftrightarrow X(e^{j\omega } )Y(e^{j\omega } )\\ another equation here Math can furthermore be used inline, i.e. :: The value of :math:`\omega` is larger than 5. Variable names are displayed in typewriter font, obtained by using ``\mathtt{var}``:: We square the input parameter `alpha` to obtain :math:`\mathtt{alpha}^2`. Note that LaTeX is not particularly easy to read, so use equations sparingly. Images are allowed, but should not be central to the explanation; users viewing the docstring as text must be able to comprehend its meaning without resorting to an image viewer. These additional illustrations are included using:: .. image:: filename where filename is a path relative to the reference guide source directory. 14. References `````````````` References cited in the :ref:`Notes ` section may be listed here, e.g. if you cited the article below using the text ``[1]_``, include it as in the list as follows:: .. [1] O. McNoleg, "The integration of GIS, remote sensing, expert systems and adaptive co-kriging for environmental habitat modelling of the Highland Haggis using object-oriented, fuzzy-logic and neural-network techniques," Computers & Geosciences, vol. 22, pp. 585-588, 1996. which renders as [1]_: .. [1] O. McNoleg, "The integration of GIS, remote sensing, expert systems and adaptive co-kriging for environmental habitat modelling of the Highland Haggis using object-oriented, fuzzy-logic and neural-network techniques," Computers & Geosciences, vol. 22, pp. 585-588, 1996. Referencing sources of a temporary nature, like web pages, is discouraged. References are meant to augment the docstring, but should not be required to understand it. References are numbered, starting from one, in the order in which they are cited. .. warning:: **References will break tables** Where references like [1] appear in a tables within a numpydoc docstring, the table markup will be broken by numpydoc processing. See `numpydoc issue #130 `_ .. highlight:: pycon 15. Examples ```````````` An optional section for examples, using the `doctest `_ format. This section is meant to illustrate usage, not to provide a testing framework -- for that, use the ``tests/`` directory. While optional, this section is very strongly encouraged. When multiple examples are provided, they should be separated by blank lines. Comments explaining the examples should have blank lines both above and below them:: Examples -------- >>> np.add(1, 2) 3 Comment explaining the second example. >>> np.add([1, 2], [3, 4]) array([4, 6]) The example code may be split across multiple lines, with each line after the first starting with '... ':: >>> np.add([[1, 2], [3, 4]], ... [[5, 6], [7, 8]]) array([[ 6, 8], [10, 12]]) For tests with a result that is random or platform-dependent, mark the output as such:: >>> import numpy.random >>> np.random.rand(2) array([ 0.35773152, 0.38568979]) #random You can run examples as doctests using:: >>> np.test(doctests=True) >>> np.linalg.test(doctests=True) # for a single module In IPython it is also possible to run individual examples simply by copy-pasting them in doctest mode:: In [1]: %doctest_mode Exception reporting mode: Plain Doctest mode is: ON >>> %paste import numpy.random np.random.rand(2) ## -- End pasted text -- array([ 0.8519522 , 0.15492887]) It is not necessary to use the doctest markup ```` to indicate empty lines in the output. Note that the option to run the examples through ``numpy.test`` is provided for checking if the examples work, not for making the examples part of the testing framework. The examples may assume that ``import numpy as np`` is executed before the example code in *numpy*. Additional examples may make use of *matplotlib* for plotting, but should import it explicitly, e.g., ``import matplotlib.pyplot as plt``. All other imports, including the demonstrated function, must be explicit. When matplotlib is imported in the example, the Example code will be wrapped in `matplotlib's Sphinx \`plot\` directive `_. When matplotlib is not explicitly imported, `.. plot::` can be used directly if `matplotlib.sphinxext.plot_directive` is loaded as a Sphinx extension in ``conf.py``. .. highlight:: rst Documenting classes ------------------- Class docstring ``````````````` Use the same sections as outlined above (all except ``Returns`` are applicable). The constructor (``__init__``) should also be documented here, the :ref:`Parameters ` section of the docstring details the constructor's parameters. An **Attributes** section, located below the :ref:`Parameters ` section, may be used to describe non-method attributes of the class:: Attributes ---------- x : float The X coordinate. y : float The Y coordinate. Attributes that are properties and have their own docstrings can be simply listed by name:: Attributes ---------- real imag x : float The X coordinate. y : float The Y coordinate. In general, it is not necessary to list class methods. Those that are not part of the public API have names that start with an underscore. In some cases, however, a class may have a great many methods, of which only a few are relevant (e.g., subclasses of ndarray). Then, it becomes useful to have an additional **Methods** section: .. code-block:: python class Photo(ndarray): """ Array with associated photographic information. ... Attributes ---------- exposure : float Exposure in seconds. Methods ------- colorspace(c='rgb') Represent the photo in the given colorspace. gamma(n=1.0) Change the photo's gamma exposure. """ If it is necessary to explain a private method (use with care!), it can be referred to in the :ref:`Extended Summary ` or the :ref:`Notes ` section. Do not list private methods in the **Methods** section. Note that `self` is *not* listed as the first parameter of methods. Method docstrings ````````````````` Document these as you would any other function. Do not include ``self`` in the list of parameters. If a method has an equivalent function (which is the case for many ndarray methods for example), the function docstring should contain the detailed documentation, and the method docstring should refer to it. Only put brief summary and :ref:`See Also ` sections in the method docstring. The method should use a :ref:`Returns ` or :ref:`Yields ` section, as appropriate. Documenting class instances --------------------------- Instances of classes that are part of the NumPy API (for example `np.r_` `np.c_`, `np.index_exp`, etc.) may require some care. To give these instances a useful docstring, we do the following: * Single instance: If only a single instance of a class is exposed, document the class. Examples can use the instance name. * Multiple instances: If multiple instances are exposed, docstrings for each instance are written and assigned to the instances' ``__doc__`` attributes at run time. The class is documented as usual, and the exposed instances can be mentioned in the :ref:`Notes ` and :ref:`See Also ` sections. Documenting generators ---------------------- Generators should be documented just as functions are documented. The only difference is that one should use the :ref:`Yields ` section instead of the :ref:`Returns ` section. Support for the :ref:`Yields ` section was added in `numpydoc `_ version 0.6. Documenting constants --------------------- Use the same sections as outlined for functions where applicable:: 1. summary 2. extended summary (optional) 3. see also (optional) 4. references (optional) 5. examples (optional) Docstrings for constants will not be visible in text terminals (constants are of immutable type, so docstrings can not be assigned to them like for for class instances), but will appear in the documentation built with Sphinx. Documenting modules ------------------- Each module should have a docstring with at least a summary line. Other sections are optional, and should be used in the same order as for documenting functions when they are appropriate:: 1. summary 2. extended summary 3. routine listings 4. see also 5. notes 6. references 7. examples Routine listings are encouraged, especially for large modules, for which it is hard to get a good overview of all functionality provided by looking at the source file(s) or the ``__all__`` dict. Note that license and author info, while often included in source files, do not belong in docstrings. Other points to keep in mind ---------------------------- * Equations : as discussed in the :ref:`Notes ` section above, LaTeX formatting should be kept to a minimum. Often it's possible to show equations as Python code or pseudo-code instead, which is much more readable in a terminal. For inline display use double backticks (like ``y = np.sin(x)``). For display with blank lines above and below, use a double colon and indent the code, like:: end of previous sentence:: y = np.sin(x) * Notes and Warnings : If there are points in the docstring that deserve special emphasis, the reST directives for a note or warning can be used in the vicinity of the context of the warning (inside a section). Syntax:: .. warning:: Warning text. .. note:: Note text. Use these sparingly, as they do not look very good in text terminals and are not often necessary. One situation in which a warning can be useful is for marking a known bug that is not yet fixed. * array_like : For functions that take arguments which can have not only a type `ndarray`, but also types that can be converted to an ndarray (i.e. scalar types, sequence types), those arguments can be documented with type `array_like`. * Links : If you need to include hyperlinks in your docstring, note that some docstring sections are not parsed as standard reST, and in these sections, numpydoc may become confused by hyperlink targets such as:: .. _Example: http://www.example.com If the Sphinx build issues a warning of the form ``WARNING: Unknown target name: "example"``, then that is what is happening. To avoid this problem, use the inline hyperlink form:: `Example `_ Common reST concepts -------------------- For paragraphs, indentation is significant and indicates indentation in the output. New paragraphs are marked with a blank line. Use ``*italics*``, ``**bold**`` and ````monospace```` if needed in any explanations (but not for variable names and doctest code or multi-line code). Variable, module, function, and class names should be written between single back-ticks (```numpy```). A more extensive example of reST markup can be found in `this example document `_; the `quick reference `_ is useful while editing. Line spacing and indentation are significant and should be carefully followed. Conclusion ---------- This document itself was written in ReStructuredText. :ref:`An example ` of the format shown here is available. .. _Sphinx: https://www.sphinx-doc.org/ .. _pylint: https://pypi.org/project/pylint/ .. _pyflakes: https://pypi.python.org/pypi/pyflakes/ .. _pycodestyle: https://pypi.org/project/pycodestyle/ .. _flake8: https://pypi.python.org/pypi/flake8/ .. _vim-flake8: https://github.com/nvie/vim-flake8 .. _SciPy: https://www.scipy.org .. _numpy-discussion list: https://scipy.org/scipylib/mailing-lists.html ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1641610654.0 numpydoc-1.2/doc/index.rst0000664002342000234200000000135200000000000015144 0ustar00jarrodjarrod===================================== numpydoc -- Numpy's Sphinx extensions ===================================== Numpy's documentation uses several custom extensions to Sphinx. These are shipped in this ``numpydoc`` package, in case you want to make use of them in third-party projects. The ``numpydoc`` extension provides support for the Numpy docstring format in Sphinx, and adds the code description directives ``np:function``, ``np-c:function``, etc. that support the Numpy docstring syntax. - Development: https://github.com/numpy/numpydoc/ - Documentation: https://numpydoc.readthedocs.io/ - PyPI: https://pypi.python.org/pypi/numpydoc/ .. toctree:: :hidden: install format validation release_notes example ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1641783986.0 numpydoc-1.2/doc/install.rst0000664002342000234200000001360600000000000015510 0ustar00jarrodjarrod=============== Getting started =============== Installation ============ This extension requires Python 3.7+, sphinx 1.8+ and is available from: * `numpydoc on PyPI `_ * `numpydoc on GitHub `_ `'numpydoc'` should be added to the ``extensions`` option in your Sphinx ``conf.py``. ``'sphinx.ext.autosummary'`` will automatically be loaded as well. Configuration ============= The following options can be set in your Sphinx ``conf.py``: numpydoc_use_plots : bool Whether to produce ``plot::`` directives for Examples sections that contain ``import matplotlib`` or ``from matplotlib import``. numpydoc_show_class_members : bool Whether to show all members of a class in the Methods and Attributes sections automatically. ``True`` by default. numpydoc_show_inherited_class_members : bool Whether to show all inherited members of a class in the Methods and Attributes sections automatically. If it's false, inherited members won't shown. ``True`` by default. numpydoc_class_members_toctree : bool Whether to create a Sphinx table of contents for the lists of class methods and attributes. If a table of contents is made, Sphinx expects each entry to have a separate page. ``True`` by default. numpydoc_citation_re : str A regular expression matching citations which should be mangled to avoid conflicts due to duplication across the documentation. Defaults to ``[\w-]+``. numpydoc_use_blockquotes : bool Until version 0.8, parameter definitions were shown as blockquotes, rather than in a definition list. If your styling requires blockquotes, switch this config option to True. This option will be removed in version 0.10. numpydoc_attributes_as_param_list : bool Whether to format the Attributes section of a class page in the same way as the Parameter section. If it's False, the Attributes section will be formatted as the Methods section using an autosummary table. ``True`` by default. numpydoc_xref_param_type : bool Whether to create cross-references for the parameter types in the ``Parameters``, ``Other Parameters``, ``Returns`` and ``Yields`` sections of the docstring. ``False`` by default. .. note:: Depending on the link types, the CSS styles might be different. consider overriding e.g. ``span.classifier a span.xref`` and ``span.classifier a code.docutils.literal.notranslate`` CSS classes to achieve a uniform appearance. numpydoc_xref_aliases : dict Mappings to fully qualified paths (or correct ReST references) for the aliases/shortcuts used when specifying the types of parameters. The keys should not have any spaces. Together with the ``intersphinx`` extension, you can map to links in any documentation. The default ``numpydoc_xref_aliases`` will supply some common ``Python`` standard library and ``NumPy`` names for you. Then for your module, a useful ``dict`` may look like the following (e.g., if you were documenting :mod:`sklearn.model_selection`):: numpydoc_xref_aliases = { 'LeaveOneOut': 'sklearn.model_selection.LeaveOneOut', ... } This option depends on the ``numpydoc_xref_param_type`` option being ``True``. numpydoc_xref_ignore : set or ``"all"`` How to handle terms not in ``numpydoc_xref_aliases`` when ``numpydoc_xref_aliases=True``. The value can either be a ``set`` containing terms to ignore, or ``"all"``. In the former case, the set contains words not to cross-reference. Most likely, these are common words used in parameter type descriptions that may be confused for classes of the same name. For example:: numpydoc_xref_ignore = {'type', 'optional', 'default'} The default is an empty set. If the ``numpydoc_xref_ignore="all"``, then all unrecognized terms are ignored, i.e. terms not in ``numpydoc_xref_aliases`` are *not* wrapped in ``:obj:`` roles. This configuration parameter may be useful if you only want to create cross references for a small number of terms. In this case, including the desired cross reference mappings in ``numpydoc_xref_aliases`` and setting ``numpydoc_xref_ignore="all"`` is more convenient than explicitly listing terms to ignore in a set. numpydoc_validation_checks : set The set of validation checks to report during the sphinx build process. The default is an empty set, so docstring validation is not run by default. If ``"all"`` is in the set, then the results of all of the :ref:`built-in validation checks ` are reported. If the set includes ``"all"`` and additional error codes, then all validation checks *except* the listed error codes will be run. If the set contains *only* individual error codes, then only those checks will be run. For example:: # Report warnings for all validation checks numpydoc_validation_checks = {"all"} # Report warnings for all checks *except* for GL01, GL02, and GL05 numpydoc_validation_checks = {"all", "GL01", "GL02", "GL05"} # Only report warnings for the SA01 and EX01 checks numpydoc_validation_checks = {"SA01", "EX01"} numpydoc_validation_exclude : set A container of strings using :py:mod:`re` syntax specifying patterns to ignore for docstring validation. For example, to skip docstring validation for all objects in ``mypkg.mymodule``:: numpydoc_validation_exclude = {"mypkg.mymodule."} If you wanted to also skip getter methods of ``MyClass``:: numpydoc_validation_exclude = {r"mypkg\.mymodule\.", r"MyClass\.get$"} The default is an empty set meaning no objects are excluded from docstring validation. Only has an effect when docstring validation is activated, i.e. ``numpydoc_validation_checks`` is not an empty set. numpydoc_edit_link : bool .. deprecated:: 0.7.0 edit your HTML template instead Whether to insert an edit link after docstrings. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1641610654.0 numpydoc-1.2/doc/make.bat0000664002342000234200000001510100000000000014705 0ustar00jarrodjarrod@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\project-template.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\project-template.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643048445.0 numpydoc-1.2/doc/release_notes.rst0000664002342000234200000002535400000000000016675 0ustar00jarrodjarrodRelease notes ============= .. roughly following https://sphinx-gallery.github.io/dev/maintainers.html, .. 1.0.0 notes were generated by: .. 1. tagging PRs as enhancement/bug/removed .. 2. $ github_changelog_generator -u numpy -p numpydoc --since-tag=v0.9.2 .. 3. $ pandoc CHANGELOG.md --wrap=none -o release_notes.rst .. 4. adding a manual addition (CSS note), tweaking heading levels, adding TOC .. contents:: Page contents :local: :depth: 2 .. note:: For release notes (sparsely) kept prior to 1.0.0, look at the `releases page on GitHub `__. 1.2.0 ----- Release date: 24 January 2022 Requires Python 3.7+ and Sphinx 1.8+. Implemented enhancements ~~~~~~~~~~~~~~~~~~~~~~~~ - Document release process `#357 `__ (`jarrodmillman `__) - Use setuptools `#349 `__ (`jarrodmillman `__) - DOC: Switch docs to pydata-sphinx-theme `#313 `__ (`rossbar `__) - Improve error messages for see also parsing `#306 `__ (`rossbar `__) - ENH: Enable validation during sphinx-build process `#302 `__ (`rossbar `__) - Add a note to the docstring standard about long ‘See Also’ entries. `#300 `__ (`WarrenWeckesser `__) - MAINT: minor refactoring in docscrape `#297 `__ (`rossbar `__) - ENH: Add configuration option for parameter cross-referencing `#295 `__ (`rossbar `__) - ENH: Better warning for sections. `#278 `__ (`Carreau `__) Fixed bugs ~~~~~~~~~~ - How to specify that parameter can equal the string ‘integer’? `#341 `__ - Fix validation bug when parameter type is set of options. `#347 `__ (`rossbar `__) - Escape newline in docstring. `#345 `__ (`Carreau `__) - Correctly validate parameters under the “Other Parameters” section `#337 `__ (`dcbr `__) - BUG: fix an incomplete check in ``Reader.\_error\_location`` `#308 `__ (`rgommers `__) - MAINT: pytest ignore doc directory. `#296 `__ (`rossbar `__) - DOC: fix inaccuracy in validate docstring. `#294 `__ (`rossbar `__) - Fix param parsing. `#286 `__ (`Carreau `__) - BUG: Properly parse See Also when summary on first line. `#283 `__ (`Carreau `__) - BUG: fix role regex. `#280 `__ (`Carreau `__) - fix splitting of parameter lines. `#279 `__ (`Carreau `__) Closed issues ~~~~~~~~~~~~~ - Class methods (@classmethod) are not documented using ``numpydoc`` `#340 `__ - Exclude certain methods from METHODS section `#338 `__ - Warnings is not allowed in “GL06” check `#334 `__ - Add version to style guide `#333 `__ - numpydoc does not render parameters as expected `#329 `__ - 1.1.0: pytest warnings `#324 `__ - RTD configuration - ``latest`` `#321 `__ - Rendering of types in latest doc build `#318 `__ - Anchors for individual sections in numpydoc doc? `#317 `__ - Development documentation not up-to-date `#311 `__ - Warning: autosummary: stub file not found `#290 `__ - Wrong number of Parameter for numpy array. `#285 `__ - syntax to document default values `#284 `__ - Failed See Also Parsing. `#281 `__ - Sphinx emits “WARNING: py:class reference target not found” with numpydoc 1.1.0 `#275 `__ 1.1.0 ----- Implemented enhancements ~~~~~~~~~~~~~~~~~~~~~~~~ - MAINT: Suggestions from reviewing test suite `#271 `__ (`rossbar `__) - DEV: Add testing requirements `#267 `__ (`rossbar `__) - BUG: Defer to autodoc for signatures `#221 `__ (`thequackdaddy `__) Fixed bugs ~~~~~~~~~~ - function signatures for \*args, \**kwargs objects off `#218 `__ - BUG: Connect to earlier event `#269 `__ (`larsoner `__) Closed issues ~~~~~~~~~~~~~ - “Handler for event ‘autodoc-process-docstring’ threw an exception” `#268 `__ - Timing of next release `#249 `__ - self included in list of params for method `#220 `__ Additional notes ~~~~~~~~~~~~~~~~ - Due to merging of `#221 `__, self and cls no longer will appear in method signatures. 1.0.0 ----- Implemented enhancements ~~~~~~~~~~~~~~~~~~~~~~~~ - ENH: Add args and kwargs to example `#258 `__ (`larsoner `__) - MAINT,STY: Upgrade to bionic, and change style similar to NumPy `#253 `__ (`mwtoews `__) - Delay import of Sphinx `#248 `__ (`cgohlke `__) - Adding –validate option \__main_\_ and run new validation `#240 `__ (`datapythonista `__) - Add docstring validation script (from pandas) `#238 `__ (`datapythonista `__) - ENH: Test full output and coverage `#230 `__ (`larsoner `__) - DOC: Add description for blank lines after the docstring. `#229 `__ (`bingyao `__) Fixed bugs ~~~~~~~~~~ - References outside function `#214 `__ - FIX: Get doc of actual class in test `#262 `__ (`larsoner `__) - TST: Add inherited method `#260 `__ (`larsoner `__) - Fixes references outside function (#214) `#259 `__ (`Hoxbro `__) - Disable escaping “\*” on signature `#256 `__ (`tk0miya `__) - MAINT: clean-up unused objects `#254 `__ (`mwtoews `__) - STY: Reword first lines of example.py docstrings `#246 `__ (`justinludwig `__) - DOC: Fixed three formatting issues in docs `#245 `__ (`rossbar `__) - STY Minor style improvements to doc/example.py to pass validation `#243 `__ (`rth `__) - BUG: Allow no . at end if indented `#239 `__ (`larsoner `__) - DOC: Update links and code checkers info in format.rst `#228 `__ (`bingyao `__) - DOC: Update links and info in conf.py. `#227 `__ (`bingyao `__) - BUG: Fix full rebuilds `#226 `__ (`larsoner `__) - MAINT: doctest and pytest `#225 `__ (`larsoner `__) - Py3fy some doctests. `#224 `__ (`anntzer `__) - MAINT: fix trivial source comment typos `#222 `__ (`luzpaz `__) - Add missing headings to code examples `#252 `__ (`Cadair `__) Removed ~~~~~~~ - MNT Drop Python 2.7 and 3.4 support `#236 `__ (`rth `__) Closed issues ~~~~~~~~~~~~~ - Prefix added to reference keys in class docstrings `#263 `__ - Test failure with python 3.9 `#261 `__ - sphinx doc napoleon extension maintainer interest request `#251 `__ - Missing reference to float_power function in the ufunc list `#250 `__ Additional notes ~~~~~~~~~~~~~~~~ - CSS styling changed from NumpyDoc < 0.8 and Sphinx < 2.0 to more properly make use of definition lists. This can cause issues with rendering that can be fixed via CSS, especially when using ``sphinx-rtd-theme``. For more information, see: - https://github.com/numpy/numpydoc/issues/215#issuecomment-568261611 - https://github.com/readthedocs/sphinx_rtd_theme/pull/838 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1641610654.0 numpydoc-1.2/doc/requirements.txt0000664002342000234200000000005600000000000016567 0ustar00jarrodjarrodnumpy matplotlib pydata-sphinx-theme sphinx>2 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1641783986.0 numpydoc-1.2/doc/validation.rst0000664002342000234200000000443700000000000016176 0ustar00jarrodjarrod========== Validation ========== Docstring Validation using Python --------------------------------- To see the Restructured Text generated for an object, the ``numpydoc`` module can be called. For example, to do it for ``numpy.ndarray``, use: .. code-block:: bash $ python -m numpydoc numpy.ndarray This will validate that the docstring can be built. For an exhaustive validation of the formatting of the docstring, use the ``--validate`` parameter. This will report the errors detected, such as incorrect capitalization, wrong order of the sections, and many other issues. Docstring Validation during Sphinx Build ---------------------------------------- It is also possible to run docstring validation as part of the sphinx build process. This behavior is controlled by the ``numpydoc_validation_checks`` configuration parameter in ``conf.py``. For example, to verify that all of the parameters in the function signature are accounted for in the ``Parameters`` section of the docstring, add the following line to ``conf.py``:: numpydoc_validation_checks = {"PR01"} This will cause a sphinx warning to be raised for any (non-module) docstring that has undocumented parameters in the signature. The full set of validation checks can be activated by:: numpydoc_validation_checks = {"all"} The complete validation suite contains `many checks `_ including some for style, capitalization, and grammar. It is unlikely that reporting *all* validation warnings is desirable for most use-cases. Individual checks can be excluded by including them in the set with the special keyword ``"all"``:: # Report warnings for all validation checks except GL01, GL02, and GL05 numpydoc_validation_checks = {"all", "GL01", "GL02", "GL05"} .. _validation_checks: Built-in Validation Checks -------------------------- The ``numpydoc.validation`` module provides a mapping with all of the checks that are run as part of the validation procedure. The mapping is of the form: ``error_code : `` where ``error_code`` provides a shorthand for the check being run, and ```` provides a more detailed message. For example:: "EX01" : "No examples section found" The full mapping of validation checks is given below. .. literalinclude:: ../numpydoc/validate.py :lines: 36-90 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1643049415.0558767 numpydoc-1.2/numpydoc/0000775002342000234200000000000000000000000014373 5ustar00jarrodjarrod././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643048543.0 numpydoc-1.2/numpydoc/__init__.py0000664002342000234200000000041100000000000016500 0ustar00jarrodjarrod""" This package provides the numpydoc Sphinx extension for handling docstrings formatted according to the NumPy documentation format. """ __version__ = '1.2' def setup(app, *args, **kwargs): from .numpydoc import setup return setup(app, *args, **kwargs) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1642724349.0 numpydoc-1.2/numpydoc/__main__.py0000664002342000234200000000306700000000000016473 0ustar00jarrodjarrod""" Implementing `python -m numpydoc` functionality. """ import sys import argparse import ast from .docscrape_sphinx import get_doc_object from .validate import validate, Validator def render_object(import_path, config=None): """Test numpydoc docstring generation for a given object""" # TODO: Move Validator._load_obj to a better place than validate print(get_doc_object(Validator._load_obj(import_path), config=dict(config or []))) return 0 def validate_object(import_path): exit_status = 0 results = validate(import_path) for err_code, err_desc in results["errors"]: exit_status += 1 print(':'.join([import_path, err_code, err_desc])) return exit_status if __name__ == '__main__': ap = argparse.ArgumentParser(description=__doc__) ap.add_argument('import_path', help='e.g. numpy.ndarray') def _parse_config(s): key, _, value = s.partition('=') value = ast.literal_eval(value) return key, value ap.add_argument('-c', '--config', type=_parse_config, action='append', help='key=val where val will be parsed by literal_eval, ' 'e.g. -c use_plots=True. Multiple -c can be used.') ap.add_argument('--validate', action='store_true', help='validate the object and report errors') args = ap.parse_args() if args.validate: exit_code = validate_object(args.import_path) else: exit_code = render_object(args.import_path, args.config) sys.exit(exit_code) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1642724375.0 numpydoc-1.2/numpydoc/docscrape.py0000664002342000234200000005414300000000000016717 0ustar00jarrodjarrod"""Extract reference documentation from the NumPy source tree. """ import inspect import textwrap import re import pydoc from warnings import warn from collections import namedtuple from collections.abc import Callable, Mapping import copy import sys def strip_blank_lines(l): "Remove leading and trailing blank lines from a list of lines" while l and not l[0].strip(): del l[0] while l and not l[-1].strip(): del l[-1] return l class Reader: """A line-based string reader. """ def __init__(self, data): """ Parameters ---------- data : str String with lines separated by '\\n'. """ if isinstance(data, list): self._str = data else: self._str = data.split('\n') # store string as list of lines self.reset() def __getitem__(self, n): return self._str[n] def reset(self): self._l = 0 # current line nr def read(self): if not self.eof(): out = self[self._l] self._l += 1 return out else: return '' def seek_next_non_empty_line(self): for l in self[self._l:]: if l.strip(): break else: self._l += 1 def eof(self): return self._l >= len(self._str) def read_to_condition(self, condition_func): start = self._l for line in self[start:]: if condition_func(line): return self[start:self._l] self._l += 1 if self.eof(): return self[start:self._l+1] return [] def read_to_next_empty_line(self): self.seek_next_non_empty_line() def is_empty(line): return not line.strip() return self.read_to_condition(is_empty) def read_to_next_unindented_line(self): def is_unindented(line): return (line.strip() and (len(line.lstrip()) == len(line))) return self.read_to_condition(is_unindented) def peek(self, n=0): if self._l + n < len(self._str): return self[self._l + n] else: return '' def is_empty(self): return not ''.join(self._str).strip() class ParseError(Exception): def __str__(self): message = self.args[0] if hasattr(self, 'docstring'): message = f"{message} in {self.docstring!r}" return message Parameter = namedtuple('Parameter', ['name', 'type', 'desc']) class NumpyDocString(Mapping): """Parses a numpydoc string to an abstract representation Instances define a mapping from section title to structured data. """ sections = { 'Signature': '', 'Summary': [''], 'Extended Summary': [], 'Parameters': [], 'Returns': [], 'Yields': [], 'Receives': [], 'Raises': [], 'Warns': [], 'Other Parameters': [], 'Attributes': [], 'Methods': [], 'See Also': [], 'Notes': [], 'Warnings': [], 'References': '', 'Examples': '', 'index': {} } def __init__(self, docstring, config=None): orig_docstring = docstring docstring = textwrap.dedent(docstring).split('\n') self._doc = Reader(docstring) self._parsed_data = copy.deepcopy(self.sections) try: self._parse() except ParseError as e: e.docstring = orig_docstring raise def __getitem__(self, key): return self._parsed_data[key] def __setitem__(self, key, val): if key not in self._parsed_data: self._error_location(f"Unknown section {key}", error=False) else: self._parsed_data[key] = val def __iter__(self): return iter(self._parsed_data) def __len__(self): return len(self._parsed_data) def _is_at_section(self): self._doc.seek_next_non_empty_line() if self._doc.eof(): return False l1 = self._doc.peek().strip() # e.g. Parameters if l1.startswith('.. index::'): return True l2 = self._doc.peek(1).strip() # ---------- or ========== if len(l2) >= 3 and (set(l2) in ({'-'}, {'='}) ) and len(l2) != len(l1): snip = '\n'.join(self._doc._str[:2])+'...' self._error_location("potentially wrong underline length... \n%s \n%s in \n%s"\ % (l1, l2, snip), error=False) return l2.startswith('-'*len(l1)) or l2.startswith('='*len(l1)) def _strip(self, doc): i = 0 j = 0 for i, line in enumerate(doc): if line.strip(): break for j, line in enumerate(doc[::-1]): if line.strip(): break return doc[i:len(doc)-j] def _read_to_next_section(self): section = self._doc.read_to_next_empty_line() while not self._is_at_section() and not self._doc.eof(): if not self._doc.peek(-1).strip(): # previous line was empty section += [''] section += self._doc.read_to_next_empty_line() return section def _read_sections(self): while not self._doc.eof(): data = self._read_to_next_section() name = data[0].strip() if name.startswith('..'): # index section yield name, data[1:] elif len(data) < 2: yield StopIteration else: yield name, self._strip(data[2:]) def _parse_param_list(self, content, single_element_is_type=False): content = dedent_lines(content) r = Reader(content) params = [] while not r.eof(): header = r.read().strip() if ' :' in header: arg_name, arg_type = header.split(' :', maxsplit=1) arg_name, arg_type = arg_name.strip(), arg_type.strip() else: if single_element_is_type: arg_name, arg_type = '', header else: arg_name, arg_type = header, '' desc = r.read_to_next_unindented_line() desc = dedent_lines(desc) desc = strip_blank_lines(desc) params.append(Parameter(arg_name, arg_type, desc)) return params # See also supports the following formats. # # # SPACE* COLON SPACE+ SPACE* # ( COMMA SPACE+ )+ (COMMA | PERIOD)? SPACE* # ( COMMA SPACE+ )* SPACE* COLON SPACE+ SPACE* # is one of # # COLON COLON BACKTICK BACKTICK # where # is a legal function name, and # is any nonempty sequence of word characters. # Examples: func_f1 :meth:`func_h1` :obj:`~baz.obj_r` :class:`class_j` # is a string describing the function. _role = r":(?P(py:)?\w+):" _funcbacktick = r"`(?P(?:~\w+\.)?[a-zA-Z0-9_\.-]+)`" _funcplain = r"(?P[a-zA-Z0-9_\.-]+)" _funcname = r"(" + _role + _funcbacktick + r"|" + _funcplain + r")" _funcnamenext = _funcname.replace('role', 'rolenext') _funcnamenext = _funcnamenext.replace('name', 'namenext') _description = r"(?P\s*:(\s+(?P\S+.*))?)?\s*$" _func_rgx = re.compile(r"^\s*" + _funcname + r"\s*") _line_rgx = re.compile( r"^\s*" + r"(?P" + # group for all function names _funcname + r"(?P([,]\s+" + _funcnamenext + r")*)" + r")" + # end of "allfuncs" r"(?P[,\.])?" + # Some function lists have a trailing comma (or period) '\s*' _description) # Empty elements are replaced with '..' empty_description = '..' def _parse_see_also(self, content): """ func_name : Descriptive text continued text another_func_name : Descriptive text func_name1, func_name2, :meth:`func_name`, func_name3 """ content = dedent_lines(content) items = [] def parse_item_name(text): """Match ':role:`name`' or 'name'.""" m = self._func_rgx.match(text) if not m: self._error_location(f"Error parsing See Also entry {line!r}") role = m.group('role') name = m.group('name') if role else m.group('name2') return name, role, m.end() rest = [] for line in content: if not line.strip(): continue line_match = self._line_rgx.match(line) description = None if line_match: description = line_match.group('desc') if line_match.group('trailing') and description: self._error_location( 'Unexpected comma or period after function list at index %d of ' 'line "%s"' % (line_match.end('trailing'), line), error=False) if not description and line.startswith(' '): rest.append(line.strip()) elif line_match: funcs = [] text = line_match.group('allfuncs') while True: if not text.strip(): break name, role, match_end = parse_item_name(text) funcs.append((name, role)) text = text[match_end:].strip() if text and text[0] == ',': text = text[1:].strip() rest = list(filter(None, [description])) items.append((funcs, rest)) else: self._error_location(f"Error parsing See Also entry {line!r}") return items def _parse_index(self, section, content): """ .. index: default :refguide: something, else, and more """ def strip_each_in(lst): return [s.strip() for s in lst] out = {} section = section.split('::') if len(section) > 1: out['default'] = strip_each_in(section[1].split(','))[0] for line in content: line = line.split(':') if len(line) > 2: out[line[1]] = strip_each_in(line[2].split(',')) return out def _parse_summary(self): """Grab signature (if given) and summary""" if self._is_at_section(): return # If several signatures present, take the last one while True: summary = self._doc.read_to_next_empty_line() summary_str = " ".join([s.strip() for s in summary]).strip() compiled = re.compile(r'^([\w., ]+=)?\s*[\w\.]+\(.*\)$') if compiled.match(summary_str): self['Signature'] = summary_str if not self._is_at_section(): continue break if summary is not None: self['Summary'] = summary if not self._is_at_section(): self['Extended Summary'] = self._read_to_next_section() def _parse(self): self._doc.reset() self._parse_summary() sections = list(self._read_sections()) section_names = set([section for section, content in sections]) has_returns = 'Returns' in section_names has_yields = 'Yields' in section_names # We could do more tests, but we are not. Arbitrarily. if has_returns and has_yields: msg = 'Docstring contains both a Returns and Yields section.' raise ValueError(msg) if not has_yields and 'Receives' in section_names: msg = 'Docstring contains a Receives section but not Yields.' raise ValueError(msg) for (section, content) in sections: if not section.startswith('..'): section = (s.capitalize() for s in section.split(' ')) section = ' '.join(section) if self.get(section): self._error_location("The section %s appears twice in %s" % (section, '\n'.join(self._doc._str))) if section in ('Parameters', 'Other Parameters', 'Attributes', 'Methods'): self[section] = self._parse_param_list(content) elif section in ('Returns', 'Yields', 'Raises', 'Warns', 'Receives'): self[section] = self._parse_param_list( content, single_element_is_type=True) elif section.startswith('.. index::'): self['index'] = self._parse_index(section, content) elif section == 'See Also': self['See Also'] = self._parse_see_also(content) else: self[section] = content @property def _obj(self): if hasattr(self, '_cls'): return self._cls elif hasattr(self, '_f'): return self._f return None def _error_location(self, msg, error=True): if self._obj is not None: # we know where the docs came from: try: filename = inspect.getsourcefile(self._obj) except TypeError: filename = None msg += f" in the docstring of {self._obj.__name__}" msg += f" in {filename}." if filename else "" if error: raise ValueError(msg) else: warn(msg) # string conversion routines def _str_header(self, name, symbol='-'): return [name, len(name)*symbol] def _str_indent(self, doc, indent=4): return [' '*indent + line for line in doc] def _str_signature(self): if self['Signature']: return [self['Signature'].replace('*', r'\*')] + [''] return [''] def _str_summary(self): if self['Summary']: return self['Summary'] + [''] return [] def _str_extended_summary(self): if self['Extended Summary']: return self['Extended Summary'] + [''] return [] def _str_param_list(self, name): out = [] if self[name]: out += self._str_header(name) for param in self[name]: parts = [] if param.name: parts.append(param.name) if param.type: parts.append(param.type) out += [' : '.join(parts)] if param.desc and ''.join(param.desc).strip(): out += self._str_indent(param.desc) out += [''] return out def _str_section(self, name): out = [] if self[name]: out += self._str_header(name) out += self[name] out += [''] return out def _str_see_also(self, func_role): if not self['See Also']: return [] out = [] out += self._str_header("See Also") out += [''] last_had_desc = True for funcs, desc in self['See Also']: assert isinstance(funcs, list) links = [] for func, role in funcs: if role: link = f':{role}:`{func}`' elif func_role: link = f':{func_role}:`{func}`' else: link = f"`{func}`_" links.append(link) link = ', '.join(links) out += [link] if desc: out += self._str_indent([' '.join(desc)]) last_had_desc = True else: last_had_desc = False out += self._str_indent([self.empty_description]) if last_had_desc: out += [''] out += [''] return out def _str_index(self): idx = self['index'] out = [] output_index = False default_index = idx.get('default', '') if default_index: output_index = True out += [f'.. index:: {default_index}'] for section, references in idx.items(): if section == 'default': continue output_index = True out += [f" :{section}: {', '.join(references)}"] if output_index: return out return '' def __str__(self, func_role=''): out = [] out += self._str_signature() out += self._str_summary() out += self._str_extended_summary() for param_list in ('Parameters', 'Returns', 'Yields', 'Receives', 'Other Parameters', 'Raises', 'Warns'): out += self._str_param_list(param_list) out += self._str_section('Warnings') out += self._str_see_also(func_role) for s in ('Notes', 'References', 'Examples'): out += self._str_section(s) for param_list in ('Attributes', 'Methods'): out += self._str_param_list(param_list) out += self._str_index() return '\n'.join(out) def dedent_lines(lines): """Deindent a list of lines maximally""" return textwrap.dedent("\n".join(lines)).split("\n") class FunctionDoc(NumpyDocString): def __init__(self, func, role='func', doc=None, config=None): self._f = func self._role = role # e.g. "func" or "meth" if doc is None: if func is None: raise ValueError("No function or docstring given") doc = inspect.getdoc(func) or '' if config is None: config = {} NumpyDocString.__init__(self, doc, config) def get_func(self): func_name = getattr(self._f, '__name__', self.__class__.__name__) if inspect.isclass(self._f): func = getattr(self._f, '__call__', self._f.__init__) else: func = self._f return func, func_name def __str__(self): out = '' func, func_name = self.get_func() roles = {'func': 'function', 'meth': 'method'} if self._role: if self._role not in roles: print(f"Warning: invalid role {self._role}") out += f".. {roles.get(self._role, '')}:: {func_name}\n \n\n" out += super().__str__(func_role=self._role) return out class ObjDoc(NumpyDocString): def __init__(self, obj, doc=None, config=None): self._f = obj if config is None: config = {} NumpyDocString.__init__(self, doc, config=config) class ClassDoc(NumpyDocString): extra_public_methods = ['__call__'] def __init__(self, cls, doc=None, modulename='', func_doc=FunctionDoc, config=None): if not inspect.isclass(cls) and cls is not None: raise ValueError(f"Expected a class or None, but got {cls!r}") self._cls = cls if 'sphinx' in sys.modules: from sphinx.ext.autodoc import ALL else: ALL = object() if config is None: config = {} self.show_inherited_members = config.get( 'show_inherited_class_members', True) if modulename and not modulename.endswith('.'): modulename += '.' self._mod = modulename if doc is None: if cls is None: raise ValueError("No class or documentation string given") doc = pydoc.getdoc(cls) NumpyDocString.__init__(self, doc) _members = config.get('members', []) if _members is ALL: _members = None _exclude = config.get('exclude-members', []) if config.get('show_class_members', True) and _exclude is not ALL: def splitlines_x(s): if not s: return [] else: return s.splitlines() for field, items in [('Methods', self.methods), ('Attributes', self.properties)]: if not self[field]: doc_list = [] for name in sorted(items): if (name in _exclude or (_members and name not in _members)): continue try: doc_item = pydoc.getdoc(getattr(self._cls, name)) doc_list.append( Parameter(name, '', splitlines_x(doc_item))) except AttributeError: pass # method doesn't exist self[field] = doc_list @property def methods(self): if self._cls is None: return [] return [name for name, func in inspect.getmembers(self._cls) if ((not name.startswith('_') or name in self.extra_public_methods) and isinstance(func, Callable) and self._is_show_member(name))] @property def properties(self): if self._cls is None: return [] return [name for name, func in inspect.getmembers(self._cls) if (not name.startswith('_') and (func is None or isinstance(func, property) or inspect.isdatadescriptor(func)) and self._is_show_member(name))] def _is_show_member(self, name): if self.show_inherited_members: return True # show all class members if name not in self._cls.__dict__: return False # class member is inherited, we do not show it return True def get_doc_object(obj, what=None, doc=None, config=None): if what is None: if inspect.isclass(obj): what = 'class' elif inspect.ismodule(obj): what = 'module' elif isinstance(obj, Callable): what = 'function' else: what = 'object' if config is None: config = {} if what == 'class': return ClassDoc(obj, func_doc=FunctionDoc, doc=doc, config=config) elif what in ('function', 'method'): return FunctionDoc(obj, doc=doc, config=config) else: if doc is None: doc = pydoc.getdoc(obj) return ObjDoc(obj, doc, config=config) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1642724375.0 numpydoc-1.2/numpydoc/docscrape_sphinx.py0000664002342000234200000003740500000000000020312 0ustar00jarrodjarrodimport re import inspect import textwrap import pydoc from collections.abc import Callable import os from jinja2 import FileSystemLoader from jinja2.sandbox import SandboxedEnvironment import sphinx from sphinx.jinja2glue import BuiltinTemplateLoader from .docscrape import NumpyDocString, FunctionDoc, ClassDoc, ObjDoc from .xref import make_xref IMPORT_MATPLOTLIB_RE = r'\b(import +matplotlib|from +matplotlib +import)\b' class SphinxDocString(NumpyDocString): def __init__(self, docstring, config=None): if config is None: config = {} NumpyDocString.__init__(self, docstring, config=config) self.load_config(config) def load_config(self, config): self.use_plots = config.get('use_plots', False) self.use_blockquotes = config.get('use_blockquotes', False) self.class_members_toctree = config.get('class_members_toctree', True) self.attributes_as_param_list = config.get('attributes_as_param_list', True) self.xref_param_type = config.get('xref_param_type', False) self.xref_aliases = config.get('xref_aliases', dict()) self.xref_ignore = config.get('xref_ignore', set()) self.template = config.get('template', None) if self.template is None: template_dirs = [os.path.join(os.path.dirname(__file__), 'templates')] template_loader = FileSystemLoader(template_dirs) template_env = SandboxedEnvironment(loader=template_loader) self.template = template_env.get_template('numpydoc_docstring.rst') # string conversion routines def _str_header(self, name, symbol='`'): return ['.. rubric:: ' + name, ''] def _str_field_list(self, name): return [':' + name + ':'] def _str_indent(self, doc, indent=4): out = [] for line in doc: out += [' '*indent + line] return out def _str_signature(self): return [''] def _str_summary(self): return self['Summary'] + [''] def _str_extended_summary(self): return self['Extended Summary'] + [''] def _str_returns(self, name='Returns'): named_fmt = '**%s** : %s' unnamed_fmt = '%s' out = [] if self[name]: out += self._str_field_list(name) out += [''] for param in self[name]: param_type = param.type if param_type and self.xref_param_type: param_type = make_xref( param_type, self.xref_aliases, self.xref_ignore ) if param.name: out += self._str_indent([named_fmt % (param.name.strip(), param_type)]) else: out += self._str_indent([unnamed_fmt % param_type.strip()]) if not param.desc: out += self._str_indent(['..'], 8) else: if self.use_blockquotes: out += [''] out += self._str_indent(param.desc, 8) out += [''] return out def _escape_args_and_kwargs(self, name): if name[:2] == '**': return r'\*\*' + name[2:] elif name[:1] == '*': return r'\*' + name[1:] else: return name def _process_param(self, param, desc, fake_autosummary): """Determine how to display a parameter Emulates autosummary behavior if fake_autosummary Parameters ---------- param : str The name of the parameter desc : list of str The parameter description as given in the docstring. This is ignored when autosummary logic applies. fake_autosummary : bool If True, autosummary-style behaviour will apply for params that are attributes of the class and have a docstring. Returns ------- display_param : str The marked up parameter name for display. This may include a link to the corresponding attribute's own documentation. desc : list of str A list of description lines. This may be identical to the input ``desc``, if ``autosum is None`` or ``param`` is not a class attribute, or it will be a summary of the class attribute's docstring. Notes ----- This does not have the autosummary functionality to display a method's signature, and hence is not used to format methods. It may be complicated to incorporate autosummary's signature mangling, as it relies on Sphinx's plugin mechanism. """ param = self._escape_args_and_kwargs(param.strip()) # param = param.strip() # XXX: If changing the following, please check the rendering when param # ends with '_', e.g. 'word_' # See https://github.com/numpy/numpydoc/pull/144 display_param = f'**{param}**' if not fake_autosummary: return display_param, desc param_obj = getattr(self._obj, param, None) if not (callable(param_obj) or isinstance(param_obj, property) or inspect.isgetsetdescriptor(param_obj) or inspect.ismemberdescriptor(param_obj)): param_obj = None obj_doc = pydoc.getdoc(param_obj) if not (param_obj and obj_doc): return display_param, desc prefix = getattr(self, '_name', '') if prefix: link_prefix = f'{prefix}.' else: link_prefix = '' # Referenced object has a docstring display_param = f':obj:`{param} <{link_prefix}{param}>`' if obj_doc: # Overwrite desc. Take summary logic of autosummary desc = re.split(r'\n\s*\n', obj_doc.strip(), 1)[0] # XXX: Should this have DOTALL? # It does not in autosummary m = re.search(r"^([A-Z].*?\.)(?:\s|$)", ' '.join(desc.split())) if m: desc = m.group(1).strip() else: desc = desc.partition('\n')[0] desc = desc.split('\n') return display_param, desc def _str_param_list(self, name, fake_autosummary=False): """Generate RST for a listing of parameters or similar Parameter names are displayed as bold text, and descriptions are in blockquotes. Descriptions may therefore contain block markup as well. Parameters ---------- name : str Section name (e.g. Parameters) fake_autosummary : bool When True, the parameter names may correspond to attributes of the object beign documented, usually ``property`` instances on a class. In this case, names will be linked to fuller descriptions. Returns ------- rst : list of str """ out = [] if self[name]: out += self._str_field_list(name) out += [''] for param in self[name]: display_param, desc = self._process_param(param.name, param.desc, fake_autosummary) parts = [] if display_param: parts.append(display_param) param_type = param.type if param_type: param_type = param.type if self.xref_param_type: param_type = make_xref( param_type, self.xref_aliases, self.xref_ignore ) parts.append(param_type) out += self._str_indent([' : '.join(parts)]) if desc and self.use_blockquotes: out += [''] elif not desc: # empty definition desc = ['..'] out += self._str_indent(desc, 8) out += [''] return out def _str_member_list(self, name): """ Generate a member listing, autosummary:: table where possible, and a table where not. """ out = [] if self[name]: out += [f'.. rubric:: {name}', ''] prefix = getattr(self, '_name', '') if prefix: prefix = f'~{prefix}.' autosum = [] others = [] for param in self[name]: param = param._replace(name=param.name.strip()) # Check if the referenced member can have a docstring or not param_obj = getattr(self._obj, param.name, None) if not (callable(param_obj) or isinstance(param_obj, property) or inspect.isdatadescriptor(param_obj)): param_obj = None if param_obj and pydoc.getdoc(param_obj): # Referenced object has a docstring autosum += [f" {prefix}{param.name}"] else: others.append(param) if autosum: out += ['.. autosummary::'] if self.class_members_toctree: out += [' :toctree:'] out += [''] + autosum if others: maxlen_0 = max(3, max([len(p.name) + 4 for p in others])) hdr = "=" * maxlen_0 + " " + "=" * 10 fmt = '%%%ds %%s ' % (maxlen_0,) out += ['', '', hdr] for param in others: name = "**" + param.name.strip() + "**" desc = " ".join(x.strip() for x in param.desc).strip() if param.type: desc = f"({param.type}) {desc}" out += [fmt % (name, desc)] out += [hdr] out += [''] return out def _str_section(self, name): out = [] if self[name]: out += self._str_header(name) content = textwrap.dedent("\n".join(self[name])).split("\n") out += content out += [''] return out def _str_see_also(self, func_role): out = [] if self['See Also']: see_also = super()._str_see_also(func_role) out = ['.. seealso::', ''] out += self._str_indent(see_also[2:]) return out def _str_warnings(self): out = [] if self['Warnings']: out = ['.. warning::', ''] out += self._str_indent(self['Warnings']) out += [''] return out def _str_index(self): idx = self['index'] out = [] if len(idx) == 0: return out out += [f".. index:: {idx.get('default', '')}"] for section, references in idx.items(): if section == 'default': continue elif section == 'refguide': out += [f" single: {', '.join(references)}"] else: out += [f" {section}: {','.join(references)}"] out += [''] return out def _str_references(self): out = [] if self['References']: out += self._str_header('References') if isinstance(self['References'], str): self['References'] = [self['References']] out.extend(self['References']) out += [''] # Latex collects all references to a separate bibliography, # so we need to insert links to it out += ['.. only:: latex', ''] items = [] for line in self['References']: m = re.match(r'.. \[([a-z0-9._-]+)\]', line, re.I) if m: items.append(m.group(1)) out += [' ' + ", ".join([f"[{item}]_" for item in items]), ''] return out def _str_examples(self): examples_str = "\n".join(self['Examples']) if (self.use_plots and re.search(IMPORT_MATPLOTLIB_RE, examples_str) and 'plot::' not in examples_str): out = [] out += self._str_header('Examples') out += ['.. plot::', ''] out += self._str_indent(self['Examples']) out += [''] return out else: return self._str_section('Examples') def __str__(self, indent=0, func_role="obj"): ns = { 'signature': self._str_signature(), 'index': self._str_index(), 'summary': self._str_summary(), 'extended_summary': self._str_extended_summary(), 'parameters': self._str_param_list('Parameters'), 'returns': self._str_returns('Returns'), 'yields': self._str_returns('Yields'), 'receives': self._str_returns('Receives'), 'other_parameters': self._str_param_list('Other Parameters'), 'raises': self._str_returns('Raises'), 'warns': self._str_returns('Warns'), 'warnings': self._str_warnings(), 'see_also': self._str_see_also(func_role), 'notes': self._str_section('Notes'), 'references': self._str_references(), 'examples': self._str_examples(), 'attributes': self._str_param_list('Attributes', fake_autosummary=True) if self.attributes_as_param_list else self._str_member_list('Attributes'), 'methods': self._str_member_list('Methods'), } ns = dict((k, '\n'.join(v)) for k, v in ns.items()) rendered = self.template.render(**ns) return '\n'.join(self._str_indent(rendered.split('\n'), indent)) class SphinxFunctionDoc(SphinxDocString, FunctionDoc): def __init__(self, obj, doc=None, config=None): if config is None: config = {} self.load_config(config) FunctionDoc.__init__(self, obj, doc=doc, config=config) class SphinxClassDoc(SphinxDocString, ClassDoc): def __init__(self, obj, doc=None, func_doc=None, config=None): if config is None: config = {} self.load_config(config) ClassDoc.__init__(self, obj, doc=doc, func_doc=None, config=config) class SphinxObjDoc(SphinxDocString, ObjDoc): def __init__(self, obj, doc=None, config=None): if config is None: config = {} self.load_config(config) ObjDoc.__init__(self, obj, doc=doc, config=config) # TODO: refactor to use docscrape.get_doc_object def get_doc_object(obj, what=None, doc=None, config=None, builder=None): if what is None: if inspect.isclass(obj): what = 'class' elif inspect.ismodule(obj): what = 'module' elif isinstance(obj, Callable): what = 'function' else: what = 'object' if config is None: config = {} template_dirs = [os.path.join(os.path.dirname(__file__), 'templates')] if builder is not None: template_loader = BuiltinTemplateLoader() template_loader.init(builder, dirs=template_dirs) else: template_loader = FileSystemLoader(template_dirs) template_env = SandboxedEnvironment(loader=template_loader) config['template'] = template_env.get_template('numpydoc_docstring.rst') if what == 'class': return SphinxClassDoc(obj, func_doc=SphinxFunctionDoc, doc=doc, config=config) elif what in ('function', 'method'): return SphinxFunctionDoc(obj, doc=doc, config=config) else: if doc is None: doc = pydoc.getdoc(obj) return SphinxObjDoc(obj, doc, config=config) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1642724370.0 numpydoc-1.2/numpydoc/numpydoc.py0000664002342000234200000004177100000000000016615 0ustar00jarrodjarrod""" ======== numpydoc ======== Sphinx extension that handles docstrings in the Numpy standard format. [1] It will: - Convert Parameters etc. sections to field lists. - Convert See Also section to a See also entry. - Renumber references. - Extract the signature from the docstring, if it can't be determined otherwise. .. [1] https://github.com/numpy/numpydoc """ from copy import deepcopy import re import pydoc import inspect from collections.abc import Callable import hashlib import itertools from docutils.nodes import citation, Text, section, comment, reference import sphinx from sphinx.addnodes import pending_xref, desc_content from sphinx.util import logging from sphinx.errors import ExtensionError if sphinx.__version__ < '1.8': raise RuntimeError("Sphinx 1.8 or newer is required") from .docscrape_sphinx import get_doc_object from .validate import validate, ERROR_MSGS from .xref import DEFAULT_LINKS from . import __version__ logger = logging.getLogger(__name__) HASH_LEN = 12 def rename_references(app, what, name, obj, options, lines): # decorate reference numbers so that there are no duplicates # these are later undecorated in the doctree, in relabel_references references = set() for line in lines: line = line.strip() m = re.match(r'^\.\. +\[(%s)\]' % app.config.numpydoc_citation_re, line, re.I) if m: references.add(m.group(1)) if references: # we use a hash to mangle the reference name to avoid invalid names sha = hashlib.sha256() sha.update(name.encode('utf8')) prefix = 'R' + sha.hexdigest()[:HASH_LEN] for r in references: new_r = prefix + '-' + r for i, line in enumerate(lines): lines[i] = lines[i].replace(f'[{r}]_', f'[{new_r}]_') lines[i] = lines[i].replace(f'.. [{r}]', f'.. [{new_r}]') def _is_cite_in_numpydoc_docstring(citation_node): # Find DEDUPLICATION_TAG in comment as last node of sibling section # XXX: I failed to use citation_node.traverse to do this: section_node = citation_node.parent def is_docstring_section(node): return isinstance(node, (section, desc_content)) while not is_docstring_section(section_node): section_node = section_node.parent if section_node is None: return False sibling_sections = itertools.chain(section_node.traverse(is_docstring_section, include_self=True, descend=False, siblings=True)) for sibling_section in sibling_sections: if not sibling_section.children: continue for child in sibling_section.children[::-1]: if not isinstance(child, comment): continue if child.rawsource.strip() == DEDUPLICATION_TAG.strip(): return True return False def relabel_references(app, doc): # Change 'hash-ref' to 'ref' in label text for citation_node in doc.traverse(citation): if not _is_cite_in_numpydoc_docstring(citation_node): continue label_node = citation_node[0] prefix, _, new_label = label_node[0].astext().partition('-') assert len(prefix) == HASH_LEN + 1 new_text = Text(new_label) label_node.replace(label_node[0], new_text) for id_ in citation_node['backrefs']: ref = doc.ids[id_] ref_text = ref[0] # Sphinx has created pending_xref nodes with [reftext] text. def matching_pending_xref(node): return (isinstance(node, pending_xref) and node[0].astext() == f'[{ref_text}]') for xref_node in ref.parent.traverse(matching_pending_xref): xref_node.replace(xref_node[0], Text(f'[{new_text}]')) ref.replace(ref_text, new_text.copy()) def clean_backrefs(app, doc, docname): # only::latex directive has resulted in citation backrefs without reference known_ref_ids = set() for ref in doc.traverse(reference, descend=True): for id_ in ref['ids']: known_ref_ids.add(id_) for citation_node in doc.traverse(citation, descend=True): # remove backrefs to non-existent refs citation_node['backrefs'] = [id_ for id_ in citation_node['backrefs'] if id_ in known_ref_ids] DEDUPLICATION_TAG = ' !! processed by numpydoc !!' def mangle_docstrings(app, what, name, obj, options, lines): if DEDUPLICATION_TAG in lines: return cfg = {'use_plots': app.config.numpydoc_use_plots, 'use_blockquotes': app.config.numpydoc_use_blockquotes, 'show_class_members': app.config.numpydoc_show_class_members, 'show_inherited_class_members': app.config.numpydoc_show_inherited_class_members, 'class_members_toctree': app.config.numpydoc_class_members_toctree, 'attributes_as_param_list': app.config.numpydoc_attributes_as_param_list, 'xref_param_type': app.config.numpydoc_xref_param_type, 'xref_aliases': app.config.numpydoc_xref_aliases_complete, 'xref_ignore': app.config.numpydoc_xref_ignore, } cfg.update(options or {}) u_NL = '\n' if what == 'module': # Strip top title pattern = '^\\s*[#*=]{4,}\\n[a-z0-9 -]+\\n[#*=]{4,}\\s*' title_re = re.compile(pattern, re.I | re.S) lines[:] = title_re.sub('', u_NL.join(lines)).split(u_NL) else: try: doc = get_doc_object(obj, what, u_NL.join(lines), config=cfg, builder=app.builder) lines[:] = str(doc).split(u_NL) except Exception: logger.error('[numpydoc] While processing docstring for %r', name) raise if app.config.numpydoc_validation_checks: # If the user has supplied patterns to ignore via the # numpydoc_validation_exclude config option, skip validation for # any objs whose name matches any of the patterns excluder = app.config.numpydoc_validation_excluder exclude_from_validation = excluder.search(name) if excluder else False if not exclude_from_validation: # TODO: Currently, all validation checks are run and only those # selected via config are reported. It would be more efficient to # only run the selected checks. errors = validate(doc)["errors"] if {err[0] for err in errors} & app.config.numpydoc_validation_checks: msg = ( f"[numpydoc] Validation warnings while processing " f"docstring for {name!r}:\n" ) for err in errors: if err[0] in app.config.numpydoc_validation_checks: msg += f" {err[0]}: {err[1]}\n" logger.warning(msg) if (app.config.numpydoc_edit_link and hasattr(obj, '__name__') and obj.__name__): if hasattr(obj, '__module__'): v = dict(full_name=f"{obj.__module__}.{obj.__name__}") else: v = dict(full_name=obj.__name__) lines += ['', '.. htmlonly::', ''] lines += [f' {x}' for x in (app.config.numpydoc_edit_link % v).split("\n")] # call function to replace reference numbers so that there are no # duplicates rename_references(app, what, name, obj, options, lines) lines += ['..', DEDUPLICATION_TAG] def mangle_signature(app, what, name, obj, options, sig, retann): # Do not try to inspect classes that don't define `__init__` if (inspect.isclass(obj) and (not hasattr(obj, '__init__') or 'initializes x; see ' in pydoc.getdoc(obj.__init__))): return '', '' if not (isinstance(obj, Callable) or hasattr(obj, '__argspec_is_invalid_')): return if not hasattr(obj, '__doc__'): return doc = get_doc_object(obj, config={'show_class_members': False}) sig = (doc['Signature'] or _clean_text_signature(getattr(obj, '__text_signature__', None))) if sig: sig = re.sub("^[^(]*", "", sig) return sig, '' def _clean_text_signature(sig): if sig is None: return None start_pattern = re.compile(r"^[^(]*\(") start, end = start_pattern.search(sig).span() start_sig = sig[start:end] sig = sig[end:-1] sig = re.sub(r'^\$(self|module|type)(,\s|$)','' , sig, count=1) sig = re.sub(r'(^|(?<=,\s))/,\s\*', '*', sig, count=1) return start_sig + sig + ')' def setup(app, get_doc_object_=get_doc_object): if not hasattr(app, 'add_config_value'): return # probably called by nose, better bail out global get_doc_object get_doc_object = get_doc_object_ app.setup_extension('sphinx.ext.autosummary') # Once we bump our Sphinx requirement higher (1.7 or 1.8?) # we can just connect to config-inited try: app.connect('config-inited', update_config) except ExtensionError: app.connect('builder-inited', update_config) app.connect('autodoc-process-docstring', mangle_docstrings) app.connect('autodoc-process-signature', mangle_signature) app.connect('doctree-read', relabel_references) app.connect('doctree-resolved', clean_backrefs) app.add_config_value('numpydoc_edit_link', None, False) app.add_config_value('numpydoc_use_plots', None, False) app.add_config_value('numpydoc_use_blockquotes', None, False) app.add_config_value('numpydoc_show_class_members', True, True) app.add_config_value('numpydoc_show_inherited_class_members', True, True) app.add_config_value('numpydoc_class_members_toctree', True, True) app.add_config_value('numpydoc_citation_re', '[a-z0-9_.-]+', True) app.add_config_value('numpydoc_attributes_as_param_list', True, True) app.add_config_value('numpydoc_xref_param_type', False, True) app.add_config_value('numpydoc_xref_aliases', dict(), True) app.add_config_value('numpydoc_xref_ignore', set(), True) app.add_config_value('numpydoc_validation_checks', set(), True) app.add_config_value('numpydoc_validation_exclude', set(), False) # Extra mangling domains app.add_domain(NumpyPythonDomain) app.add_domain(NumpyCDomain) metadata = {'version': __version__, 'parallel_read_safe': True} return metadata def update_config(app, config=None): """Update the configuration with default values.""" if config is None: # needed for testing and old Sphinx config = app.config # Do not simply overwrite the `app.config.numpydoc_xref_aliases` # otherwise the next sphinx-build will compare the incoming values (without # our additions) to the old values (with our additions) and trigger # a full rebuild! numpydoc_xref_aliases_complete = deepcopy(config.numpydoc_xref_aliases) for key, value in DEFAULT_LINKS.items(): if key not in numpydoc_xref_aliases_complete: numpydoc_xref_aliases_complete[key] = value config.numpydoc_xref_aliases_complete = numpydoc_xref_aliases_complete # Processing to determine whether numpydoc_validation_checks is treated # as a blocklist or allowlist valid_error_codes = set(ERROR_MSGS.keys()) if "all" in config.numpydoc_validation_checks: block = deepcopy(config.numpydoc_validation_checks) config.numpydoc_validation_checks = valid_error_codes - block # Ensure that the validation check set contains only valid error codes invalid_error_codes = config.numpydoc_validation_checks - valid_error_codes if invalid_error_codes: raise ValueError( f"Unrecognized validation code(s) in numpydoc_validation_checks " f"config value: {invalid_error_codes}" ) # Generate the regexp for docstrings to ignore during validation if isinstance(config.numpydoc_validation_exclude, str): raise ValueError( f"numpydoc_validation_exclude must be a container of strings, " f"e.g. [{config.numpydoc_validation_exclude!r}]." ) config.numpydoc_validation_excluder = None if config.numpydoc_validation_exclude: exclude_expr = re.compile( r"|".join(exp for exp in config.numpydoc_validation_exclude) ) config.numpydoc_validation_excluder = exclude_expr # ------------------------------------------------------------------------------ # Docstring-mangling domains # ------------------------------------------------------------------------------ from docutils.statemachine import ViewList from sphinx.domains.c import CDomain from sphinx.domains.python import PythonDomain class ManglingDomainBase: directive_mangling_map = {} def __init__(self, *a, **kw): super().__init__(*a, **kw) self.wrap_mangling_directives() def wrap_mangling_directives(self): for name, objtype in list(self.directive_mangling_map.items()): self.directives[name] = wrap_mangling_directive( self.directives[name], objtype) class NumpyPythonDomain(ManglingDomainBase, PythonDomain): name = 'np' directive_mangling_map = { 'function': 'function', 'class': 'class', 'exception': 'class', 'method': 'function', 'classmethod': 'function', 'staticmethod': 'function', 'attribute': 'attribute', } indices = [] class NumpyCDomain(ManglingDomainBase, CDomain): name = 'np-c' directive_mangling_map = { 'function': 'function', 'member': 'attribute', 'macro': 'function', 'type': 'class', 'var': 'object', } def match_items(lines, content_old): """Create items for mangled lines. This function tries to match the lines in ``lines`` with the items (source file references and line numbers) in ``content_old``. The ``mangle_docstrings`` function changes the actual docstrings, but doesn't keep track of where each line came from. The manging does many operations on the original lines, which are hard to track afterwards. Many of the line changes come from deleting or inserting blank lines. This function tries to match lines by ignoring blank lines. All other changes (such as inserting figures or changes in the references) are completely ignored, so the generated line numbers will be off if ``mangle_docstrings`` does anything non-trivial. This is a best-effort function and the real fix would be to make ``mangle_docstrings`` actually keep track of the ``items`` together with the ``lines``. Examples -------- >>> lines = ['', 'A', '', 'B', ' ', '', 'C', 'D'] >>> lines_old = ['a', '', '', 'b', '', 'c'] >>> items_old = [('file1.py', 0), ('file1.py', 1), ('file1.py', 2), ... ('file2.py', 0), ('file2.py', 1), ('file2.py', 2)] >>> content_old = ViewList(lines_old, items=items_old) >>> match_items(lines, content_old) # doctest: +NORMALIZE_WHITESPACE [('file1.py', 0), ('file1.py', 0), ('file2.py', 0), ('file2.py', 0), ('file2.py', 2), ('file2.py', 2), ('file2.py', 2), ('file2.py', 2)] >>> # first 2 ``lines`` are matched to 'a', second 2 to 'b', rest to 'c' >>> # actual content is completely ignored. Notes ----- The algorithm tries to match any line in ``lines`` with one in ``lines_old``. It skips over all empty lines in ``lines_old`` and assigns this line number to all lines in ``lines``, unless a non-empty line is found in ``lines`` in which case it goes to the next line in ``lines_old``. """ items_new = [] lines_old = content_old.data items_old = content_old.items j = 0 for i, line in enumerate(lines): # go to next non-empty line in old: # line.strip() checks whether the string is all whitespace while j < len(lines_old) - 1 and not lines_old[j].strip(): j += 1 items_new.append(items_old[j]) if line.strip() and j < len(lines_old) - 1: j += 1 assert(len(items_new) == len(lines)) return items_new def wrap_mangling_directive(base_directive, objtype): class directive(base_directive): def run(self): env = self.state.document.settings.env name = None if self.arguments: m = re.match(r'^(.*\s+)?(.*?)(\(.*)?', self.arguments[0]) name = m.group(2).strip() if not name: name = self.arguments[0] lines = list(self.content) mangle_docstrings(env.app, objtype, name, None, None, lines) if self.content: items = match_items(lines, self.content) self.content = ViewList(lines, items=items, parent=self.content.parent) return base_directive.run(self) return directive ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1643049415.0568767 numpydoc-1.2/numpydoc/templates/0000775002342000234200000000000000000000000016371 5ustar00jarrodjarrod././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1641610654.0 numpydoc-1.2/numpydoc/templates/numpydoc_docstring.rst0000664002342000234200000000034300000000000023035 0ustar00jarrodjarrod{{index}} {{summary}} {{extended_summary}} {{parameters}} {{returns}} {{yields}} {{receives}} {{other_parameters}} {{raises}} {{warns}} {{warnings}} {{see_also}} {{notes}} {{references}} {{examples}} {{attributes}} {{methods}} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1643049415.0578768 numpydoc-1.2/numpydoc/tests/0000775002342000234200000000000000000000000015535 5ustar00jarrodjarrod././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1642724375.0 numpydoc-1.2/numpydoc/tests/test_docscrape.py0000664002342000234200000010420200000000000021110 0ustar00jarrodjarrod# -*- encoding:utf-8 -*- from collections import namedtuple from copy import deepcopy import re import textwrap import warnings import jinja2 from numpydoc.numpydoc import update_config from numpydoc.xref import DEFAULT_LINKS from numpydoc.docscrape import ( NumpyDocString, FunctionDoc, ClassDoc, ParseError ) from numpydoc.docscrape_sphinx import (SphinxDocString, SphinxClassDoc, SphinxFunctionDoc, get_doc_object) import pytest from pytest import raises as assert_raises from pytest import warns as assert_warns doc_txt = '''\ numpy.multivariate_normal(mean, cov, shape=None, spam=None) Draw values from a multivariate normal distribution with specified mean and covariance. The multivariate normal or Gaussian distribution is a generalisation of the one-dimensional normal distribution to higher dimensions. Parameters ---------- mean : (N,) ndarray Mean of the N-dimensional distribution. .. math:: (1+2+3)/3 cov : (N, N) ndarray Covariance matrix of the distribution. shape : tuple of ints Given a shape of, for example, (m,n,k), m*n*k samples are generated, and packed in an m-by-n-by-k arrangement. Because each sample is N-dimensional, the output shape is (m,n,k,N). dtype : data type object, optional (default : float) The type and size of the data to be returned. Returns ------- out : ndarray The drawn samples, arranged according to `shape`. If the shape given is (m,n,...), then the shape of `out` is (m,n,...,N). In other words, each entry ``out[i,j,...,:]`` is an N-dimensional value drawn from the distribution. list of str This is not a real return value. It exists to test anonymous return values. no_description Other Parameters ---------------- spam : parrot A parrot off its mortal coil. Raises ------ RuntimeError Some error Warns ----- RuntimeWarning Some warning Warnings -------- Certain warnings apply. Notes ----- Instead of specifying the full covariance matrix, popular approximations include: - Spherical covariance (`cov` is a multiple of the identity matrix) - Diagonal covariance (`cov` has non-negative elements only on the diagonal) This geometrical property can be seen in two dimensions by plotting generated data-points: >>> mean = [0,0] >>> cov = [[1,0],[0,100]] # diagonal covariance, points lie on x or y-axis >>> x,y = multivariate_normal(mean,cov,5000).T >>> plt.plot(x,y,'x'); plt.axis('equal'); plt.show() Note that the covariance matrix must be symmetric and non-negative definite. References ---------- .. [1] A. Papoulis, "Probability, Random Variables, and Stochastic Processes," 3rd ed., McGraw-Hill Companies, 1991 .. [2] R.O. Duda, P.E. Hart, and D.G. Stork, "Pattern Classification," 2nd ed., Wiley, 2001. See Also -------- some, other, funcs otherfunc : relationship :py:meth:`spyder.widgets.mixins.GetHelpMixin.show_object_info` Examples -------- >>> mean = (1,2) >>> cov = [[1,0],[1,0]] >>> x = multivariate_normal(mean,cov,(3,3)) >>> print(x.shape) (3, 3, 2) The following is probably true, given that 0.6 is roughly twice the standard deviation: >>> print(list((x[0, 0, :] - mean) < 0.6)) [True, True] .. index:: random :refguide: random;distributions, random;gauss ''' @pytest.fixture(params=['','\n '], ids=["flush", "newline_indented"]) def doc(request): return NumpyDocString(request.param+doc_txt) doc_yields_txt = """ Test generator Yields ------ a : int The number of apples. b : int The number of bananas. int The number of unknowns. """ doc_yields = NumpyDocString(doc_yields_txt) doc_sent_txt = """ Test generator Yields ------ a : int The number of apples. Receives -------- b : int The number of bananas. c : int The number of oranges. """ doc_sent = NumpyDocString(doc_sent_txt) def test_signature(doc): assert doc['Signature'].startswith('numpy.multivariate_normal(') assert doc['Signature'].endswith('spam=None)') def test_summary(doc): assert doc['Summary'][0].startswith('Draw values') assert doc['Summary'][-1].endswith('covariance.') def test_extended_summary(doc): assert doc['Extended Summary'][0].startswith('The multivariate normal') def test_parameters(doc): assert len(doc['Parameters']) == 4 names = [n for n, _, _ in doc['Parameters']] assert all(a == b for a, b in zip(names, ['mean', 'cov', 'shape'])) arg, arg_type, desc = doc['Parameters'][1] assert arg_type == '(N, N) ndarray' assert desc[0].startswith('Covariance matrix') assert doc['Parameters'][0][-1][-1] == ' (1+2+3)/3' arg, arg_type, desc = doc['Parameters'][2] assert arg == 'shape' assert arg_type == 'tuple of ints' assert desc[0].startswith('Given') assert doc['Parameters'][0][-1][-1] == ' (1+2+3)/3' arg, arg_type, desc = doc['Parameters'][3] assert arg == 'dtype' assert arg_type == 'data type object, optional (default : float)' assert desc[0].startswith('The type and size') def test_other_parameters(doc): assert len(doc['Other Parameters']) == 1 assert [n for n, _, _ in doc['Other Parameters']] == ['spam'] arg, arg_type, desc = doc['Other Parameters'][0] assert arg_type == 'parrot' assert desc[0].startswith('A parrot off its mortal coil') def test_returns(doc): assert len(doc['Returns']) == 3 arg, arg_type, desc = doc['Returns'][0] assert arg == 'out' assert arg_type == 'ndarray' assert desc[0].startswith('The drawn samples') assert desc[-1].endswith('distribution.') arg, arg_type, desc = doc['Returns'][1] assert arg == '' assert arg_type == 'list of str' assert desc[0].startswith('This is not a real') assert desc[-1].endswith('anonymous return values.') arg, arg_type, desc = doc['Returns'][2] assert arg == '' assert arg_type == 'no_description' assert not ''.join(desc).strip() def test_yields(): section = doc_yields['Yields'] assert len(section) == 3 truth = [('a', 'int', 'apples.'), ('b', 'int', 'bananas.'), ('', 'int', 'unknowns.')] for (arg, arg_type, desc), (arg_, arg_type_, end) in zip(section, truth): assert arg == arg_ assert arg_type == arg_type_ assert desc[0].startswith('The number of') assert desc[0].endswith(end) def test_sent(): section = doc_sent['Receives'] assert len(section) == 2 truth = [('b', 'int', 'bananas.'), ('c', 'int', 'oranges.')] for (arg, arg_type, desc), (arg_, arg_type_, end) in zip(section, truth): assert arg == arg_ assert arg_type == arg_type_ assert desc[0].startswith('The number of') assert desc[0].endswith(end) def test_returnyield(): doc_text = """ Test having returns and yields. Returns ------- int The number of apples. Yields ------ a : int The number of apples. b : int The number of bananas. """ assert_raises(ValueError, NumpyDocString, doc_text) def test_section_twice(): doc_text = """ Test having a section Notes twice Notes ----- See the next note for more information Notes ----- That should break... """ with pytest.raises(ValueError, match="The section Notes appears twice"): NumpyDocString(doc_text) # if we have a numpydoc object, we know where the error came from class Dummy: """ Dummy class. Notes ----- First note. Notes ----- Second note. """ def spam(self, a, b): """Spam\n\nSpam spam.""" pass def ham(self, c, d): """Cheese\n\nNo cheese.""" pass def dummy_func(arg): """ Dummy function. Notes ----- First note. Notes ----- Second note. """ with pytest.raises(ValueError, match="Dummy class"): SphinxClassDoc(Dummy) with pytest.raises(ValueError, match="dummy_func"): SphinxFunctionDoc(dummy_func) def test_notes(doc): assert doc['Notes'][0].startswith('Instead') assert doc['Notes'][-1].endswith('definite.') assert len(doc['Notes']) == 17 def test_references(doc): assert doc['References'][0].startswith('..') assert doc['References'][-1].endswith('2001.') def test_examples(doc): assert doc['Examples'][0].startswith('>>>') assert doc['Examples'][-1].endswith('True]') def test_index(doc): assert doc['index']['default'] == 'random' assert len(doc['index']) == 2 assert len(doc['index']['refguide']) == 2 def _strip_blank_lines(s): "Remove leading, trailing and multiple blank lines" s = re.sub(r'^\s*\n', '', s) s = re.sub(r'\n\s*$', '', s) s = re.sub(r'\n\s*\n', r'\n\n', s) return s def line_by_line_compare(a, b, n_lines=None): a = textwrap.dedent(a) b = textwrap.dedent(b) a = [l.rstrip() for l in _strip_blank_lines(a).split('\n')][:n_lines] b = [l.rstrip() for l in _strip_blank_lines(b).split('\n')][:n_lines] assert len(a) == len(b) for ii, (aa, bb) in enumerate(zip(a, b)): assert aa == bb def test_str(doc): # doc_txt has the order of Notes and See Also sections flipped. # This should be handled automatically, and so, one thing this test does # is to make sure that See Also precedes Notes in the output. line_by_line_compare(str(doc), """numpy.multivariate_normal(mean, cov, shape=None, spam=None) Draw values from a multivariate normal distribution with specified mean and covariance. The multivariate normal or Gaussian distribution is a generalisation of the one-dimensional normal distribution to higher dimensions. Parameters ---------- mean : (N,) ndarray Mean of the N-dimensional distribution. .. math:: (1+2+3)/3 cov : (N, N) ndarray Covariance matrix of the distribution. shape : tuple of ints Given a shape of, for example, (m,n,k), m*n*k samples are generated, and packed in an m-by-n-by-k arrangement. Because each sample is N-dimensional, the output shape is (m,n,k,N). dtype : data type object, optional (default : float) The type and size of the data to be returned. Returns ------- out : ndarray The drawn samples, arranged according to `shape`. If the shape given is (m,n,...), then the shape of `out` is (m,n,...,N). In other words, each entry ``out[i,j,...,:]`` is an N-dimensional value drawn from the distribution. list of str This is not a real return value. It exists to test anonymous return values. no_description Other Parameters ---------------- spam : parrot A parrot off its mortal coil. Raises ------ RuntimeError Some error Warns ----- RuntimeWarning Some warning Warnings -------- Certain warnings apply. See Also -------- `some`_, `other`_, `funcs`_ .. `otherfunc`_ relationship :py:meth:`spyder.widgets.mixins.GetHelpMixin.show_object_info` .. Notes ----- Instead of specifying the full covariance matrix, popular approximations include: - Spherical covariance (`cov` is a multiple of the identity matrix) - Diagonal covariance (`cov` has non-negative elements only on the diagonal) This geometrical property can be seen in two dimensions by plotting generated data-points: >>> mean = [0,0] >>> cov = [[1,0],[0,100]] # diagonal covariance, points lie on x or y-axis >>> x,y = multivariate_normal(mean,cov,5000).T >>> plt.plot(x,y,'x'); plt.axis('equal'); plt.show() Note that the covariance matrix must be symmetric and non-negative definite. References ---------- .. [1] A. Papoulis, "Probability, Random Variables, and Stochastic Processes," 3rd ed., McGraw-Hill Companies, 1991 .. [2] R.O. Duda, P.E. Hart, and D.G. Stork, "Pattern Classification," 2nd ed., Wiley, 2001. Examples -------- >>> mean = (1,2) >>> cov = [[1,0],[1,0]] >>> x = multivariate_normal(mean,cov,(3,3)) >>> print(x.shape) (3, 3, 2) The following is probably true, given that 0.6 is roughly twice the standard deviation: >>> print(list((x[0, 0, :] - mean) < 0.6)) [True, True] .. index:: random :refguide: random;distributions, random;gauss""") def test_yield_str(): line_by_line_compare(str(doc_yields), """Test generator Yields ------ a : int The number of apples. b : int The number of bananas. int The number of unknowns. """) def test_receives_str(): line_by_line_compare(str(doc_sent), """Test generator Yields ------ a : int The number of apples. Receives -------- b : int The number of bananas. c : int The number of oranges. """) def test_no_index_in_str(): assert "index" not in str(NumpyDocString("""Test idx """)) assert "index" in str(NumpyDocString("""Test idx .. index :: random """)) assert "index" in str(NumpyDocString("""Test idx .. index :: foo """)) def test_sphinx_str(): sphinx_doc = SphinxDocString(doc_txt) line_by_line_compare(str(sphinx_doc), """ .. index:: random single: random;distributions, random;gauss Draw values from a multivariate normal distribution with specified mean and covariance. The multivariate normal or Gaussian distribution is a generalisation of the one-dimensional normal distribution to higher dimensions. :Parameters: **mean** : (N,) ndarray Mean of the N-dimensional distribution. .. math:: (1+2+3)/3 **cov** : (N, N) ndarray Covariance matrix of the distribution. **shape** : tuple of ints Given a shape of, for example, (m,n,k), m*n*k samples are generated, and packed in an m-by-n-by-k arrangement. Because each sample is N-dimensional, the output shape is (m,n,k,N). **dtype** : data type object, optional (default : float) The type and size of the data to be returned. :Returns: **out** : ndarray The drawn samples, arranged according to `shape`. If the shape given is (m,n,...), then the shape of `out` is (m,n,...,N). In other words, each entry ``out[i,j,...,:]`` is an N-dimensional value drawn from the distribution. list of str This is not a real return value. It exists to test anonymous return values. no_description .. :Other Parameters: **spam** : parrot A parrot off its mortal coil. :Raises: RuntimeError Some error :Warns: RuntimeWarning Some warning .. warning:: Certain warnings apply. .. seealso:: :obj:`some`, :obj:`other`, :obj:`funcs` .. :obj:`otherfunc` relationship :py:meth:`spyder.widgets.mixins.GetHelpMixin.show_object_info` .. .. rubric:: Notes Instead of specifying the full covariance matrix, popular approximations include: - Spherical covariance (`cov` is a multiple of the identity matrix) - Diagonal covariance (`cov` has non-negative elements only on the diagonal) This geometrical property can be seen in two dimensions by plotting generated data-points: >>> mean = [0,0] >>> cov = [[1,0],[0,100]] # diagonal covariance, points lie on x or y-axis >>> x,y = multivariate_normal(mean,cov,5000).T >>> plt.plot(x,y,'x'); plt.axis('equal'); plt.show() Note that the covariance matrix must be symmetric and non-negative definite. .. rubric:: References .. [1] A. Papoulis, "Probability, Random Variables, and Stochastic Processes," 3rd ed., McGraw-Hill Companies, 1991 .. [2] R.O. Duda, P.E. Hart, and D.G. Stork, "Pattern Classification," 2nd ed., Wiley, 2001. .. only:: latex [1]_, [2]_ .. rubric:: Examples >>> mean = (1,2) >>> cov = [[1,0],[1,0]] >>> x = multivariate_normal(mean,cov,(3,3)) >>> print(x.shape) (3, 3, 2) The following is probably true, given that 0.6 is roughly twice the standard deviation: >>> print(list((x[0, 0, :] - mean) < 0.6)) [True, True] """) def test_sphinx_yields_str(): sphinx_doc = SphinxDocString(doc_yields_txt) line_by_line_compare(str(sphinx_doc), """Test generator :Yields: **a** : int The number of apples. **b** : int The number of bananas. int The number of unknowns. """) doc2 = NumpyDocString(""" Returns array of indices of the maximum values of along the given axis. Parameters ---------- a : {array_like} Array to look in. axis : {None, integer} If None, the index is into the flattened array, otherwise along the specified axis""") def test_parameters_without_extended_description(): assert len(doc2['Parameters']) == 2 doc3 = NumpyDocString(""" my_signature(*params, **kwds) Return this and that. """) def test_escape_stars(): signature = str(doc3).split('\n')[0] assert signature == r'my_signature(\*params, \*\*kwds)' def my_func(a, b, **kwargs): pass fdoc = FunctionDoc(func=my_func) assert fdoc['Signature'] == '' doc4 = NumpyDocString( """a.conj() Return an array with all complex-valued elements conjugated.""") def test_empty_extended_summary(): assert doc4['Extended Summary'] == [] doc5 = NumpyDocString( """ a.something() Raises ------ LinAlgException If array is singular. Warns ----- SomeWarning If needed """) def test_raises(): assert len(doc5['Raises']) == 1 param = doc5['Raises'][0] assert param.name == '' assert param.type == 'LinAlgException' assert param.desc == ['If array is singular.'] def test_warns(): assert len(doc5['Warns']) == 1 param = doc5['Warns'][0] assert param.name == '' assert param.type == 'SomeWarning' assert param.desc == ['If needed'] # see numpydoc/numpydoc #281 # we want to correctly parse "See Also" both in docstrings both like #"""foo # and #""" #foo @pytest.mark.parametrize('prefix', ['', '\n ']) def test_see_also(prefix): doc6 = NumpyDocString( prefix + """z(x,theta) See Also -------- func_a, func_b, func_c func_d : some equivalent func foo.func_e : some other func over multiple lines func_f, func_g, :meth:`func_h`, func_j, func_k func_f1, func_g1, :meth:`func_h1`, func_j1 func_f2, func_g2, :meth:`func_h2`, func_j2 : description of multiple :obj:`baz.obj_q` :obj:`~baz.obj_r` :class:`class_j`: fubar foobar """) assert len(doc6['See Also']) == 10 for funcs, desc in doc6['See Also']: for func, role in funcs: if func in ('func_a', 'func_b', 'func_c', 'func_f', 'func_g', 'func_h', 'func_j', 'func_k', 'baz.obj_q', 'func_f1', 'func_g1', 'func_h1', 'func_j1', '~baz.obj_r'): assert not desc, str([func, desc]) elif func in ('func_f2', 'func_g2', 'func_h2', 'func_j2'): assert desc, str([func, desc]) else: assert desc, str([func, desc]) if func == 'func_h': assert role == 'meth' elif func == 'baz.obj_q' or func == '~baz.obj_r': assert role == 'obj' elif func == 'class_j': assert role == 'class' elif func in ['func_h1', 'func_h2']: assert role == 'meth' else: assert role is None, str([func, role]) if func == 'func_d': assert desc == ['some equivalent func'] elif func == 'foo.func_e': assert desc == ['some other func over', 'multiple lines'] elif func == 'class_j': assert desc == ['fubar', 'foobar'] elif func in ['func_f2', 'func_g2', 'func_h2', 'func_j2']: assert desc == ['description of multiple'], str([desc, ['description of multiple']]) def test_see_also_parse_error(): text = ( """ z(x,theta) See Also -------- :func:`~foo` """) with pytest.raises(ValueError, match="See Also entry ':func:`~foo`'"): NumpyDocString(text) def test_see_also_print(): class Dummy: """ See Also -------- func_a, func_b func_c : some relationship goes here func_d """ pass s = str(FunctionDoc(Dummy, role='func')) assert(':func:`func_a`, :func:`func_b`' in s) assert(' some relationship' in s) assert(':func:`func_d`' in s) def test_see_also_trailing_comma_warning(): warnings.filterwarnings('error') with assert_warns(Warning, match='Unexpected comma or period after function list at index 43 of line .*'): NumpyDocString( """ z(x,theta) See Also -------- func_f2, func_g2, :meth:`func_h2`, func_j2, : description of multiple :class:`class_j`: fubar foobar """) def test_unknown_section(): doc_text = """ Test having an unknown section Mope ---- This should be ignored and warned about """ class BadSection: """Class with bad section. Nope ---- This class has a nope section. """ pass with pytest.warns(UserWarning, match="Unknown section Mope") as record: NumpyDocString(doc_text) assert len(record) == 1 # SphinxClassDoc has _obj.__name__ == "BadSection". Test that this is # included in the message msg_match = "Unknown section Nope in the docstring of BadSection" with pytest.warns(UserWarning, match=msg_match) as record: SphinxClassDoc(BadSection) assert len(record) == 1 doc7 = NumpyDocString(""" Doc starts on second line. """) def test_empty_first_line(): assert doc7['Summary'][0].startswith('Doc starts') doc8 = NumpyDocString(""" Parameters with colon and no types: Parameters ---------- data : some stuff, technically invalid """) def test_trailing_colon(): assert doc8['Parameters'][0].name == 'data' def test_no_summary(): str(SphinxDocString(""" Parameters ----------""")) def test_unicode(): doc = SphinxDocString(""" öäöäöäöäöåååå öäöäöäööäååå Parameters ---------- ååå : äää ööö Returns ------- ååå : ööö äää """) assert isinstance(doc['Summary'][0], str) assert doc['Summary'][0] == 'öäöäöäöäöåååå' def test_plot_examples(): cfg = dict(use_plots=True) doc = SphinxDocString(""" Examples -------- >>> import matplotlib.pyplot as plt >>> plt.plot([1,2,3],[4,5,6]) >>> plt.show() """, config=cfg) assert 'plot::' in str(doc), str(doc) doc = SphinxDocString(""" Examples -------- >>> from matplotlib import pyplot as plt >>> plt.plot([1,2,3],[4,5,6]) >>> plt.show() """, config=cfg) assert 'plot::' in str(doc), str(doc) doc = SphinxDocString(""" Examples -------- .. plot:: import matplotlib.pyplot as plt plt.plot([1,2,3],[4,5,6]) plt.show() """, config=cfg) assert str(doc).count('plot::') == 1, str(doc) def test_use_blockquotes(): cfg = dict(use_blockquotes=True) doc = SphinxDocString(""" Parameters ---------- abc : def ghi jkl mno Returns ------- ABC : DEF GHI JKL MNO """, config=cfg) line_by_line_compare(str(doc), ''' :Parameters: **abc** : def ghi **jkl** mno :Returns: **ABC** : DEF GHI JKL MNO ''') def test_class_members(): class Dummy: """ Dummy class. """ def spam(self, a, b): """Spam\n\nSpam spam.""" pass def ham(self, c, d): """Cheese\n\nNo cheese.""" pass @property def spammity(self): """Spammity index""" return 0.95 class Ignorable: """local class, to be ignored""" pass for cls in (ClassDoc, SphinxClassDoc): doc = cls(Dummy, config=dict(show_class_members=False)) assert 'Methods' not in str(doc), (cls, str(doc)) assert 'spam' not in str(doc), (cls, str(doc)) assert 'ham' not in str(doc), (cls, str(doc)) assert 'spammity' not in str(doc), (cls, str(doc)) assert 'Spammity index' not in str(doc), (cls, str(doc)) doc = cls(Dummy, config=dict(show_class_members=True)) assert 'Methods' in str(doc), (cls, str(doc)) assert 'spam' in str(doc), (cls, str(doc)) assert 'ham' in str(doc), (cls, str(doc)) assert 'spammity' in str(doc), (cls, str(doc)) if cls is SphinxClassDoc: assert '.. autosummary::' in str(doc), str(doc) else: assert 'Spammity index' in str(doc), str(doc) class SubDummy(Dummy): """ Subclass of Dummy class. """ def ham(self, c, d): """Cheese\n\nNo cheese.\nOverloaded Dummy.ham""" pass def bar(self, a, b): """Bar\n\nNo bar""" pass for cls in (ClassDoc, SphinxClassDoc): doc = cls(SubDummy, config=dict(show_class_members=True, show_inherited_class_members=False)) assert 'Methods' in str(doc), (cls, str(doc)) assert 'spam' not in str(doc), (cls, str(doc)) assert 'ham' in str(doc), (cls, str(doc)) assert 'bar' in str(doc), (cls, str(doc)) assert 'spammity' not in str(doc), (cls, str(doc)) if cls is SphinxClassDoc: assert '.. autosummary::' in str(doc), str(doc) else: assert 'Spammity index' not in str(doc), str(doc) doc = cls(SubDummy, config=dict(show_class_members=True, show_inherited_class_members=True)) assert 'Methods' in str(doc), (cls, str(doc)) assert 'spam' in str(doc), (cls, str(doc)) assert 'ham' in str(doc), (cls, str(doc)) assert 'bar' in str(doc), (cls, str(doc)) assert 'spammity' in str(doc), (cls, str(doc)) if cls is SphinxClassDoc: assert '.. autosummary::' in str(doc), str(doc) else: assert 'Spammity index' in str(doc), str(doc) def test_duplicate_signature(): # Duplicate function signatures occur e.g. in ufuncs, when the # automatic mechanism adds one, and a more detailed comes from the # docstring itself. doc = NumpyDocString( """ z(x1, x2) z(a, theta) """) assert doc['Signature'].strip() == 'z(a, theta)' class_doc_txt = """ Foo Parameters ---------- f : callable ``f(t, y, *f_args)`` Aaa. jac : callable ``jac(t, y, *jac_args)`` Bbb. 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 Examples -------- For usage examples, see `ode`. """ def test_class_members_doc(): doc = ClassDoc(None, class_doc_txt) 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 """) def test_class_members_doc_sphinx(): class Foo: @property def an_attribute(self): """Test attribute""" return None @property def no_docstring(self): return None @property def no_docstring2(self): return None @property def multiline_sentence(self): """This is a sentence. It spans multiple lines.""" return None @property def midword_period(self): """The sentence for numpy.org.""" return None @property def no_period(self): """This does not have a period so we truncate its summary to the first linebreak Apparently. """ return None doc = SphinxClassDoc(Foo, class_doc_txt) line_by_line_compare(str(doc), """ Foo :Parameters: **f** : callable ``f(t, y, *f_args)`` Aaa. **jac** : callable ``jac(t, y, *jac_args)`` Bbb. .. rubric:: Examples For usage examples, see `ode`. :Attributes: **t** : float Current time. **y** : ndarray Current variable values. * hello * world :obj:`an_attribute ` : float Test attribute **no_docstring** : str But a description **no_docstring2** : str .. :obj:`multiline_sentence ` This is a sentence. :obj:`midword_period ` The sentence for numpy.org. :obj:`no_period ` This does not have a period .. rubric:: Methods ===== ========== **a** **b** **c** ===== ========== """) def test_class_attributes_as_member_list(): class Foo: """ Class docstring. Attributes ---------- an_attribute Another description that is not used. """ @property def an_attribute(self): """Test attribute""" return None attr_doc = """:Attributes: :obj:`an_attribute ` Test attribute""" assert attr_doc in str(SphinxClassDoc(Foo)) assert "Another description" not in str(SphinxClassDoc(Foo)) attr_doc2 = """.. rubric:: Attributes .. autosummary:: :toctree: an_attribute""" cfg = dict(attributes_as_param_list=False) assert attr_doc2 in str(SphinxClassDoc(Foo, config=cfg)) assert "Another description" not in str(SphinxClassDoc(Foo, config=cfg)) def test_templated_sections(): doc = SphinxClassDoc(None, class_doc_txt, config={'template': jinja2.Template('{{examples}}\n{{parameters}}')}) line_by_line_compare(str(doc), """ .. rubric:: Examples For usage examples, see `ode`. :Parameters: **f** : callable ``f(t, y, *f_args)`` Aaa. **jac** : callable ``jac(t, y, *jac_args)`` Bbb. """) def test_nonstandard_property(): # test discovery of a property that does not satisfy isinstace(.., property) class SpecialProperty: def __init__(self, axis=0, doc=""): self.axis = axis self.__doc__ = doc def __get__(self, obj, type): if obj is None: # Only instances have actual _data, not classes return self else: return obj._data.axes[self.axis] def __set__(self, obj, value): obj._set_axis(self.axis, value) class Dummy: attr = SpecialProperty(doc="test attribute") doc = get_doc_object(Dummy) assert "test attribute" in str(doc) def test_args_and_kwargs(): cfg = dict() doc = SphinxDocString(""" Parameters ---------- param1 : int First parameter *args : tuple Arguments **kwargs : dict Keyword arguments """, config=cfg) line_by_line_compare(str(doc), r""" :Parameters: **param1** : int First parameter **\*args** : tuple Arguments **\*\*kwargs** : dict Keyword arguments """) def test_autoclass(): cfg=dict(show_class_members=True, show_inherited_class_members=True) doc = SphinxClassDoc(str, ''' A top section before .. autoclass:: str ''', config=cfg) line_by_line_compare(str(doc), r''' A top section before .. autoclass:: str .. rubric:: Methods ''', 5) xref_doc_txt = """ Test xref in Parameters, Other Parameters and Returns Parameters ---------- p1 : int Integer value p2 : float, optional Integer value Other Parameters ---------------- p3 : list[int] List of integers p4 : :class:`pandas.DataFrame` A dataframe p5 : sequence of `int` A sequence Returns ------- out : array Numerical return value """ xref_doc_txt_expected = r""" Test xref in Parameters, Other Parameters and Returns :Parameters: **p1** : :class:`python:int` Integer value **p2** : :class:`python:float`, optional Integer value :Returns: **out** : :obj:`array ` Numerical return value :Other Parameters: **p3** : :class:`python:list`\[:class:`python:int`] List of integers **p4** : :class:`pandas.DataFrame` A dataframe **p5** : :obj:`python:sequence` of `int` A sequence """ def test_xref(): xref_aliases = { 'sequence': ':obj:`python:sequence`', } class Config(): def __init__(self, a, b): self.numpydoc_xref_aliases = a self.numpydoc_xref_aliases_complete = b # numpydoc.update_config fails if this config option not present self.numpydoc_validation_checks = set() self.numpydoc_validation_exclude = set() xref_aliases_complete = deepcopy(DEFAULT_LINKS) for key in xref_aliases: xref_aliases_complete[key] = xref_aliases[key] config = Config(xref_aliases, xref_aliases_complete) app = namedtuple('config', 'config')(config) update_config(app) xref_ignore = {'of', 'default', 'optional'} doc = SphinxDocString( xref_doc_txt, config=dict( xref_param_type=True, xref_aliases=xref_aliases_complete, xref_ignore=xref_ignore ) ) line_by_line_compare(str(doc), xref_doc_txt_expected) if __name__ == "__main__": import pytest pytest.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1642724375.0 numpydoc-1.2/numpydoc/tests/test_full.py0000664002342000234200000000661400000000000020117 0ustar00jarrodjarrodimport os.path as op import re import shutil import pytest import sphinx from sphinx.application import Sphinx from sphinx.util.docutils import docutils_namespace # Test framework adapted from sphinx-gallery (BSD 3-clause) @pytest.fixture(scope='module') def sphinx_app(tmpdir_factory): temp_dir = (tmpdir_factory.getbasetemp() / 'root').strpath src_dir = op.join(op.dirname(__file__), 'tinybuild') def ignore(src, names): return ('_build', 'generated') shutil.copytree(src_dir, temp_dir, ignore=ignore) # For testing iteration, you can get similar behavior just doing `make` # inside the tinybuild directory src_dir = temp_dir conf_dir = temp_dir out_dir = op.join(temp_dir, '_build', 'html') toctrees_dir = op.join(temp_dir, '_build', 'toctrees') kwargs = {'warningiserror': True, 'keep_going': True} # Avoid warnings about re-registration, see: # https://github.com/sphinx-doc/sphinx/issues/5038 with docutils_namespace(): app = Sphinx(src_dir, conf_dir, out_dir, toctrees_dir, buildername='html', **kwargs) # need to build within the context manager # for automodule and backrefs to work app.build(False, []) return app def test_MyClass(sphinx_app): """Test that class documentation is reasonable.""" src_dir, out_dir = sphinx_app.srcdir, sphinx_app.outdir class_rst = op.join(src_dir, 'generated', 'numpydoc_test_module.MyClass.rst') with open(class_rst, 'r') as fid: rst = fid.read() assert r'numpydoc\_test\_module' in rst # properly escaped class_html = op.join(out_dir, 'generated', 'numpydoc_test_module.MyClass.html') with open(class_html, 'r') as fid: html = fid.read() # ensure that no autodoc weirdness ($) occurs assert '$self' not in html assert '/,' not in html assert '__init__' in html # inherited # escaped * chars should no longer be preceded by \'s, # if we see a \* in the output we know it's incorrect: assert r'\*' not in html # "self" should not be in the parameter list for the class: assert 'self,' not in html # check xref was embedded properly (dict should link using xref): assert 'stdtypes.html#dict' in html def test_my_function(sphinx_app): """Test that function documentation is reasonable.""" out_dir = sphinx_app.outdir function_html = op.join(out_dir, 'generated', 'numpydoc_test_module.my_function.html') with open(function_html, 'r') as fid: html = fid.read() assert r'\*args' not in html assert '*args' in html # check xref (iterable should link using xref): assert 'glossary.html#term-iterable' in html @pytest.mark.parametrize(("html_file", "expected_length"), ( (["index.html"], 1), (["generated", "numpydoc_test_module.my_function.html"], 1), (["generated", "numpydoc_test_module.MyClass.html"], 1), )) def test_reference(sphinx_app, html_file, expected_length): """Test for bad references""" out_dir = sphinx_app.outdir with open(op.join(out_dir, *html_file), 'r') as fid: html = fid.read() reference_list = re.findall(r'(.*)<\/a>', html) assert len(reference_list) == expected_length for ref in reference_list: assert '-' not in ref # Bad reference if it contains "-" e.g. R1896e33633d5-1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1642724370.0 numpydoc-1.2/numpydoc/tests/test_main.py0000664002342000234200000000553100000000000020076 0ustar00jarrodjarrodimport sys import io import pytest import numpydoc import numpydoc.__main__ def _capture_stdout(func_name, *args, **kwargs): """ Return stdout of calling `func_name`. This docstring should be perfect, as it is used to test the validation with a docstring without errors. Parameters ---------- func_name : callable Function to be called. *args, **kwargs Will be passed to `func_name`. Returns ------- str The content that the function printed. See Also -------- sys.stdout : Python's file handler for stdout. Examples -------- >>> _capture_stdout(print, 'hello world') 'hello world' """ f = io.StringIO() sys.stdout, old_stdout = f, sys.stdout try: func_name(*args, **kwargs) return f.getvalue().strip('\n\r') finally: sys.stdout = old_stdout def _docstring_with_errors(): """ this docstring should report some errors Parameters ---------- made_up_param : str """ pass def _invalid_docstring(): """ This docstring should break the parsing. See Also -------- : this is invalid """ pass def test_renders_package_docstring(): out = _capture_stdout(numpydoc.__main__.render_object, 'numpydoc') assert out.startswith('This package provides the numpydoc Sphinx') def test_renders_module_docstring(): out = _capture_stdout(numpydoc.__main__.render_object, 'numpydoc.__main__') assert out.startswith('Implementing `python -m numpydoc` functionality.') def test_renders_function_docstring(): out = _capture_stdout(numpydoc.__main__.render_object, 'numpydoc.tests.test_main._capture_stdout') assert out.startswith('Return stdout of calling') def test_render_object_returns_correct_exit_status(): exit_status = numpydoc.__main__.render_object( 'numpydoc.tests.test_main._capture_stdout') assert exit_status == 0 with pytest.raises(ValueError): numpydoc.__main__.render_object( 'numpydoc.tests.test_main._invalid_docstring') def test_validate_detects_errors(): out = _capture_stdout(numpydoc.__main__.validate_object, 'numpydoc.tests.test_main._docstring_with_errors') assert 'SS02' in out assert 'Summary does not start with a capital letter' in out exit_status = numpydoc.__main__.validate_object( 'numpydoc.tests.test_main._docstring_with_errors') assert exit_status > 0 def test_validate_perfect_docstring(): out = _capture_stdout(numpydoc.__main__.validate_object, 'numpydoc.tests.test_main._capture_stdout') assert out == '' exit_status = numpydoc.__main__.validate_object( 'numpydoc.tests.test_main._capture_stdout') assert exit_status == 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1642724375.0 numpydoc-1.2/numpydoc/tests/test_numpydoc.py0000664002342000234200000001464700000000000021020 0ustar00jarrodjarrod# -*- encoding:utf-8 -*- import pytest from io import StringIO from copy import deepcopy from numpydoc.numpydoc import ( mangle_docstrings, _clean_text_signature, update_config ) from numpydoc.xref import DEFAULT_LINKS from sphinx.ext.autodoc import ALL from sphinx.util import logging class MockConfig(): numpydoc_use_plots = False numpydoc_use_blockquotes = True numpydoc_show_class_members = True numpydoc_show_inherited_class_members = True numpydoc_class_members_toctree = True numpydoc_xref_param_type = False numpydoc_xref_aliases = {} numpydoc_xref_aliases_complete = deepcopy(DEFAULT_LINKS) numpydoc_xref_ignore = set() templates_path = [] numpydoc_edit_link = False numpydoc_citation_re = '[a-z0-9_.-]+' numpydoc_attributes_as_param_list = True numpydoc_validation_checks = set() numpydoc_validation_exclude = set() class MockBuilder(): config = MockConfig() class MockApp(): config = MockConfig() builder = MockBuilder() translator = None def __init__(self): self.builder.app = self # Attrs required for logging self.verbosity = 2 self._warncount = 0 self.warningiserror = False def test_mangle_docstrings(): s = ''' A top section before .. autoclass:: str ''' lines = s.split('\n') mangle_docstrings(MockApp(), 'class', 'str', str, {}, lines) assert 'rpartition' in [x.strip() for x in lines] lines = s.split('\n') mangle_docstrings( MockApp(), 'class', 'str', str, {'members': ['upper']}, lines) assert 'rpartition' not in [x.strip() for x in lines] assert 'upper' in [x.strip() for x in lines] lines = s.split('\n') mangle_docstrings( MockApp(), 'class', 'str', str, {'exclude-members': ALL}, lines) assert 'rpartition' not in [x.strip() for x in lines] assert 'upper' not in [x.strip() for x in lines] lines = s.split('\n') mangle_docstrings( MockApp(), 'class', 'str', str, {'exclude-members': ['upper']}, lines) assert 'rpartition' in [x.strip() for x in lines] assert 'upper' not in [x.strip() for x in lines] def test_clean_text_signature(): assert _clean_text_signature(None) is None assert _clean_text_signature('func($self)') == 'func()' assert (_clean_text_signature('func($self, *args, **kwargs)') == 'func(*args, **kwargs)') assert _clean_text_signature('($self)') == '()' assert _clean_text_signature('()') == '()' assert _clean_text_signature('func()') == 'func()' assert (_clean_text_signature('func($self, /, *args, **kwargs)') == 'func(*args, **kwargs)') assert (_clean_text_signature('func($self, other, /, *args, **kwargs)') == 'func(other, *args, **kwargs)') assert _clean_text_signature('($module)') == '()' assert _clean_text_signature('func($type)') == 'func()' assert (_clean_text_signature('func($self, foo="hello world")') == 'func(foo="hello world")') assert (_clean_text_signature("func($self, foo='hello world')") == "func(foo='hello world')") assert (_clean_text_signature('func(foo="hello world")') == 'func(foo="hello world")') assert (_clean_text_signature('func(foo="$self")') == 'func(foo="$self")') assert (_clean_text_signature('func($self, foo="$self")') == 'func(foo="$self")') assert _clean_text_signature('func(self, other)') == 'func(self, other)' assert _clean_text_signature('func($self, *args)') == 'func(*args)' @pytest.fixture def f(): def _function_without_seealso_and_examples(): """ A function whose docstring has no examples or see also section. Expect SA01 and EX01 errors if validation enabled. """ pass return _function_without_seealso_and_examples @pytest.mark.parametrize( ( 'numpydoc_validation_checks', 'expected_warn', 'non_warnings', ), ( # Validation configured off - expect no warnings (set(), [], []), # Validation on with expected warnings (set(['SA01', 'EX01']), ('SA01', 'EX01'), []), # Validation on with only one activated check (set(['SA01']), ('SA01',), ('EX01',)), ), ) def test_mangle_docstring_validation_warnings( f, numpydoc_validation_checks, expected_warn, non_warnings, ): app = MockApp() # Set up config for test app.config.numpydoc_validation_checks = numpydoc_validation_checks # Update configuration update_config(app) # Set up logging status, warning = StringIO(), StringIO() logging.setup(app, status, warning) # Run mangle docstrings with the above configuration mangle_docstrings(app, 'function', 'f', f, None, f.__doc__.split('\n')) # Assert that all (and only) expected warnings are logged warnings = warning.getvalue() for w in expected_warn: assert w in warnings for w in non_warnings: assert w not in warnings def test_mangle_docstring_validation_exclude(): def function_with_bad_docstring(): """ This docstring will raise docstring validation warnings.""" app = MockApp() app.config.numpydoc_validation_checks = {"all"} app.config.numpydoc_validation_exclude = [r"_bad_"] # Call update_config to construct regexp from config value update_config(app) # Setup for catching warnings status, warning = StringIO(), StringIO() logging.setup(app, status, warning) # Run mangle docstrings on function_with_bad_docstring mangle_docstrings( app, 'function', function_with_bad_docstring.__name__, function_with_bad_docstring, None, function_with_bad_docstring.__doc__.split('\n'), ) # Validation is skipped due to exclude pattern matching fn name, therefore # no warnings expected assert warning.getvalue() == "" def test_update_config_invalid_validation_set(): app = MockApp() # Results in {'a', 'l'} instead of {"all"} app.config.numpydoc_validation_checks = set("all") with pytest.raises(ValueError, match="Unrecognized validation code"): update_config(app) def test_update_config_exclude_str(): app = MockApp() app.config.numpydoc_validation_checks = set() app.config.numpydoc_validation_exclude = "shouldnt-be-a-str" with pytest.raises(ValueError, match="\['shouldnt-be-a-str'\]"): update_config(app) if __name__ == "__main__": import pytest pytest.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1642724349.0 numpydoc-1.2/numpydoc/tests/test_validate.py0000664002342000234200000010500500000000000020740 0ustar00jarrodjarrodimport pytest import numpydoc.validate import numpydoc.tests validate_one = numpydoc.validate.validate class GoodDocStrings: """ Collection of good doc strings. This class contains a lot of docstrings that should pass the validation script without any errors. See Also -------- AnotherClass : With its description. Examples -------- >>> result = 1 + 1 """ def one_liner(self): """Allow one liner docstrings (including quotes).""" # This should fail, but not because of the position of the quotes pass def plot(self, kind, color="blue", **kwargs): """ Generate a plot. Render the data in the Series as a matplotlib plot of the specified kind. Parameters ---------- kind : str Kind of matplotlib plot, e.g.:: 'foo' color : str, default 'blue' Color name or rgb code. **kwargs These parameters will be passed to the matplotlib plotting function. See Also -------- related : Something related. Examples -------- >>> result = 1 + 1 """ pass def swap(self, arr, i, j, *args, **kwargs): """ Swap two indices on an array. The extended summary can be multiple paragraphs, but just one is enough to pass the validation. Parameters ---------- arr : list The list having indexes swapped. i, j : int The indexes being swapped. *args, **kwargs Extraneous parameters are being permitted. See Also -------- related : Something related. Examples -------- >>> result = 1 + 1 """ pass def sample(self): """ Generate and return a random number. The value is sampled from a continuous uniform distribution between 0 and 1. Returns ------- float Random number generated. - Make sure you set a seed for reproducibility See Also -------- related : Something related. Examples -------- >>> result = 1 + 1 """ pass def random_letters(self): """ Generate and return a sequence of random letters. The length of the returned string is also random, and is also returned. Returns ------- length : int Length of the returned string. letters : str String of random letters. .. versionadded:: 0.1 See Also -------- related : Something related. Examples -------- >>> result = 1 + 1 """ pass def sample_values(self): """ Generate an infinite sequence of random numbers. The values are sampled from a continuous uniform distribution between 0 and 1. Yields ------ float Random number generated. See Also -------- related : Something related. Examples -------- >>> result = 1 + 1 """ pass def head(self): """ Return the first 5 elements of the Series. This function is mainly useful to preview the values of the Series without displaying the whole of it. Returns ------- int Subset of the original series with the 5 first values. See Also -------- Series.tail : Return the last 5 elements of the Series. Series.iloc : Return a slice of the elements in the Series, which can also be used to return the first or last n. Examples -------- >>> 1 + 1 2 """ return 1 def head1(self, n=5): """ Return the first elements of the Series. This function is mainly useful to preview the values of the Series without displaying the whole of it. Parameters ---------- n : int Number of values to return. Returns ------- int Subset of the original series with the n first values. See Also -------- tail : Return the last n elements of the Series. Examples -------- >>> s = 10 >>> s 10 With the `n` parameter, we can change the number of returned rows: >>> s + 1 11 """ return 1 def summary_starts_with_number(self, n=5): """ 2nd rule of summaries should allow this. 3 Starting the summary with a number instead of a capital letter. Also in parameters, returns, see also... Parameters ---------- n : int 4 Number of values to return. Returns ------- int 5 Subset of the original series with the n first values. See Also -------- tail : 6 Return the last n elements of the Series. Examples -------- >>> s = 10 >>> s 10 7 With the `n` parameter, we can change the number of returned rows: >>> s + 1 11 """ return 1 def contains(self, pat, case=True, na=float('NaN')): """ Return whether each value contains `pat`. In this case, we are illustrating how to use sections, even if the example is simple enough and does not require them. Parameters ---------- pat : str Pattern to check for within each element. case : bool, default True Whether check should be done with case sensitivity. na : object, default np.nan Fill value for missing data. See Also -------- related : Something related. Examples -------- >>> s = 25 >>> s 25 **Case sensitivity** With `case_sensitive` set to `False` we can match `a` with both `a` and `A`: >>> s + 1 26 **Missing values** We can fill missing values in the output using the `na` parameter: >>> s * 2 50 """ pass def mode(self, axis, numeric_only): """ Ensure reST directives don't affect checks for leading periods. The extended summary can be multiple paragraphs, but just one is enough to pass the validation. Parameters ---------- axis : str Sentence ending in period, followed by single directive. .. versionchanged:: 0.1.2 numeric_only : bool Sentence ending in period, followed by multiple directives. .. versionadded:: 0.1.2 .. deprecated:: 0.00.0 A multiline description, which spans another line. See Also -------- related : Something related. Examples -------- >>> result = 1 + 1 """ pass def good_imports(self): """ Ensure import other than numpy and pandas are fine. The extended summary can be multiple paragraphs, but just one is enough to pass the validation. See Also -------- related : Something related. Examples -------- This example does not import pandas or import numpy. >>> import datetime >>> datetime.MAXYEAR 9999 """ pass def no_returns(self): """ Say hello and have no returns. The extended summary can be multiple paragraphs, but just one is enough to pass the validation. See Also -------- related : Something related. Examples -------- >>> result = 1 + 1 """ pass def empty_returns(self): """ Say hello and always return None. Since this function never returns a value, this docstring doesn't need a return section. See Also -------- related : Something related. Examples -------- >>> result = 1 + 1 """ def say_hello(): return "Hello World!" say_hello() if True: return else: return None def warnings(self): """ Do one thing. Sometimes, this function does other things. Warnings -------- This function may produce side effects when some condition is met. See Also -------- related : Something related. Examples -------- >>> result = 1 + 1 """ pass def multiple_variables_on_one_line(self, matrix, a, b, i, j): """ Swap two values in a matrix. The extended summary can be multiple paragraphs, but just one is enough to pass the validation. Parameters ---------- matrix : list of list A double list that represents a matrix. a, b : int The indices of the first value. i, j : int The indices of the second value. See Also -------- related : Something related. Examples -------- >>> result = 1 + 1 """ pass def other_parameters(self, param1, param2): """ Ensure "Other Parameters" are recognized. The second parameter is used infrequently, so it is put in the "Other Parameters" section. Parameters ---------- param1 : bool Description of commonly used parameter. Other Parameters ---------------- param2 : str Description of infrequently used parameter. See Also -------- related : Something related. Examples -------- >>> result = 1 + 1 """ pass def valid_options_in_parameter_description_sets(self, bar): """ Ensure a PR06 error is not raised when type is member of a set. Literal keywords like 'integer' are valid when specified in a set of valid options for a keyword parameter. Parameters ---------- bar : {'integer', 'boolean'} The literal values of 'integer' and 'boolean' are part of an options set and thus should not be subject to PR06 warnings. See Also -------- related : Something related. Examples -------- >>> result = 1 + 1 """ class BadGenericDocStrings: """Everything here has a bad docstring """ def func(self): """Some function. With several mistakes in the docstring. It has a blank like after the signature `def func():`. The text 'Some function' should go in the line after the opening quotes of the docstring, not in the same line. There is a blank line between the docstring and the first line of code `foo = 1`. The closing quotes should be in the next line, not in this one.""" foo = 1 bar = 2 return foo + bar def astype(self, dtype): """ Casts Series type. Verb in third-person of the present simple, should be infinitive. """ pass def astype1(self, dtype): """ Method to cast Series type. Does not start with verb. """ pass def astype2(self, dtype): """ Cast Series type Missing dot at the end. """ pass def astype3(self, dtype): """ Cast Series type from its current type to the new type defined in the parameter dtype. Summary is too verbose and doesn't fit in a single line. """ pass def two_linebreaks_between_sections(self, foo): """ Test linebreaks message GL03. Note 2 blank lines before parameters section. Parameters ---------- foo : str Description of foo parameter. """ pass def linebreak_at_end_of_docstring(self, foo): """ Test linebreaks message GL03. Note extra blank line at end of docstring. Parameters ---------- foo : str Description of foo parameter. """ pass def plot(self, kind, **kwargs): """ Generate a plot. Render the data in the Series as a matplotlib plot of the specified kind. Note the blank line between the parameters title and the first parameter. Also, note that after the name of the parameter `kind` and before the colon, a space is missing. Also, note that the parameter descriptions do not start with a capital letter, and do not finish with a dot. Finally, the `**kwargs` parameter is missing. Parameters ---------- kind: str kind of matplotlib plot """ pass def unknown_section(self): """ This section has an unknown section title. Unknown Section --------------- This should raise an error in the validation. """ def sections_in_wrong_order(self): """ This docstring has the sections in the wrong order. Parameters ---------- name : str This section is in the right position. Examples -------- >>> print('So far Examples is good, as it goes before Parameters') So far Examples is good, as it goes before Parameters See Also -------- function : This should generate an error, as See Also needs to go before Examples. """ def deprecation_in_wrong_order(self): """ This docstring has the deprecation warning in the wrong order. This is the extended summary. The correct order should be summary, deprecation warning, extended summary. .. deprecated:: 1.0 This should generate an error as it needs to go before extended summary. """ def method_wo_docstrings(self): pass def directives_without_two_colons(self, first, second): """ Ensure reST directives have trailing colons. Parameters ---------- first : str Sentence ending in period, followed by single directive w/o colons. .. versionchanged 0.1.2 second : bool Sentence ending in period, followed by multiple directives w/o colons. .. versionadded 0.1.2 .. deprecated 0.00.0 """ pass class WarnGenericFormat: """ Those contains things that _may_ be incorrect formatting. """ def too_short_header_underline(self, a, b): """ The header line is too short. Parameters ------ a, b : int Foo bar baz. """ pass class BadSummaries: def no_summary(self): """ Returns ------- int Always one. """ def heading_whitespaces(self): """ Summary with heading whitespaces. Returns ------- int Always one. """ def wrong_line(self): """Quotes are on the wrong line. Both opening and closing.""" pass def no_punctuation(self): """ Has the right line but forgets punctuation """ pass def no_capitalization(self): """ provides a lowercase summary. """ pass def no_infinitive(self): """ Started with a verb that is not infinitive. """ def multi_line(self): """ Extends beyond one line which is not correct. """ def two_paragraph_multi_line(self): """ Extends beyond one line which is not correct. Extends beyond one line, which in itself is correct but the previous short summary should still be an issue. """ class BadParameters: """ Everything here has a problem with its Parameters section. """ def no_type(self, value): """ Lacks the type. Parameters ---------- value A parameter without type. """ def type_with_period(self, value): """ Has period after type. Parameters ---------- value : str. A parameter type should not finish with period. """ def no_description(self, value): """ Lacks the description. Parameters ---------- value : str """ def missing_params(self, kind, **kwargs): """ Lacks kwargs in Parameters. Parameters ---------- kind : str Foo bar baz. """ def bad_colon_spacing(self, kind): """ Has bad spacing in the type line. Parameters ---------- kind: str Needs a space after kind. """ def no_description_period(self, kind): """ Forgets to add a period to the description. Parameters ---------- kind : str Doesn't end with a dot """ def no_description_period_with_directive(self, kind): """ Forgets to add a period, and also includes a directive. Parameters ---------- kind : str Doesn't end with a dot .. versionadded:: 0.00.0 """ def no_description_period_with_directives(self, kind): """ Forgets to add a period, and also includes multiple directives. Parameters ---------- kind : str Doesn't end with a dot .. versionchanged:: 0.00.0 .. deprecated:: 0.00.0 """ def parameter_capitalization(self, kind): """ Forgets to capitalize the description. Parameters ---------- kind : str this is not capitalized. """ def blank_lines(self, kind): """ Adds a blank line after the section header. Parameters ---------- kind : str Foo bar baz. """ pass def integer_parameter(self, kind): """ Uses integer instead of int. Parameters ---------- kind : integer Foo bar baz. """ pass def string_parameter(self, kind): """ Uses string instead of str. Parameters ---------- kind : string Foo bar baz. """ pass def boolean_parameter(self, kind): """ Uses boolean instead of bool. Parameters ---------- kind : boolean Foo bar baz. """ pass def list_incorrect_parameter_type(self, kind): """ Uses list of boolean instead of list of bool. Parameters ---------- kind : list of boolean, integer, float or string Foo bar baz. """ pass def bad_parameter_spacing(self, a, b): """ The parameters on the same line have an extra space between them. Parameters ---------- a, b : int Foo bar baz. """ pass class BadReturns: def return_not_documented(self): """ Lacks section for Returns """ return "Hello world!" def yield_not_documented(self): """ Lacks section for Yields """ yield "Hello world!" def no_type(self): """ Returns documented but without type. Returns ------- Some value. """ return "Hello world!" def no_description(self): """ Provides type but no description. Returns ------- str """ return "Hello world!" def no_punctuation(self): """ Provides type and description but no period. Returns ------- str A nice greeting """ return "Hello world!" def named_single_return(self): """ Provides name but returns only one value. Returns ------- s : str A nice greeting. """ return "Hello world!" def no_capitalization(self): """ Forgets capitalization in return values description. Returns ------- foo : str The first returned string. bar : str the second returned string. """ return "Hello", "World!" def no_period_multi(self): """ Forgets period in return values description. Returns ------- foo : str The first returned string bar : str The second returned string. """ return "Hello", "World!" class BadSeeAlso: def no_desc(self): """ Return the first 5 elements of the Series. See Also -------- Series.tail """ pass def desc_no_period(self): """ Return the first 5 elements of the Series. See Also -------- Series.tail : Return the last 5 elements of the Series. Series.iloc : Return a slice of the elements in the Series, which can also be used to return the first or last n """ pass def desc_first_letter_lowercase(self): """ Return the first 5 elements of the Series. See Also -------- Series.tail : return the last 5 elements of the Series. Series.iloc : Return a slice of the elements in the Series, which can also be used to return the first or last n. """ pass def prefix_pandas(self): """ Have `pandas` prefix in See Also section. See Also -------- pandas.Series.rename : Alter Series index labels or name. DataFrame.head : The first `n` rows of the caller object. """ pass class BadExamples: def missing_whitespace_around_arithmetic_operator(self): """ Examples -------- >>> 2+5 7 """ pass def indentation_is_not_a_multiple_of_four(self): """ Examples -------- >>> if 2 + 5: ... pass """ pass def missing_whitespace_after_comma(self): """ Examples -------- >>> import datetime >>> value = datetime.date(2019,1,1) """ pass class TestValidator: def _import_path(self, klass=None, func=None): """ Build the required import path for tests in this module. Parameters ---------- klass : str Class name of object in module. func : str Function name of object in module. Returns ------- str Import path of specified object in this module """ base_path = "numpydoc.tests.test_validate" if klass: base_path = ".".join([base_path, klass]) if func: base_path = ".".join([base_path, func]) return base_path def test_one_liner(self, capsys): result = validate_one(self._import_path(klass="GoodDocStrings", func='one_liner')) errors = " ".join(err[1] for err in result["errors"]) assert 'should start in the line immediately after the opening quotes' not in errors assert 'should be placed in the line after the last text' not in errors def test_good_class(self, capsys): errors = validate_one(self._import_path(klass="GoodDocStrings"))["errors"] assert isinstance(errors, list) assert not errors @pytest.mark.parametrize( "func", [ "plot", "swap", "sample", "random_letters", "sample_values", "head", "head1", "summary_starts_with_number", "contains", "mode", "good_imports", "no_returns", "empty_returns", "multiple_variables_on_one_line", "other_parameters", "warnings", "valid_options_in_parameter_description_sets", ], ) def test_good_functions(self, capsys, func): errors = validate_one(self._import_path(klass="GoodDocStrings", func=func))[ "errors" ] assert isinstance(errors, list) assert not errors def test_bad_class(self, capsys): errors = validate_one(self._import_path(klass="BadGenericDocStrings"))["errors"] assert isinstance(errors, list) assert errors @pytest.mark.parametrize( "func", [ "too_short_header_underline", ], ) def test_bad_generic_functions(self, capsys, func): with pytest.warns(UserWarning): errors = validate_one( self._import_path(klass="WarnGenericFormat", func=func) # noqa:F821 ) assert 'is too short' in w.msg @pytest.mark.parametrize( "func", [ "func", "astype", "astype1", "astype2", "astype3", "plot", "directives_without_two_colons", ], ) def test_bad_generic_functions(self, capsys, func): errors = validate_one( self._import_path(klass="BadGenericDocStrings", func=func) # noqa:F821 )["errors"] assert isinstance(errors, list) assert errors @pytest.mark.parametrize( "klass,func,msgs", [ # See Also tests ( "BadGenericDocStrings", "unknown_section", ('Found unknown section "Unknown Section".',), ), ( "BadGenericDocStrings", "sections_in_wrong_order", ( "Sections are in the wrong order. Correct order is: Parameters, " "See Also, Examples", ), ), ( "BadGenericDocStrings", "deprecation_in_wrong_order", ("Deprecation warning should precede extended summary",), ), ( "BadGenericDocStrings", "directives_without_two_colons", ( "reST directives ['versionchanged', 'versionadded', " "'deprecated'] must be followed by two colons", ), ), ( "BadSeeAlso", "no_desc", ('Missing description for See Also "Series.tail" reference',), ), ( "BadSeeAlso", "desc_no_period", ('Missing period at end of description for See Also "Series.iloc"',), ), ( "BadSeeAlso", "desc_first_letter_lowercase", ('should be capitalized for See Also "Series.tail"',), ), # Summary tests ( "BadSummaries", "no_summary", ("No summary found",), ), ( "BadSummaries", "heading_whitespaces", ("Summary contains heading whitespaces",), ), ( "BadSummaries", "wrong_line", ("should start in the line immediately after the opening quotes", "should be placed in the line after the last text"), ), ("BadSummaries", "no_punctuation", ("Summary does not end with a period",)), ( "BadSummaries", "no_capitalization", ("Summary does not start with a capital letter",), ), ( "BadSummaries", "no_capitalization", ("Summary must start with infinitive verb",), ), ("BadSummaries", "multi_line", ("Summary should fit in a single line",)), ( "BadSummaries", "two_paragraph_multi_line", ("Summary should fit in a single line",), ), # Parameters tests ( "BadParameters", "no_type", ('Parameter "value" has no type',), ), ( "BadParameters", "type_with_period", ('Parameter "value" type should not finish with "."',), ), ( "BadParameters", "no_description", ('Parameter "value" has no description',), ), ( "BadParameters", "missing_params", ("Parameters {'**kwargs'} not documented",), ), ( "BadParameters", "bad_colon_spacing", ( 'Parameter "kind" requires a space before the colon ' "separating the parameter name and type", ), ), ( "BadParameters", "no_description_period", ('Parameter "kind" description should finish with "."',), ), ( "BadParameters", "no_description_period_with_directive", ('Parameter "kind" description should finish with "."',), ), ( "BadParameters", "parameter_capitalization", ('Parameter "kind" description should start with a capital letter',), ), ( "BadParameters", "integer_parameter", ('Parameter "kind" type should use "int" instead of "integer"',), ), ( "BadParameters", "string_parameter", ('Parameter "kind" type should use "str" instead of "string"',), ), ( "BadParameters", "boolean_parameter", ('Parameter "kind" type should use "bool" instead of "boolean"',), ), ( "BadParameters", "list_incorrect_parameter_type", ('Parameter "kind" type should use "bool" instead of "boolean"',), ), ( "BadParameters", "list_incorrect_parameter_type", ('Parameter "kind" type should use "int" instead of "integer"',), ), ( "BadParameters", "list_incorrect_parameter_type", ('Parameter "kind" type should use "str" instead of "string"',), ), ( "BadParameters", "bad_parameter_spacing", ("Parameters {'b'} not documented", "Unknown parameters {' b'}"), ), pytest.param( "BadParameters", "blank_lines", ("No error yet?",), marks=pytest.mark.xfail, ), # Returns tests ("BadReturns", "return_not_documented", ("No Returns section found",)), ("BadReturns", "yield_not_documented", ("No Yields section found",)), pytest.param("BadReturns", "no_type", ("foo",), marks=pytest.mark.xfail), ("BadReturns", "no_description", ("Return value has no description",)), ( "BadReturns", "no_punctuation", ('Return value description should finish with "."',), ), ( "BadReturns", "named_single_return", ( "The first line of the Returns section should contain only the " "type, unless multiple values are being returned", ), ), ( "BadReturns", "no_capitalization", ("Return value description should start with a capital letter",), ), ( "BadReturns", "no_period_multi", ('Return value description should finish with "."',), ), ( "BadGenericDocStrings", "method_wo_docstrings", ("The object does not have a docstring",), ), ( "BadGenericDocStrings", "two_linebreaks_between_sections", ( "Double line break found; please use only one blank line to " "separate sections or paragraphs, and do not leave blank lines " "at the end of docstrings", ), ), ( "BadGenericDocStrings", "linebreak_at_end_of_docstring", ( "Double line break found; please use only one blank line to " "separate sections or paragraphs, and do not leave blank lines " "at the end of docstrings", ), ), ], ) def test_bad_docstrings(self, capsys, klass, func, msgs): with pytest.warns(None) as w: result = validate_one(self._import_path(klass=klass, func=func)) if len(w): assert all('Unknown section' in str(ww.message) for ww in w) for msg in msgs: assert msg in " ".join(err[1] for err in result["errors"]) class TestValidatorClass: @pytest.mark.parametrize("invalid_name", ["unknown_mod", "unknown_mod.MyClass"]) def test_raises_for_invalid_module_name(self, invalid_name): msg = f'No module can be imported from "{invalid_name}"' with pytest.raises(ImportError, match=msg): numpydoc.validate.Validator._load_obj(invalid_name) @pytest.mark.parametrize( "invalid_name", ["datetime.BadClassName", "datetime.bad_method_name"] ) def test_raises_for_invalid_attribute_name(self, invalid_name): name_components = invalid_name.split(".") obj_name, invalid_attr_name = name_components[-2], name_components[-1] msg = f"'{obj_name}' has no attribute '{invalid_attr_name}'" with pytest.raises(AttributeError, match=msg): numpydoc.validate.Validator._load_obj(invalid_name) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1642724375.0 numpydoc-1.2/numpydoc/tests/test_xref.py0000664002342000234200000001424200000000000020115 0ustar00jarrodjarrod# -*- encoding:utf-8 -*- import pytest from numpydoc.xref import make_xref, DEFAULT_LINKS # Use the default numpydoc link mapping xref_aliases = DEFAULT_LINKS # Comes mainly from numpy data = r""" (...) array_like, float, optional (...) :term:`numpy:array_like`, :class:`python:float`, optional (2,) ndarray (2,) :obj:`ndarray ` (...,M,N) array_like (...,M,N) :term:`numpy:array_like` (..., M, N) array_like (..., :obj:`M`, :obj:`N`) :term:`numpy:array_like` (float, float), optional (:class:`python:float`, :class:`python:float`), optional 1-D array or sequence 1-D :obj:`array ` or :term:`python:sequence` array of str or unicode-like :obj:`array ` of :class:`python:str` or unicode-like array_like of float :term:`numpy:array_like` of :class:`python:float` bool or callable :ref:`bool ` or :func:`python:callable` int in [0, 255] :class:`python:int` in [0, 255] int or None, optional :class:`python:int` or :data:`python:None`, optional list of str or array_like :class:`python:list` of :class:`python:str` or :term:`numpy:array_like` sequence of array_like :term:`python:sequence` of :term:`numpy:array_like` str or pathlib.Path :class:`python:str` or :obj:`pathlib.Path` {'', string}, optional {'', :class:`python:str`}, optional {'C', 'F', 'A', or 'K'}, optional {'C', 'F', 'A', or 'K'}, optional {'linear', 'lower', 'higher', 'midpoint', 'nearest'} {'linear', 'lower', 'higher', 'midpoint', 'nearest'} {False, True, 'greedy', 'optimal'} {:data:`python:False`, :data:`python:True`, 'greedy', 'optimal'} {{'begin', 1}, {'end', 0}}, {string, int} {{'begin', 1}, {'end', 0}}, {:class:`python:str`, :class:`python:int`} callable f'(x,*args) :func:`python:callable` f'(x,*args) callable ``fhess(x, *args)``, optional :func:`python:callable` ``fhess(x, *args)``, optional spmatrix (format: ``csr``, ``bsr``, ``dia`` or coo``) :obj:`spmatrix` (format: ``csr``, ``bsr``, ``dia`` or coo``) :ref:`strftime ` :ref:`strftime ` callable or :ref:`strftime ` :func:`python:callable` or :ref:`strftime ` callable or :ref:`strftime behavior ` :func:`python:callable` or :ref:`strftime behavior ` list(int) :class:`python:list`\(:class:`python:int`) list[int] :class:`python:list`\[:class:`python:int`] dict(str, int) :class:`python:dict`\(:class:`python:str`, :class:`python:int`) dict[str, int] :class:`python:dict`\[:class:`python:str`, :class:`python:int`] tuple(float, float) :class:`python:tuple`\(:class:`python:float`, :class:`python:float`) dict[tuple(str, str), int] :class:`python:dict`\[:class:`python:tuple`\(:class:`python:str`, :class:`python:str`), :class:`python:int`] """ # noqa: E501 data_ignore_obj = r""" (...) array_like, float, optional (...) :term:`numpy:array_like`, :class:`python:float`, optional (2,) ndarray (2,) :obj:`ndarray ` (...,M,N) array_like (...,M,N) :term:`numpy:array_like` (..., M, N) array_like (..., M, N) :term:`numpy:array_like` (float, float), optional (:class:`python:float`, :class:`python:float`), optional 1-D array or sequence 1-D :obj:`array ` or :term:`python:sequence` array of str or unicode-like :obj:`array ` of :class:`python:str` or unicode-like array_like of float :term:`numpy:array_like` of :class:`python:float` bool or callable :ref:`bool ` or :func:`python:callable` int in [0, 255] :class:`python:int` in [0, 255] int or None, optional :class:`python:int` or :data:`python:None`, optional list of str or array_like :class:`python:list` of :class:`python:str` or :term:`numpy:array_like` sequence of array_like :term:`python:sequence` of :term:`numpy:array_like` str or pathlib.Path :class:`python:str` or pathlib.Path {'', string}, optional {'', :class:`python:str`}, optional {'C', 'F', 'A', or 'K'}, optional {'C', 'F', 'A', or 'K'}, optional {'linear', 'lower', 'higher', 'midpoint', 'nearest'} {'linear', 'lower', 'higher', 'midpoint', 'nearest'} {False, True, 'greedy', 'optimal'} {:data:`python:False`, :data:`python:True`, 'greedy', 'optimal'} {{'begin', 1}, {'end', 0}}, {string, int} {{'begin', 1}, {'end', 0}}, {:class:`python:str`, :class:`python:int`} callable f'(x,*args) :func:`python:callable` f'(x,*args) callable ``fhess(x, *args)``, optional :func:`python:callable` ``fhess(x, *args)``, optional spmatrix (format: ``csr``, ``bsr``, ``dia`` or coo``) spmatrix (format: ``csr``, ``bsr``, ``dia`` or coo``) :ref:`strftime ` :ref:`strftime ` callable or :ref:`strftime ` :func:`python:callable` or :ref:`strftime ` callable or :ref:`strftime behavior ` :func:`python:callable` or :ref:`strftime behavior ` list(int) :class:`python:list`\(:class:`python:int`) list[int] :class:`python:list`\[:class:`python:int`] dict(str, int) :class:`python:dict`\(:class:`python:str`, :class:`python:int`) dict[str, int] :class:`python:dict`\[:class:`python:str`, :class:`python:int`] tuple(float, float) :class:`python:tuple`\(:class:`python:float`, :class:`python:float`) dict[tuple(str, str), int] :class:`python:dict`\[:class:`python:tuple`\(:class:`python:str`, :class:`python:str`), :class:`python:int`] """ # noqa: E501 xref_ignore = {'or', 'in', 'of', 'default', 'optional'} @pytest.mark.parametrize( ('param_type', 'expected_result'), [tuple(s.split('\n')) for s in data.strip().split('\n\n')] ) def test_make_xref(param_type, expected_result): assert make_xref(param_type, xref_aliases, xref_ignore) == expected_result @pytest.mark.parametrize( ('param_type', 'expected_result'), [tuple(s.split('\n')) for s in data_ignore_obj.strip().split('\n\n')] ) def test_make_xref_ignore_unknown(param_type, expected_result): assert make_xref(param_type, xref_aliases, xref_ignore="all") == expected_result def test_xref_ignore_is_all(): with pytest.raises(TypeError, match="must be a set or 'all'"): make_xref("array_like", xref_aliases, xref_ignore="foo") ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1643049415.0578768 numpydoc-1.2/numpydoc/tests/tinybuild/0000775002342000234200000000000000000000000017540 5ustar00jarrodjarrod././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1641610654.0 numpydoc-1.2/numpydoc/tests/tinybuild/Makefile0000664002342000234200000000037100000000000021201 0ustar00jarrodjarrodall: clean html show clean: rm -rf _build/* rm -rf generated/ html: sphinx-build -nWT --keep-going -b html -d _build/doctrees . _build/html show: @python -c "import webbrowser; webbrowser.open_new_tab('file://$(PWD)/_build/html/index.html')" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1642724349.0 numpydoc-1.2/numpydoc/tests/tinybuild/conf.py0000664002342000234200000000123400000000000021037 0ustar00jarrodjarrodimport os import sys path = os.path.dirname(__file__) if path not in sys.path: sys.path.insert(0, path) import numpydoc_test_module # noqa extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'numpydoc', ] project = 'numpydoc_test_module' autosummary_generate = True autodoc_default_options = {'inherited-members': None} source_suffix = '.rst' master_doc = 'index' # NOTE: will be changed to `root_doc` in sphinx 4 exclude_patterns = ['_build'] intersphinx_mapping = { 'python': ('https://docs.python.org/3', None), } nitpicky = True highlight_language = 'python3' numpydoc_class_members_toctree = False numpydoc_xref_param_type = True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1641610654.0 numpydoc-1.2/numpydoc/tests/tinybuild/index.rst0000664002342000234200000000022200000000000021375 0ustar00jarrodjarrodnumpydoc_test_module ==================== .. automodule:: numpydoc_test_module :no-members: :no-inherited-members: :no-special-members: ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1642724349.0 numpydoc-1.2/numpydoc/tests/tinybuild/numpydoc_test_module.py0000664002342000234200000000163500000000000024361 0ustar00jarrodjarrod"""Numpydoc test module. .. currentmodule:: numpydoc_test_module .. autosummary:: :toctree: generated/ MyClass my_function Reference [1]_ References ---------- .. [1] https://numpydoc.readthedocs.io """ __all__ = ['MyClass', 'my_function'] class MyClass: """A class. Reference [2]_ Parameters ---------- *args : iterable Arguments. **kwargs : dict Keyword arguments. References ---------- .. [2] https://numpydoc.readthedocs.io """ def example(self, x): """Example method.""" pass def my_function(*args, **kwargs): """Return None. See [3]_. Parameters ---------- *args : iterable Arguments. **kwargs : dict Keyword arguments. Returns ------- out : None The output. References ---------- .. [3] https://numpydoc.readthedocs.io """ return None ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1642724349.0 numpydoc-1.2/numpydoc/validate.py0000664002342000234200000005127700000000000016552 0ustar00jarrodjarrod#!/usr/bin/env python """ Analyze docstrings to detect errors. Call ``validate(object_name_to_validate)`` to get a dictionary with all the detected errors. """ import ast import collections import importlib import inspect import pydoc import re import textwrap from .docscrape import get_doc_object DIRECTIVES = ["versionadded", "versionchanged", "deprecated"] DIRECTIVE_PATTERN = re.compile(r"^\s*\.\. ({})(?!::)".format('|'.join(DIRECTIVES)), re.I | re.M) ALLOWED_SECTIONS = [ "Parameters", "Attributes", "Methods", "Returns", "Yields", "Other Parameters", "Raises", "Warns", "Warnings", "See Also", "Notes", "References", "Examples", ] ERROR_MSGS = { "GL01": "Docstring text (summary) should start in the line immediately " "after the opening quotes (not in the same line, or leaving a " "blank line in between)", "GL02": "Closing quotes should be placed in the line after the last text " "in the docstring (do not close the quotes in the same line as " "the text, or leave a blank line between the last text and the " "quotes)", "GL03": "Double line break found; please use only one blank line to " "separate sections or paragraphs, and do not leave blank lines " "at the end of docstrings", "GL05": 'Tabs found at the start of line "{line_with_tabs}", please use ' "whitespace only", "GL06": 'Found unknown section "{section}". Allowed sections are: ' "{allowed_sections}", "GL07": "Sections are in the wrong order. Correct order is: {correct_sections}", "GL08": "The object does not have a docstring", "GL09": "Deprecation warning should precede extended summary", "GL10": "reST directives {directives} must be followed by two colons", "SS01": "No summary found (a short summary in a single line should be " "present at the beginning of the docstring)", "SS02": "Summary does not start with a capital letter", "SS03": "Summary does not end with a period", "SS04": "Summary contains heading whitespaces", "SS05": "Summary must start with infinitive verb, not third person " '(e.g. use "Generate" instead of "Generates")', "SS06": "Summary should fit in a single line", "ES01": "No extended summary found", "PR01": "Parameters {missing_params} not documented", "PR02": "Unknown parameters {unknown_params}", "PR03": "Wrong parameters order. Actual: {actual_params}. " "Documented: {documented_params}", "PR04": 'Parameter "{param_name}" has no type', "PR05": 'Parameter "{param_name}" type should not finish with "."', "PR06": 'Parameter "{param_name}" type should use "{right_type}" instead ' 'of "{wrong_type}"', "PR07": 'Parameter "{param_name}" has no description', "PR08": 'Parameter "{param_name}" description should start with a ' "capital letter", "PR09": 'Parameter "{param_name}" description should finish with "."', "PR10": 'Parameter "{param_name}" requires a space before the colon ' "separating the parameter name and type", "RT01": "No Returns section found", "RT02": "The first line of the Returns section should contain only the " "type, unless multiple values are being returned", "RT03": "Return value has no description", "RT04": "Return value description should start with a capital letter", "RT05": 'Return value description should finish with "."', "YD01": "No Yields section found", "SA01": "See Also section not found", "SA02": "Missing period at end of description for See Also " '"{reference_name}" reference', "SA03": "Description should be capitalized for See Also " '"{reference_name}" reference', "SA04": 'Missing description for See Also "{reference_name}" reference', "EX01": "No examples section found", } # Ignore these when evaluating end-of-line-"." checks IGNORE_STARTS = (" ", "* ", "- ") def error(code, **kwargs): """ Return a tuple with the error code and the message with variables replaced. This is syntactic sugar so instead of: - `('PR02', ERROR_MSGS['PR02'].format(doctest_log=log))` We can simply use: - `error('PR02', doctest_log=log)` Parameters ---------- code : str Error code. **kwargs Values for the variables in the error messages Returns ------- code : str Error code. message : str Error message with variables replaced. """ return (code, ERROR_MSGS[code].format(**kwargs)) class Validator: # TODO Can all this class be merged into NumpyDocString? def __init__(self, doc_object): self.doc = doc_object self.obj = self.doc._obj self.code_obj = inspect.unwrap(self.obj) self.raw_doc = self.obj.__doc__ or "" self.clean_doc = pydoc.getdoc(self.obj) @property def name(self): return '.'.join([self.obj.__module__, self.obj.__name__]) @staticmethod def _load_obj(name): """ Import Python object from its name as string. Parameters ---------- name : str Object name to import (e.g. pandas.Series.str.upper) Returns ------- object Python object that can be a class, method, function... Examples -------- >>> Validator._load_obj('datetime.datetime') """ for maxsplit in range(0, name.count(".") + 1): module, *func_parts = name.rsplit(".", maxsplit) try: obj = importlib.import_module(module) except ImportError: pass else: break else: raise ImportError(f"No module can be imported from \"{name}\"") for part in func_parts: obj = getattr(obj, part) return obj @property def type(self): return type(self.obj).__name__ @property def is_function_or_method(self): return inspect.isfunction(self.obj) @property def source_file_name(self): """ File name where the object is implemented (e.g. pandas/core/frame.py). """ try: fname = inspect.getsourcefile(self.code_obj) except TypeError: # In some cases the object is something complex like a cython # object that can't be easily introspected. An it's better to # return the source code file of the object as None, than crash pass else: return fname @property def source_file_def_line(self): """ Number of line where the object is defined in its file. """ try: return inspect.getsourcelines(self.code_obj)[-1] except (OSError, TypeError): # In some cases the object is something complex like a cython # object that can't be easily introspected. An it's better to # return the line number as None, than crash pass @property def start_blank_lines(self): i = None if self.raw_doc: for i, row in enumerate(self.raw_doc.split("\n")): if row.strip(): break return i @property def end_blank_lines(self): i = None if self.raw_doc: for i, row in enumerate(reversed(self.raw_doc.split("\n"))): if row.strip(): break return i @property def double_blank_lines(self): prev = True for row in self.raw_doc.split("\n"): if not prev and not row.strip(): return True prev = row.strip() return False @property def section_titles(self): sections = [] self.doc._doc.reset() while not self.doc._doc.eof(): content = self.doc._read_to_next_section() if ( len(content) > 1 and len(content[0]) == len(content[1]) and set(content[1]) == {"-"} ): sections.append(content[0]) return sections @property def summary(self): return " ".join(self.doc["Summary"]) @property def num_summary_lines(self): return len(self.doc["Summary"]) @property def extended_summary(self): if not self.doc["Extended Summary"] and len(self.doc["Summary"]) > 1: return " ".join(self.doc["Summary"]) return " ".join(self.doc["Extended Summary"]) def _doc_parameters(self, sections): parameters = collections.OrderedDict() for section in sections: for names, type_, desc in self.doc[section]: for name in names.split(", "): parameters[name] = (type_, desc) return parameters @property def doc_parameters(self): return self._doc_parameters(["Parameters"]) @property def doc_other_parameters(self): return self._doc_parameters(["Other Parameters"]) @property def doc_all_parameters(self): return self._doc_parameters(["Parameters", "Other Parameters"]) @property def signature_parameters(self): def add_stars(param_name, info): """ Add stars to *args and **kwargs parameters """ if info.kind == inspect.Parameter.VAR_POSITIONAL: return f"*{param_name}" elif info.kind == inspect.Parameter.VAR_KEYWORD: return f"**{param_name}" else: return param_name if inspect.isclass(self.obj): if hasattr(self.obj, "_accessors") and ( self.name.split(".")[-1] in self.obj._accessors ): # accessor classes have a signature but don't want to show this return tuple() try: sig = inspect.signature(self.obj) except (TypeError, ValueError): # Some objects, mainly in C extensions do not support introspection # of the signature return tuple() params = tuple( add_stars(parameter, sig.parameters[parameter]) for parameter in sig.parameters ) if params and params[0] in ("self", "cls"): return params[1:] return params @property def parameter_mismatches(self): errs = [] signature_params = self.signature_parameters all_params = tuple(self.doc_all_parameters) missing = set(signature_params) - set(all_params) if missing: errs.append(error("PR01", missing_params=str(missing))) extra = set(all_params) - set(signature_params) if extra: errs.append(error("PR02", unknown_params=str(extra))) if ( not missing and not extra and signature_params != all_params and not (not signature_params and not all_params) ): errs.append( error( "PR03", actual_params=signature_params, documented_params=all_params ) ) return errs @property def directives_without_two_colons(self): return DIRECTIVE_PATTERN.findall(self.raw_doc) def parameter_type(self, param): return self.doc_all_parameters[param][0] @property def see_also(self): result = collections.OrderedDict() for funcs, desc in self.doc["See Also"]: for func, _ in funcs: result[func] = "".join(desc) return result @property def examples(self): return self.doc["Examples"] @property def returns(self): return self.doc["Returns"] @property def yields(self): return self.doc["Yields"] @property def method_source(self): try: source = inspect.getsource(self.obj) except TypeError: return "" return textwrap.dedent(source) @property def method_returns_something(self): """ Check if the docstrings method can return something. Bare returns, returns valued None and returns from nested functions are disconsidered. Returns ------- bool Whether the docstrings method can return something. """ def get_returns_not_on_nested_functions(node): returns = [node] if isinstance(node, ast.Return) else [] for child in ast.iter_child_nodes(node): # Ignore nested functions and its subtrees. if not isinstance(child, ast.FunctionDef): child_returns = get_returns_not_on_nested_functions(child) returns.extend(child_returns) return returns tree = ast.parse(self.method_source).body if tree: returns = get_returns_not_on_nested_functions(tree[0]) return_values = [r.value for r in returns] # Replace NameConstant nodes valued None for None. for i, v in enumerate(return_values): if isinstance(v, ast.NameConstant) and v.value is None: return_values[i] = None return any(return_values) else: return False @property def deprecated(self): return ".. deprecated:: " in (self.summary + self.extended_summary) def _check_desc(desc, code_no_desc, code_no_upper, code_no_period, **kwargs): # Find and strip out any sphinx directives desc = "\n".join(desc) for directive in DIRECTIVES: full_directive = f".. {directive}" if full_directive in desc: # Only retain any description before the directive desc = desc[: desc.index(full_directive)].rstrip("\n") desc = desc.split("\n") errs = list() if not "".join(desc): errs.append(error(code_no_desc, **kwargs)) else: if desc[0][0].isalpha() and not desc[0][0].isupper(): errs.append(error(code_no_upper, **kwargs)) # Not ending in "." is only an error if the last bit is not # indented (e.g., quote or code block) if not desc[-1].endswith(".") and \ not desc[-1].startswith(IGNORE_STARTS): errs.append(error(code_no_period, **kwargs)) return errs def validate(obj_name): """ Validate the docstring. Parameters ---------- obj_name : str The name of the object whose docstring will be evaluated, e.g. 'pandas.read_csv'. The string must include the full, unabbreviated package/module names, i.e. 'pandas.read_csv', not 'pd.read_csv' or 'read_csv'. Returns ------- dict A dictionary containing all the information obtained from validating the docstring. Notes ----- The errors codes are defined as: - First two characters: Section where the error happens: * GL: Global (no section, like section ordering errors) * SS: Short summary * ES: Extended summary * PR: Parameters * RT: Returns * YD: Yields * RS: Raises * WN: Warns * SA: See Also * NT: Notes * RF: References * EX: Examples - Last two characters: Numeric error code inside the section For example, PR02 is the second codified error in the Parameters section (which in this case is assigned to the error when unknown parameters are documented). The error codes, their corresponding error messages, and the details on how they are validated, are not documented more than in the source code of this function. """ if isinstance(obj_name, str): doc = Validator(get_doc_object(Validator._load_obj(obj_name))) else: doc = Validator(obj_name) errs = [] if not doc.raw_doc: errs.append(error("GL08")) return { "type": doc.type, "docstring": doc.clean_doc, "deprecated": doc.deprecated, "file": doc.source_file_name, "file_line": doc.source_file_def_line, "errors": errs, "examples_errors": "", } if doc.start_blank_lines != 1 and "\n" in doc.raw_doc: errs.append(error("GL01")) if doc.end_blank_lines != 1 and "\n" in doc.raw_doc: errs.append(error("GL02")) if doc.double_blank_lines: errs.append(error("GL03")) for line in doc.raw_doc.splitlines(): if re.match("^ *\t", line): errs.append(error("GL05", line_with_tabs=line.lstrip())) unexpected_sections = [ section for section in doc.section_titles if section not in ALLOWED_SECTIONS ] for section in unexpected_sections: errs.append( error("GL06", section=section, allowed_sections=", ".join(ALLOWED_SECTIONS)) ) correct_order = [ section for section in ALLOWED_SECTIONS if section in doc.section_titles ] if correct_order != doc.section_titles: errs.append(error("GL07", correct_sections=", ".join(correct_order))) if doc.deprecated and not doc.extended_summary.startswith(".. deprecated:: "): errs.append(error("GL09")) directives_without_two_colons = doc.directives_without_two_colons if directives_without_two_colons: errs.append(error("GL10", directives=directives_without_two_colons)) if not doc.summary: errs.append(error("SS01")) else: if doc.summary[0].isalpha() and not doc.summary[0].isupper(): errs.append(error("SS02")) if doc.summary[-1] != ".": errs.append(error("SS03")) if doc.summary != doc.summary.lstrip(): errs.append(error("SS04")) elif doc.is_function_or_method and doc.summary.split(" ")[0][-1] == "s": errs.append(error("SS05")) if doc.num_summary_lines > 1: errs.append(error("SS06")) if not doc.extended_summary: errs.append(("ES01", "No extended summary found")) # PR01: Parameters not documented # PR02: Unknown parameters # PR03: Wrong parameters order errs += doc.parameter_mismatches for param, kind_desc in doc.doc_all_parameters.items(): if not param.startswith("*"): # Check can ignore var / kwargs if not doc.parameter_type(param): if ":" in param: errs.append(error("PR10", param_name=param.split(":")[0])) else: errs.append(error("PR04", param_name=param)) else: if doc.parameter_type(param)[-1] == ".": errs.append(error("PR05", param_name=param)) # skip common_type_error checks when the param type is a set of # options if "{" in doc.parameter_type(param): continue common_type_errors = [ ("integer", "int"), ("boolean", "bool"), ("string", "str"), ] for wrong_type, right_type in common_type_errors: if wrong_type in doc.parameter_type(param): errs.append( error( "PR06", param_name=param, right_type=right_type, wrong_type=wrong_type, ) ) errs.extend(_check_desc( kind_desc[1], "PR07", "PR08", "PR09", param_name=param)) if doc.is_function_or_method: if not doc.returns: if doc.method_returns_something: errs.append(error("RT01")) else: if len(doc.returns) == 1 and doc.returns[0].name: errs.append(error("RT02")) for name_or_type, type_, desc in doc.returns: errs.extend(_check_desc(desc, "RT03", "RT04", "RT05")) if not doc.yields and "yield" in doc.method_source: errs.append(error("YD01")) if not doc.see_also: errs.append(error("SA01")) else: for rel_name, rel_desc in doc.see_also.items(): if rel_desc: if not rel_desc.endswith("."): errs.append(error("SA02", reference_name=rel_name)) if rel_desc[0].isalpha() and not rel_desc[0].isupper(): errs.append(error("SA03", reference_name=rel_name)) else: errs.append(error("SA04", reference_name=rel_name)) if not doc.examples: errs.append(error("EX01")) return { "type": doc.type, "docstring": doc.clean_doc, "deprecated": doc.deprecated, "file": doc.source_file_name, "file_line": doc.source_file_def_line, "errors": errs, } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1642724349.0 numpydoc-1.2/numpydoc/xref.py0000664002342000234200000001464700000000000015725 0ustar00jarrodjarrodimport re # When sphinx (including the napoleon extension) parses the parameters # section of a docstring, it converts the information into field lists. # Some items in the list are for the parameter type. When the type fields # are processed, the text is split and some tokens are turned into # pending_xref nodes. These nodes are responsible for creating links. # # numpydoc does not create field lists, so the type information is # not placed into fields that can be processed to make links. Instead, # when parsing the type information we identify tokens that are link # worthy and wrap them around a :obj: role. # Note: we never split on commas that are not followed by a space # You risk creating bad rst markup if you do so. QUALIFIED_NAME_RE = re.compile( # e.g int, numpy.array, ~numpy.array, .class_in_current_module r'^' r'[~\.]?' r'[a-zA-Z_]\w*' r'(?:\.[a-zA-Z_]\w*)*' r'$' ) CONTAINER_SPLIT_RE = re.compile( # splits dict(str, int) into # ['dict', '[', 'str', ', ', 'int', ']', ''] r'(\s*[\[\]\(\)\{\}]\s*|,\s+)' ) CONTAINER_SPLIT_REJECT_RE = re.compile( # Leads to bad markup e.g. # {int}qualified_name r'[\]\)\}]\w' ) DOUBLE_QUOTE_SPLIT_RE = re.compile( # splits 'callable ``f(x0, *args)`` or ``f(x0, y0, *args)``' into # ['callable ', '``f(x0, *args)``', ' or ', '``f(x0, y0, *args)``', ''] r'(``.+?``)' ) ROLE_SPLIT_RE = re.compile( # splits to preserve ReST roles r'(:\w+:`.+?(?`', 'boolean': ':ref:`bool `', 'True': ':data:`python:True`', 'False': ':data:`python:False`', 'list': ':class:`python:list`', 'tuple': ':class:`python:tuple`', 'str': ':class:`python:str`', 'string': ':class:`python:str`', 'dict': ':class:`python:dict`', 'float': ':class:`python:float`', 'int': ':class:`python:int`', 'callable': ':func:`python:callable`', 'iterable': ':term:`python:iterable`', 'sequence': ':term:`python:sequence`', 'contextmanager': ':func:`python:contextlib.contextmanager`', 'namedtuple': ':func:`python:collections.namedtuple`', 'generator': ':term:`python:generator`', # NumPy 'array': 'numpy.ndarray', 'ndarray': 'numpy.ndarray', 'np.ndarray': 'numpy.ndarray', 'array-like': ':term:`numpy:array_like`', 'array_like': ':term:`numpy:array_like`', 'scalar': ':ref:`scalar `', 'RandomState': 'numpy.random.RandomState', 'np.random.RandomState': 'numpy.random.RandomState', 'np.inf': ':data:`numpy.inf`', 'np.nan': ':data:`numpy.nan`', 'numpy': ':mod:`numpy`', } def make_xref(param_type, xref_aliases, xref_ignore): """Parse and apply appropriate sphinx role(s) to `param_type`. The :obj: role is the default. Parameters ---------- param_type : str text xref_aliases : dict Mapping used to resolve common abbreviations and aliases to fully qualified names that can be cross-referenced. xref_ignore : set or "all" A set containing words not to cross-reference. Instead of a set, the string 'all' can be given to ignore all unrecognized terms. Unrecognized terms include those that are not in `xref_aliases` and are not already wrapped in a reST role. Returns ------- out : str Text with fully-qualified names and terms that may be wrapped in a ``:obj:`` role. """ ignore_set = xref_ignore wrap_unknown = True if isinstance(xref_ignore, str): if xref_ignore.lower() == "all": wrap_unknown = False ignore_set = set() else: raise TypeError( f"xref_ignore must be a set or 'all', got {xref_ignore}" ) if param_type in xref_aliases: link, title = xref_aliases[param_type], param_type param_type = link else: link = title = param_type if QUALIFIED_NAME_RE.match(link) and link not in ignore_set: if link != title: return f':obj:`{title} <{link}>`' if wrap_unknown: return f':obj:`{link}`' return link def _split_and_apply_re(s, pattern): """ Split string using the regex pattern, apply main function to the parts that do not match the pattern, combine the results """ results = [] tokens = pattern.split(s) n = len(tokens) if n > 1: for i, tok in enumerate(tokens): if pattern.match(tok): results.append(tok) else: res = make_xref(tok, xref_aliases, xref_ignore) # Opening brackets immediately after a role is # bad markup. Detect that and add backslash. # :role:`type`( to :role:`type`\( if res and res[-1] == '`' and i < n-1: next_char = tokens[i+1][0] if next_char in '([{': res += '\\' results.append(res) return ''.join(results) return s # The cases are dealt with in an order the prevents # conflict. # Then the strategy is: # - Identify a pattern we are not interested in # - split off the pattern # - re-apply the function to the other parts # - join the results with the pattern # Unsplittable literal if '``' in param_type: return _split_and_apply_re(param_type, DOUBLE_QUOTE_SPLIT_RE) # Any roles if ':`' in param_type: return _split_and_apply_re(param_type, ROLE_SPLIT_RE) # Any quoted expressions if '`' in param_type: return _split_and_apply_re(param_type, SINGLE_QUOTE_SPLIT_RE) # Any sort of bracket '[](){}' if any(c in CONTAINER_CHARS for c in param_type): if CONTAINER_SPLIT_REJECT_RE.search(param_type): return param_type return _split_and_apply_re(param_type, CONTAINER_SPLIT_RE) # Common splitter tokens return _split_and_apply_re(param_type, TEXT_SPLIT_RE) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1643049415.0558767 numpydoc-1.2/numpydoc.egg-info/0000775002342000234200000000000000000000000016065 5ustar00jarrodjarrod././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643049415.0 numpydoc-1.2/numpydoc.egg-info/PKG-INFO0000664002342000234200000000374300000000000017171 0ustar00jarrodjarrodMetadata-Version: 2.1 Name: numpydoc Version: 1.2 Summary: Sphinx extension to support docstrings in Numpy format Home-page: https://numpydoc.readthedocs.io Author: Pauli Virtanen and others Author-email: pav@iki.fi License: BSD Keywords: sphinx numpy Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Plugins Classifier: License :: OSI Approved :: BSD License Classifier: Topic :: Documentation Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Requires-Python: >=3.7 Provides-Extra: testing License-File: LICENSE.txt ===================================== numpydoc -- Numpy's Sphinx extensions ===================================== .. image:: https://readthedocs.org/projects/numpydoc/badge/?version=latest :alt: Documentation Status :scale: 100% :target: https://numpydoc.readthedocs.io/en/latest/ .. image:: https://codecov.io/gh/numpy/numpydoc/branch/main/graph/badge.svg :target: https://app.codecov.io/gh/numpy/numpydoc/branch/main .. image:: https://github.com/numpy/numpydoc/actions/workflows/test.yml/badge.svg?branch=main :target: https://github.com/numpy/numpydoc/actions/workflows/test.yml This package provides the ``numpydoc`` Sphinx extension for handling docstrings formatted according to the NumPy documentation format. The extension also adds the code description directives ``np:function``, ``np-c:function``, etc. numpydoc requires Python 3.7+ and sphinx 1.8+. For usage information, please refer to the `documentation `_. The `numpydoc docstring guide `_ explains how to write docs formatted for this extension, and the `user guide `_ explains how to use it with Sphinx. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643049415.0 numpydoc-1.2/numpydoc.egg-info/SOURCES.txt0000664002342000234200000000166400000000000017760 0ustar00jarrodjarrodLICENSE.txt MANIFEST.in README.rst RELEASE.rst setup.cfg setup.py test_requirements.txt doc/Makefile doc/conf.py doc/example.py doc/example.rst doc/format.rst doc/index.rst doc/install.rst doc/make.bat doc/release_notes.rst doc/requirements.txt doc/validation.rst numpydoc/__init__.py numpydoc/__main__.py numpydoc/docscrape.py numpydoc/docscrape_sphinx.py numpydoc/numpydoc.py numpydoc/validate.py numpydoc/xref.py numpydoc.egg-info/PKG-INFO numpydoc.egg-info/SOURCES.txt numpydoc.egg-info/dependency_links.txt numpydoc.egg-info/requires.txt numpydoc.egg-info/top_level.txt numpydoc/templates/numpydoc_docstring.rst numpydoc/tests/test_docscrape.py numpydoc/tests/test_full.py numpydoc/tests/test_main.py numpydoc/tests/test_numpydoc.py numpydoc/tests/test_validate.py numpydoc/tests/test_xref.py numpydoc/tests/tinybuild/Makefile numpydoc/tests/tinybuild/conf.py numpydoc/tests/tinybuild/index.rst numpydoc/tests/tinybuild/numpydoc_test_module.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643049415.0 numpydoc-1.2/numpydoc.egg-info/dependency_links.txt0000664002342000234200000000000100000000000022133 0ustar00jarrodjarrod ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643049415.0 numpydoc-1.2/numpydoc.egg-info/requires.txt0000664002342000234200000000010100000000000020455 0ustar00jarrodjarrodsphinx>=1.8 Jinja2>=2.10 [testing] pytest pytest-cov matplotlib ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643049415.0 numpydoc-1.2/numpydoc.egg-info/top_level.txt0000664002342000234200000000001100000000000020607 0ustar00jarrodjarrodnumpydoc ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1643049415.0588768 numpydoc-1.2/setup.cfg0000664002342000234200000000056600000000000014365 0ustar00jarrodjarrod[tool:pytest] addopts = --showlocals --doctest-modules -ra --cov-report= --cov=numpydoc --junit-xml=junit-results.xml --ignore=doc/ junit_family = xunit2 filterwarnings = ignore:'U' mode is deprecated:DeprecationWarning ignore:.*sphinx\.util\.smartypants is deprecated.*: ignore:Using or importing the ABCs.*:DeprecationWarning [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1642724362.0 numpydoc-1.2/setup.py0000664002342000234200000000356300000000000014256 0ustar00jarrodjarrodimport sys import os from setuptools import setup from numpydoc import __version__ as version if sys.version_info < (3, 7): raise RuntimeError("Python version >= 3.7 required.") def read(fname): """Utility function to get README.rst into long_description. ``long_description`` is what ends up on the PyPI front page. """ with open(os.path.join(os.path.dirname(__file__), fname)) as f: contents = f.read() return contents setup( name="numpydoc", packages=["numpydoc"], version=version, description="Sphinx extension to support docstrings in Numpy format", long_description=read('README.rst'), # classifiers from http://pypi.python.org/pypi?%3Aaction=list_classifiers classifiers=["Development Status :: 4 - Beta", "Environment :: Plugins", "License :: OSI Approved :: BSD License", "Topic :: Documentation", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", ], keywords="sphinx numpy", author="Pauli Virtanen and others", author_email="pav@iki.fi", url="https://numpydoc.readthedocs.io", license="BSD", install_requires=["sphinx >= 1.8", 'Jinja2>=2.10'], python_requires=">=3.7", extras_require={ "testing": [ req for req in read('test_requirements.txt').split('\n') if not req.startswith('#') ], }, package_data={'numpydoc': [ 'tests/test_*.py', 'tests/tinybuild/Makefile', 'tests/tinybuild/index.rst', 'tests/tinybuild/*.py', 'templates/*.rst', ]}, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1641782216.0 numpydoc-1.2/test_requirements.txt0000664002342000234200000000003500000000000017056 0ustar00jarrodjarrodpytest pytest-cov matplotlib