././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1695687844.3821564 numpydoc-1.6.0/0000755002342000234200000000000014504422244012735 5ustar00jarrodjarrod././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501235.0 numpydoc-1.6.0/LICENSE.txt0000644002342000234200000000242214474143463014571 0ustar00jarrodjarrodCopyright (C) 2008-2023 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=1693501235.0 numpydoc-1.6.0/MANIFEST.in0000644002342000234200000000046214474143463014506 0ustar00jarrodjarrodinclude MANIFEST.in include *.txt include *.rst recursive-include doc * recursive-include requirements * recursive-include numpydoc * # Exclude what we don't want to include prune */__pycache__ exclude .circleci/config.yml exclude .coveragerc exclude codecov.yml global-exclude *.pyc *~ *.bak *.swp *.pyo ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1695687844.3821564 numpydoc-1.6.0/PKG-INFO0000644002342000234200000001015614504422244014035 0ustar00jarrodjarrodMetadata-Version: 2.1 Name: numpydoc Version: 1.6.0 Summary: Sphinx extension to support docstrings in Numpy format Author-email: Pauli Virtanen and others License: Copyright (C) 2008-2023 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. Project-URL: Homepage, https://numpydoc.readthedocs.io Project-URL: Source, https://github.com/numpy/numpydoc/ Keywords: sphinx,numpy 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.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Requires-Python: >=3.8 Description-Content-Type: text/x-rst License-File: LICENSE.txt Requires-Dist: sphinx>=5 Requires-Dist: Jinja2>=2.10 Requires-Dist: tabulate>=0.8.10 Requires-Dist: tomli>=1.1.0; python_version < "3.11" Provides-Extra: developer Requires-Dist: pre-commit>=3.3; extra == "developer" Requires-Dist: tomli; python_version < "3.11" and extra == "developer" Provides-Extra: doc Requires-Dist: numpy>=1.22; extra == "doc" Requires-Dist: matplotlib>=3.5; extra == "doc" Requires-Dist: pydata-sphinx-theme>=0.13.3; extra == "doc" Requires-Dist: sphinx>=7; extra == "doc" Provides-Extra: test Requires-Dist: pytest; extra == "test" Requires-Dist: pytest-cov; extra == "test" Requires-Dist: matplotlib; extra == "test" ===================================== 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.8+ and sphinx 5+. 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=1693501235.0 numpydoc-1.6.0/README.rst0000644002342000234200000000227714474143463014445 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.8+ and sphinx 5+. 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=1693501227.0 numpydoc-1.6.0/RELEASE.rst0000644002342000234200000000274314474143453014565 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/_version.py``. - Commit changes:: git add numpydoc/_version.py doc/release_notes.rst git commit -m 'Designate release' - Add the version number (e.g., `v1.2.0`) as a tag in git:: git tag -s [-u ] v -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 --upgrade build wheel twine python -m build --sdist --wheel twine upload -s dist/* - Update ``__version__`` in ``numpydoc/_version.py``. - Commit changes:: git add numpydoc/_version.py git commit -m 'Bump version' git push origin main ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1695687844.3801563 numpydoc-1.6.0/doc/0000755002342000234200000000000014504422244013502 5ustar00jarrodjarrod././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501226.0 numpydoc-1.6.0/doc/Makefile0000644002342000234200000001605514474143452015160 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=1693501226.0 numpydoc-1.6.0/doc/conf.py0000644002342000234200000001075114474143452015014 0ustar00jarrodjarrod# Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html from datetime import date import numpydoc # -- Path setup -------------------------------------------------------------- # 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. import os import sys # 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 # -- Project information ----------------------------------------------------- project = "numpydoc" copyright = f"2019-{date.today().year}, 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. # version = .__version__ # The full version, including alpha/beta/rc tags. release = numpydoc.__version__ version = numpydoc.__version__ # -- General configuration --------------------------------------------------- # 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", "sphinx.ext.imgmath", ] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The root toctree document # master_doc = "index" # NOTE: will be changed to `root_doc` in sphinx 4 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"} # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" # -- 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 = { "show_prev_next": False, "navbar_end": ["theme-switcher", "search-field.html", "navbar-icon-links.html"], "icon_links": [ { "name": "GitHub", "url": "https://github.com/numpy/numpydoc", "icon": "fab fa-github-square", "type": "fontawesome", }, ], } html_sidebars = { "**": [], } html_context = { "default_mode": "light", } 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'] # 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", "numpydoc Documentation", "Numpydoc maintainers", "manual", ), ] # -- Intersphinx setup ---------------------------------------------------- # 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=1693501226.0 numpydoc-1.6.0/doc/example.py0000644002342000234200000001001514474143452015513 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. """ 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=1693501226.0 numpydoc-1.6.0/doc/example.rst0000644002342000234200000000037314474143452015701 0ustar00jarrodjarrod.. _example: ======= Example ======= Source ====== .. literalinclude:: example.py :caption: `example.py `_ :language: python Rendered ======== .. automodule:: example :members: ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501226.0 numpydoc-1.6.0/doc/format.rst0000644002342000234200000005707414474143452015550 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. .. _receives: 7. Receives ``````````` Explanation of parameters passed to a generator's ``.send()`` method, formatted as for :ref:`Parameters `, above. Since, like for :ref:`Yields ` and :ref:`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 :ref:`Receives ` it must also include :ref:`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. .. _raises: 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 :ref:`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 :ref:`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=1693501226.0 numpydoc-1.6.0/doc/index.rst0000644002342000234200000000142614474143452015355 0ustar00jarrodjarrod:html_theme.sidebar_secondary.remove: true ===================================== 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=1693501226.0 numpydoc-1.6.0/doc/install.rst0000644002342000234200000001536714474143452015725 0ustar00jarrodjarrod=============== Getting started =============== Installation ============ This extension requires Python 3.8+, sphinx 5+ 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 | dict 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. It can also be a dict mapping names of classes to boolean values (missing keys are treated as ``True``). For example, ``defaultdict(lambda: False, {'mymod.MyClass': True})`` would only show inherited class members for ``MyClass``, whereas ``{'mymod.MyClass': False}`` would show inherited class members for all classes except ``MyClass``. Note that disabling this for a limited set of classes might simultaneously require the use of a separate, custom autosummary class template with ``:no-inherited-members:`` in the ``autoclass`` directive options. 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_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_validation_overrides : dict A dictionary mapping :ref:`validation checks ` to a container of strings using :py:mod:`re` syntax specifying patterns to ignore for docstring validation. For example, the following skips the ``SS02`` check for docstrings starting with the word ``Process``:: numpydoc_validation_overrides = {"SS02": [r'^Process ']} The default is an empty dictionary meaning no overrides. Only has an effect when docstring validation is activated, i.e. ``numpydoc_validation_checks`` is not an empty set. Use :ref:`inline ignore comments ` to turn off specific checks for parts of your code. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501226.0 numpydoc-1.6.0/doc/make.bat0000644002342000234200000001510114474143452015114 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=1695687179.0 numpydoc-1.6.0/doc/release_notes.rst0000644002342000234200000006543414504421013017071 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.6.0 ----- Release date: 21 August 2023 Requires Python 3.8+ and Sphinx 5+. `Full Changelog `__ Closed issues ~~~~~~~~~~~~~ - ``numpydoc ignore`` inline comment not recognized when using decorators `#495 `__ - Align pre-commit and Sphinx rule override syntax `#466 `__ - Use pyproject.toml `#473 `__ - rtd is failing `#471 `__ - circleci-artifacts-redirector-action error `#469 `__ - Incorporate a pre-commit hook for numpydoc validation `#450 `__ - PR06 fails when offending type name is used only as a substring `#446 `__ - style of ``[1]`` is strange in numpydoc docs `#443 `__ - Double click on function parameter selects parameter type as well `#427 `__ Merged pull requests ~~~~~~~~~~~~~~~~~~~~ - Bump actions/checkout from 3 to 4 `#504 `__ (`dependabot[bot] `__) - [pre-commit.ci] pre-commit autoupdate `#501 `__ (`pre-commit-ci[bot] `__) - BUG: validator now handles properties `#500 `__ (`sdiebolt `__) - Fix clean_backref for extensions that have backrefs to inline elements. `#499 `__ (`mcmtroffaes `__) - BUG: validator now handles decorators `#496 `__ (`sdiebolt `__) - TST: Test no-file for source `#493 `__ (`larsoner `__) - Use label-check and attach-next-milestone-action `#492 `__ (`jarrodmillman `__) - Remove old linter check (using precommit bot now) `#491 `__ (`jarrodmillman `__) - Update pydata-sphinx-theme `#490 `__ (`jarrodmillman `__) - Use dependabot to update actions `#489 `__ (`jarrodmillman `__) - Add config for pre-commit bot `#488 `__ (`jarrodmillman `__) - Update pre-commits `#487 `__ (`jarrodmillman `__) - Sync updates to generate_requirements.py from scikit-image `#485 `__ (`mwtoews `__) - Generate requirements files from pyproject.toml `#483 `__ (`mwtoews `__) - Add Source to project.urls table `#480 `__ (`mwtoews `__) - Align behavior of pre-commit hook and Sphinx extension `#476 `__ (`stefmolin `__) - Fix dynamic version specification. `#477 `__ (`stefmolin `__) - Update year `#475 `__ (`jarrodmillman `__) - Use pyproject.toml `#474 `__ (`jarrodmillman `__) - Install package on rtd `#472 `__ (`jarrodmillman `__) - Add token for artifact redirector `#470 `__ (`jarrodmillman `__) - Use rtd template (old method is deprecated) `#468 `__ (`jarrodmillman `__) - Update doc requirements `#465 `__ (`jarrodmillman `__) - Update pre-commit hooks `#464 `__ (`jarrodmillman `__) - Drop Python 3.7 `#462 `__ (`jarrodmillman `__) - Require Sphinx 5+ `#461 `__ (`jarrodmillman `__) - Pre-commit hook for running numpydoc validation `#454 `__ (`stefmolin `__) - MAINT: Compatibility with pydata-sphinx-theme `#453 `__ (`larsoner `__) - MAINT: Refactoring ``get_doc_object`` `#452 `__ (`F3eQnxN3RriK `__) - Update blacken-docs repo after owner transfer. `#451 `__ (`stefmolin `__) - Update pre-commit `#449 `__ (`jarrodmillman `__) - MAINT: Fix CIs for sphinx 6 `#448 `__ (`larsoner `__) - Change PR06 logic to only fail when type is used standalone `#447 `__ (`amoeba `__) - Fix release notes `#442 `__ (`jarrodmillman `__) \* *This Changelog was automatically generated by* `github_changelog_generator `__ 1.5.0 ----- Release date: 8 October 2022 Requires Python 3.7+ and Sphinx 4.2+. `Full Changelog `__ Fixed bugs ~~~~~~~~~~ - Parsing ``returns`` section with several types and no name `#428 `__ - BUG: Fix returns parsing no name `#429 `__ (`rossbar `__) Closed issues ~~~~~~~~~~~~~ - readthedocs build failing `#439 `__ - Exclude class properties from being listed under METHODS section `#339 `__ - BUG: Numpydoc doesn’t render attributes decorated with ``cached\_property`` in the Attributes section `#432 `__ - Is numpydoc_use_blockquotes deprecated or not yet? `#420 `__ - No light theme available in docs `#413 `__ - 1.4.0 release plan `#408 `__ Merged pull requests ~~~~~~~~~~~~~~~~~~~~ - Update doc requirements `#441 `__ (`jarrodmillman `__) - Update pydata-sphinx-theme `#440 `__ (`jarrodmillman `__) - Support Python 3.11 `#438 `__ (`jarrodmillman `__) - Update precommit hooks `#437 `__ (`jarrodmillman `__) - Use Python 3.10 to build docs `#436 `__ (`jarrodmillman `__) - Use `requirements/*.txt` files for CI `#435 `__ (`jarrodmillman `__) - Fix front page `#434 `__ (`jarrodmillman `__) - Add cached property support `#433 `__ (`rossbar `__) - ENH: Update validate.py to allow parameters with trailing underscores. `#425 `__ (`stefmolin `__) - DOC: Use ``:ref:`` when referring to section headers `#424 `__ (`namurphy `__) - Remove numpydoc_use_blockquotes `#422 `__ (`jarrodmillman `__) - Require sphinx>=4.2 (cleanup) `#421 `__ (`jarrodmillman `__) - docs: fix validation include line numbers `#418 `__ (`thatlittleboy `__) - Update precommit linters `#417 `__ (`jarrodmillman `__) - Update GH actions `#416 `__ (`jarrodmillman `__) - ENH: Add support for dict show_inherited_class_members `#415 `__ (`larsoner `__) - DOC: Add theme switcher and default to lightmode. `#414 `__ (`rossbar `__) - Require sphinx>=4.2 `#411 `__ (`jarrodmillman `__) \* *This Changelog was automatically generated by*\ `github_changelog_generator `__ 1.4.0 ----- Release date: 9 June 2022 Requires Python 3.7+ and Sphinx 3.0+. `Full Changelog `__ Fixed bugs ~~~~~~~~~~ - Fix bug with version name `#400 `__ (`jarrodmillman `__) Closed issues ~~~~~~~~~~~~~ - sphinx 5 compatibility `#399 `__ Merged pull requests ~~~~~~~~~~~~~~~~~~~~ - Fix CI `#410 `__ (`jarrodmillman `__) - Remove pytest py3.11b2 workaround `#407 `__ (`jarrodmillman `__) - Update GH actions `#406 `__ (`jarrodmillman `__) - Add workaround for pytest failures on 3.11b2 `#404 `__ (`jarrodmillman `__) - Use node.findall if available (docutils 18.x) `#403 `__ (`drammock `__) - Test docutils 0.18.1 `#402 `__ (`jarrodmillman `__) - Remove old warning filters `#398 `__ (`jarrodmillman `__) - Cleanup sphinx conf `#397 `__ (`jarrodmillman `__) - Update pre-commit `#396 `__ (`jarrodmillman `__) - Test prereleases of requirements `#395 `__ (`jarrodmillman `__) - Ignore black formatting `#394 `__ (`jarrodmillman `__) - Use black `#391 `__ (`jarrodmillman `__) - Test on 3.11 `#375 `__ (`jarrodmillman `__) \* *This Changelog was automatically generated by*\ `github_changelog_generator `__ 1.3.1 ----- Release date: 30 April 2022 Requires Python 3.7+ and Sphinx 3.0+. `Full Changelog `__ Closed issues ~~~~~~~~~~~~~ - numpydoc-1.3.tar.gz on pypi is missing requirements/ needed by setup.py `#387 `__ - What to do about Jinja2 dependency & supporting old sphinx versions `#380 `__ - RFE: please update for ``jinja2`` 3.x `#376 `__ - Test failures with Sphinx 4.5.0 `#373 `__ Merged pull requests ~~~~~~~~~~~~~~~~~~~~ - Update doc requirements `#389 `__ (`jarrodmillman `__) - Update manifest `#388 `__ (`jarrodmillman `__) 1.3 --- Release date: 29 April 2022 Requires Python 3.7+ and Sphinx 3.0+. `Full Changelog `__ Closed issues ~~~~~~~~~~~~~ - Broken “many checks” link in validation chapter `#378 `__ - 1.2.1: pytest warnings `#377 `__ Merged pull requests ~~~~~~~~~~~~~~~~~~~~ - Require sphinx>3 `#385 `__ (`jarrodmillman `__) - Update tests + coverage config to rm warnings. `#383 `__ (`rossbar `__) - MAINT: Dont import for version `#382 `__ (`larsoner `__) - Runtime verification of sphinx and jinja2 versions `#381 `__ (`rossbar `__) - DOC: fix broken link to built-in validation checks. `#379 `__ (`rossbar `__) - Add pre-commit hook / linter `#374 `__ (`jarrodmillman `__) - Remove deprecated numpydoc_edit_link `#372 `__ (`jarrodmillman `__) - Cleanup cruft `#371 `__ (`jarrodmillman `__) 1.2.1 ----- Release date: 29 March 2022 Requires Python 3.7+ and Sphinx 1.8+. `Full Changelog `__ Implemented enhancements ~~~~~~~~~~~~~~~~~~~~~~~~ - Update year `#370 `__ (`jarrodmillman `__) Fixed bugs ~~~~~~~~~~ - Use consistent release tags `#361 `__ (`jarrodmillman `__) - BUG: 1.2 raises error w.r.t. object has no attribute ‘\__name_\_’ `#362 `__ - Use isgeneratorfunction to avoid false alarm YD01 validation `#368 `__ (`jnothman `__) - Fix AttributeError in underline length check `#363 `__ (`rossbar `__) - Upper bound jinja `#369 `__ (`jarrodmillman `__) Closed issues ~~~~~~~~~~~~~ - Validation check YD01 not implemented properly `#365 `__ - Support numpydoc validation without running sphinx `#364 `__ - 1.2: change tagging convention? `#360 `__ 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=1693501226.0 numpydoc-1.6.0/doc/validation.rst0000644002342000234200000001700014474143452016373 0ustar00jarrodjarrod========== Validation ========== .. _pre_commit_hook: Docstring Validation using Pre-Commit Hook ------------------------------------------ To enable validation of docstrings as you commit files, add the following to your ``.pre-commit-config.yaml`` file: .. code-block:: yaml - repo: https://github.com/numpy/numpydoc rev: hooks: - id: numpydoc-validation After installing ``numpydoc``, run the following to see available command line options for this hook: .. code-block:: bash $ python -m numpydoc.hooks.validate_docstrings --help Using a config file provides additional customization. Both ``pyproject.toml`` and ``setup.cfg`` are supported; however, if the project contains both you must use the ``pyproject.toml`` file. The example below configures the pre-commit hook as follows: * ``checks``: Report findings on all checks except ``EX01``, ``SA01``, and ``ES01`` (using the same logic as the :ref:`validation during Sphinx build ` for ``numpydoc_validation_checks``). * ``exclude``: Don't report issues on objects matching any of the regular regular expressions ``\.undocumented_method$`` or ``\.__repr__$``. This maps to ``numpydoc_validation_exclude`` from the :ref:`Sphinx build configuration `. * ``override_SS05``: Allow docstrings to start with "Process ", "Assess ", or "Access ". To override different checks, add a field for each code in the form of ``override_`` with a collection of regular expression(s) to search for in the contents of a docstring, not the object name. This maps to ``numpydoc_validation_overrides`` from the :ref:`Sphinx build configuration `. ``pyproject.toml``:: [tool.numpydoc_validation] checks = [ "all", # report on all checks, except the below "EX01", "SA01", "ES01", ] exclude = [ # don't report on objects that match any of these regex '\.undocumented_method$', '\.__repr__$', ] override_SS05 = [ # override SS05 to allow docstrings starting with these words '^Process ', '^Assess ', '^Access ', ] ``setup.cfg``:: [tool:numpydoc_validation] checks = all,EX01,SA01,ES01 exclude = \.undocumented_method$,\.__repr__$ override_SS05 = ^Process ,^Assess ,^Access , In addition to the above, :ref:`inline ignore comments ` can be used to ignore findings on a case by case basis. If any issues are found when commiting, a report is printed out, and the commit is halted: .. code-block:: output numpydoc-validation......................................................Failed - hook id: numpydoc-validation - exit code: 1 +----------------------+----------------------+---------+--------------------------------------+ | file | item | check | description | +======================+======================+=========+======================================+ | src/pkg/utils.py:1 | utils | GL08 | The object does not have a docstring | | src/pkg/utils.py:90 | utils.normalize | PR04 | Parameter "field" has no type | | src/pkg/module.py:12 | module.MyClass | GL08 | The object does not have a docstring | | src/pkg/module.py:33 | module.MyClass.parse | RT03 | Return value has no description | +----------------------+----------------------+---------+--------------------------------------+ See :ref:`below ` for a full listing of checks. .. _validation_via_cli: 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. Note that this will honor :ref:`inline ignore comments `, but will not look for any configuration like the :ref:`pre-commit hook ` or :ref:`Sphinx extension ` do. .. _validation_during_sphinx_build: 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 :ref:`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"} In addition, you can exclude any findings on certain objects with ``numpydoc_validation_exclude``, which maps to ``exclude`` in the :ref:`pre-commit hook setup `:: # don't report on objects that match any of these regex numpydoc_validation_exclude = [ '\.undocumented_method$', '\.__repr__$', ] Overrides based on docstring contents are also supported, but the structure is slightly different than the :ref:`pre-commit hook setup `:: numpydoc_validation_overrides = { "SS02": [ # override SS05 to allow docstrings starting with these words '^Process ', '^Assess ', '^Access ', ] } .. _inline_ignore_comments: Ignoring Validation Checks with Inline Comments ----------------------------------------------- Sometimes you only want to ignore a specific check or set of checks for a specific piece of code. This level of fine-tuned control is provided via inline comments: .. code-block:: python class SomeClass: # numpydoc ignore=EX01,SA01,ES01 """This is the docstring for SomeClass.""" def __init__(self): # numpydoc ignore=GL08 pass This is supported by the :ref:`CLI `, :ref:`pre-commit hook `, and :ref:`Sphinx extension `. .. _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 :start-after: start-err-msg :end-before: end-err-msg ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1695687844.3801563 numpydoc-1.6.0/numpydoc/0000755002342000234200000000000014504422244014573 5ustar00jarrodjarrod././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501235.0 numpydoc-1.6.0/numpydoc/__init__.py0000644002342000234200000000043014474143463016712 0ustar00jarrodjarrod""" This package provides the numpydoc Sphinx extension for handling docstrings formatted according to the NumPy documentation format. """ from ._version import __version__ def setup(app, *args, **kwargs): from .numpydoc import setup return setup(app, *args, **kwargs) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501234.0 numpydoc-1.6.0/numpydoc/__main__.py0000644002342000234200000000301614474143462016675 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=1695687301.0 numpydoc-1.6.0/numpydoc/_version.py0000644002342000234200000000002614504421205016763 0ustar00jarrodjarrod__version__ = "1.6.0" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501235.0 numpydoc-1.6.0/numpydoc/docscrape.py0000644002342000234200000005563114474143463017133 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 from functools import cached_property 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( f"potentially wrong underline length... \n{l1} \n{l2} in \n{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) else: # NOTE: param line with single element should never have a # a " :" before the description line, so this should probably # warn. if header.endswith(" :"): header = header[:-2] 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" + _funcname # group for all function names + r"(?P([,]\s+" + _funcnamenext + r")*)" + r")" + r"(?P[,\.])?" # end of "allfuncs" + _description # Some function lists have a trailing comma (or period) '\s*' ) # 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 = {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 # Make UserWarning more descriptive via object introspection. # Skip if introspection fails name = getattr(self._obj, "__name__", None) if name is None: name = getattr(getattr(self._obj, "__class__", None), "__name__", None) if name is not None: msg += f" in the docstring of {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, cached_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, class_doc=ClassDoc, func_doc=FunctionDoc, obj_doc=ObjDoc, ): 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 class_doc(obj, func_doc=func_doc, doc=doc, config=config) elif what in ("function", "method"): return func_doc(obj, doc=doc, config=config) else: if doc is None: doc = pydoc.getdoc(obj) return obj_doc(obj, doc, config=config) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501235.0 numpydoc-1.6.0/numpydoc/docscrape_sphinx.py0000644002342000234200000003555614474143463020530 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 .docscrape import get_doc_object as get_doc_object_orig 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.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: 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 definition lists. 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 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 = {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) def get_doc_object(obj, what=None, doc=None, config=None, builder=None): 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") return get_doc_object_orig( obj, what=what, doc=doc, config=config, class_doc=SphinxClassDoc, func_doc=SphinxFunctionDoc, obj_doc=SphinxObjDoc, ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1695687844.3811562 numpydoc-1.6.0/numpydoc/hooks/0000755002342000234200000000000014504422244015716 5ustar00jarrodjarrod././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501234.0 numpydoc-1.6.0/numpydoc/hooks/__init__.py0000644002342000234200000000004714474143462020040 0ustar00jarrodjarrod"""Pre-commit hooks using numpydoc.""" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501234.0 numpydoc-1.6.0/numpydoc/hooks/utils.py0000644002342000234200000000256114474143462017444 0ustar00jarrodjarrod"""Utility functions for pre-commit hooks.""" import itertools import os from pathlib import Path from typing import Sequence def find_project_root(srcs: Sequence[str]): """ Return a directory containing .git, .hg, pyproject.toml, or setup.cfg. That directory can be one of the directories passed in ``srcs`` or their common parent. If no directory in the tree contains a marker that would specify it's the project root, the root of the file system is returned. Parameters ---------- srcs : Sequence[str] The filepaths to run the hook on. Returns ------- str The project root directory. See Also -------- black.find_project_root : This function was adapted from `Black `_. """ if not srcs: return Path(".").resolve(), "current directory" common_path = Path( os.path.commonpath([Path(src).expanduser().resolve() for src in srcs]) ) for dir in itertools.chain([common_path], common_path.parents): if (dir / "pyproject.toml").is_file(): return dir, "pyproject.toml" if (dir / "setup.cfg").is_file(): return dir, "setup.cfg" if (dir / ".git").exists() or (dir / ".hg").is_dir(): return dir, "version control" return dir, "file system root" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501234.0 numpydoc-1.6.0/numpydoc/hooks/validate_docstrings.py0000644002342000234200000003142314474143462022333 0ustar00jarrodjarrod"""Run numpydoc validation on contents of a file.""" import argparse import ast import configparser import os import re import sys try: import tomllib except ImportError: import tomli as tomllib from pathlib import Path from typing import Sequence, Tuple, Union from tabulate import tabulate from .. import docscrape, validate from .utils import find_project_root class AstValidator(validate.Validator): """ Overrides the :class:`Validator` to work entirely with the AST. Parameters ---------- ast_node : ast.AST The node under inspection. filename : os.PathLike The file where the node is defined. obj_name : str A name for the node to use in the listing of issues for the file as a whole. """ def __init__( self, *, ast_node: ast.AST, filename: os.PathLike, obj_name: str ) -> None: self.node: ast.AST = ast_node self.raw_doc: str = ast.get_docstring(self.node, clean=False) or "" self.clean_doc: str = ast.get_docstring(self.node, clean=True) self.doc: docscrape.NumpyDocString = docscrape.NumpyDocString(self.raw_doc) self._source_file: os.PathLike = Path(filename).resolve() self._name: str = obj_name self.is_class: bool = isinstance(ast_node, ast.ClassDef) self.is_module: bool = isinstance(ast_node, ast.Module) @staticmethod def _load_obj(name): raise NotImplementedError("AstValidator does not support this method.") @property def name(self) -> str: return self._name @property def is_function_or_method(self) -> bool: return isinstance(self.node, (ast.FunctionDef, ast.AsyncFunctionDef)) @property def is_generator_function(self) -> bool: if not self.is_function_or_method: return False for child in ast.iter_child_nodes(self.node): if isinstance(child, ast.Expr) and isinstance(child.value, ast.Yield): return True return False @property def type(self) -> str: if self.is_function_or_method: return "function" if self.is_class: return "type" if self.is_module: return "module" raise ValueError("Unknown type.") @property def source_file_name(self) -> str: return self._source_file @property def source_file_def_line(self) -> int: return self.node.lineno if not self.is_module else 1 @property def signature_parameters(self) -> Tuple[str]: def extract_signature(node): args_node = node.args params = [] for arg_type in ["posonlyargs", "args", "vararg", "kwonlyargs", "kwarg"]: entries = getattr(args_node, arg_type) if arg_type in ["vararg", "kwarg"]: if entries and arg_type == "vararg": params.append(f"*{entries.arg}") if entries and arg_type == "kwarg": params.append(f"**{entries.arg}") else: params.extend([arg.arg for arg in entries]) params = tuple(params) if params and params[0] in {"self", "cls"}: return params[1:] return params params = tuple() if self.is_function_or_method: params = extract_signature(self.node) elif self.is_class: for child in self.node.body: if isinstance(child, ast.FunctionDef) and child.name == "__init__": params = extract_signature(child) return params @property def method_source(self) -> str: with open(self.source_file_name) as file: source = ast.get_source_segment(file.read(), self.node) return source class DocstringVisitor(ast.NodeVisitor): """ Visits nodes in the AST from a given module and reporting numpydoc issues. Parameters ---------- filepath : str The absolute or relative path to the file to inspect. config : dict Configuration options for reviewing flagged issues. """ def __init__( self, filepath: str, config: dict, ) -> None: self.config: dict = config self.filepath: str = filepath self.module_name: str = Path(self.filepath).stem self.stack: list[str] = [] self.findings: list = [] def _ignore_issue(self, node: ast.AST, check: str) -> bool: """ Check whether the issue should be ignored. Parameters ---------- node : ast.AST The node under inspection. check : str The code for the check being evaluated. Return ------ bool Whether the issue should be excluded from the report. """ if check not in self.config["checks"]: return True if self.config["overrides"]: try: pattern = self.config["overrides"][check] if re.search(pattern, ast.get_docstring(node)) is not None: return True except KeyError: pass return False def _get_numpydoc_issues(self, node: ast.AST) -> None: """ Get numpydoc validation issues. Parameters ---------- node : ast.AST The node under inspection. """ name = ".".join(self.stack) report = validate.validate( name, AstValidator, ast_node=node, filename=self.filepath ) self.findings.extend( [ [f'{self.filepath}:{report["file_line"]}', name, check, description] for check, description in report["errors"] if not self._ignore_issue(node, check) ] ) def visit(self, node: ast.AST) -> None: """ Visit a node in the AST and report on numpydoc validation issues. Parameters ---------- node : ast.AST The node to visit. """ if isinstance( node, (ast.Module, ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef) ): self.stack.append( self.module_name if isinstance(node, ast.Module) else node.name ) if not ( self.config["exclude"] and re.search(self.config["exclude"], ".".join(self.stack)) ): self._get_numpydoc_issues(node) self.generic_visit(node) _ = self.stack.pop() def parse_config(dir_path: os.PathLike = None) -> dict: """ Parse config information from a pyproject.toml or setup.cfg file. This function looks in the provided directory path first for a pyproject.toml file. If it finds that, it won't look for a setup.cfg file. Parameters ---------- dir_path : os.PathLike An absolute or relative path to a directory containing either a pyproject.toml file specifying a [tool.numpydoc_validation] section or a setup.cfg file specifying a [tool:numpydoc_validation] section. For example, ``~/my_project``. If not provided, the hook will try to find the project root directory. Returns ------- dict Config options for the numpydoc validation hook. """ options = {"checks": {"all"}, "exclude": set(), "overrides": {}} dir_path = Path(dir_path).expanduser().resolve() toml_path = dir_path / "pyproject.toml" cfg_path = dir_path / "setup.cfg" def compile_regex(expressions): return ( re.compile(r"|".join(exp for exp in expressions if exp)) if expressions else None ) def extract_check_overrides(options, config_items): for option, value in config_items: if option.startswith("override_"): _, check = option.split("_") if value: options["overrides"][check.upper()] = compile_regex(value) if toml_path.is_file(): with open(toml_path, "rb") as toml_file: pyproject_toml = tomllib.load(toml_file) config = pyproject_toml.get("tool", {}).get("numpydoc_validation", {}) options["checks"] = set(config.get("checks", options["checks"])) global_exclusions = config.get("exclude", options["exclude"]) options["exclude"] = set( global_exclusions if not isinstance(global_exclusions, str) else [global_exclusions] ) extract_check_overrides(options, config.items()) elif cfg_path.is_file(): config = configparser.ConfigParser() config.read(cfg_path) numpydoc_validation_config_section = "tool:numpydoc_validation" try: try: options["checks"] = set( config.get(numpydoc_validation_config_section, "checks") .rstrip(",") .split(",") or options["checks"] ) except configparser.NoOptionError: pass try: options["exclude"] = set( config.get(numpydoc_validation_config_section, "exclude") .rstrip(",") .split(",") or options["exclude"] ) except configparser.NoOptionError: pass extract_check_overrides( options, config.items(numpydoc_validation_config_section) ) except configparser.NoSectionError: pass options["checks"] = validate.get_validation_checks(options["checks"]) options["exclude"] = compile_regex(options["exclude"]) return options def process_file(filepath: os.PathLike, config: dict) -> "list[list[str]]": """ Run numpydoc validation on a file. Parameters ---------- filepath : path-like The absolute or relative path to the file to inspect. config : dict Configuration options for reviewing flagged issues. Returns ------- list[list[str]] A list of [name, check, description] lists for flagged issues. """ with open(filepath) as file: module_node = ast.parse(file.read(), filepath) docstring_visitor = DocstringVisitor(filepath=str(filepath), config=config) docstring_visitor.visit(module_node) return docstring_visitor.findings def main(argv: Union[Sequence[str], None] = None) -> int: """Run the numpydoc validation hook.""" project_root_from_cwd, config_file = find_project_root(["."]) config_options = parse_config(project_root_from_cwd) ignored_checks = ( "\n " + "\n ".join( [ f"- {check}: {validate.ERROR_MSGS[check]}" for check in set(validate.ERROR_MSGS.keys()) - config_options["checks"] ] ) + "\n" ) parser = argparse.ArgumentParser( description="Run numpydoc validation on files with option to ignore individual checks.", formatter_class=argparse.RawTextHelpFormatter, ) parser.add_argument( "files", type=str, nargs="+", help="File(s) to run numpydoc validation on." ) parser.add_argument( "--config", type=str, help=( "Path to a directory containing a pyproject.toml or setup.cfg file.\n" "The hook will look for it in the root project directory.\n" "If both are present, only pyproject.toml will be used.\n" "Options must be placed under\n" " - [tool:numpydoc_validation] for setup.cfg files and\n" " - [tool.numpydoc_validation] for pyproject.toml files." ), ) parser.add_argument( "--ignore", type=str, nargs="*", help=( f"""Check codes to ignore.{ ' Currently ignoring the following from ' f'{Path(project_root_from_cwd) / config_file}: {ignored_checks}' 'Values provided here will be in addition to the above, unless an alternate config is provided.' if config_options["checks"] else '' }""" ), ) args = parser.parse_args(argv) project_root, _ = find_project_root(args.files) config_options = parse_config(args.config or project_root) config_options["checks"] -= set(args.ignore or []) findings = [] for file in args.files: findings.extend(process_file(file, config_options)) if findings: print( tabulate( findings, headers=["file", "item", "check", "description"], tablefmt="grid", maxcolwidths=50, ), file=sys.stderr, ) return 1 return 0 if __name__ == "__main__": raise SystemExit(main()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1695679785.0 numpydoc-1.6.0/numpydoc/numpydoc.py0000644002342000234200000004215314504402451017006 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, inline import sphinx from sphinx.addnodes import pending_xref, desc_content from sphinx.util import logging from sphinx.errors import ExtensionError if sphinx.__version__ < "5": raise RuntimeError("Sphinx 5 or newer is required") from .docscrape_sphinx import get_doc_object from .validate import validate, ERROR_MSGS, get_validation_checks from .xref import DEFAULT_LINKS from . import __version__ logger = logging.getLogger(__name__) HASH_LEN = 12 def _traverse_or_findall(node, condition, **kwargs): """Triage node.traverse (docutils <0.18.1) vs node.findall. TODO: This check can be removed when the minimum supported docutils version for numpydoc is docutils>=0.18.1 """ return ( node.findall(condition, **kwargs) if hasattr(node, "findall") else node.traverse(condition, **kwargs) ) 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( _traverse_or_findall( section_node, 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 _traverse_or_findall(doc, 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 _traverse_or_findall(ref.parent, 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 _traverse_or_findall(doc, reference, descend=True): for id_ in ref["ids"]: known_ref_ids.add(id_) # some extensions produce backrefs to inline elements for ref in _traverse_or_findall(doc, inline, descend=True): for id_ in ref["ids"]: known_ref_ids.add(id_) for citation_node in _traverse_or_findall(doc, 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 show_inherited_class_members = app.config.numpydoc_show_inherited_class_members if isinstance(show_inherited_class_members, dict): try: show_inherited_class_members = show_inherited_class_members[name] except KeyError: show_inherited_class_members = True cfg = { "use_plots": app.config.numpydoc_use_plots, "show_class_members": app.config.numpydoc_show_class_members, "show_inherited_class_members": 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. report = validate(doc) errors = [ err for err in report["errors"] if not ( ( overrides := app.config.numpydoc_validation_overrides.get( err[0] ) ) and re.search(overrides, report["docstring"]) ) ] 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) # 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") app.connect("config-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_use_plots", None, False) app.add_config_value("numpydoc_show_class_members", True, True) app.add_config_value( "numpydoc_show_inherited_class_members", True, True, types=(bool, dict) ) 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) app.add_config_value("numpydoc_validation_overrides", dict(), 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 config.numpydoc_validation_checks = get_validation_checks( config.numpydoc_validation_checks ) # 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 for check, patterns in config.numpydoc_validation_overrides.items(): config.numpydoc_validation_overrides[check] = re.compile( r"|".join(exp for exp in patterns) ) # ------------------------------------------------------------------------------ # 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=1695687844.3811562 numpydoc-1.6.0/numpydoc/templates/0000755002342000234200000000000014504422244016571 5ustar00jarrodjarrod././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501234.0 numpydoc-1.6.0/numpydoc/templates/numpydoc_docstring.rst0000644002342000234200000000034314474143462023245 0ustar00jarrodjarrod{{index}} {{summary}} {{extended_summary}} {{parameters}} {{returns}} {{yields}} {{receives}} {{other_parameters}} {{raises}} {{warns}} {{warnings}} {{see_also}} {{notes}} {{references}} {{examples}} {{attributes}} {{methods}} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1695687844.3811562 numpydoc-1.6.0/numpydoc/tests/0000755002342000234200000000000014504422244015735 5ustar00jarrodjarrod././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1695687844.3811562 numpydoc-1.6.0/numpydoc/tests/hooks/0000755002342000234200000000000014504422244017060 5ustar00jarrodjarrod././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501234.0 numpydoc-1.6.0/numpydoc/tests/hooks/__init__.py0000644002342000234200000000002714474143462021200 0ustar00jarrodjarrod"""Tests for hooks.""" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501234.0 numpydoc-1.6.0/numpydoc/tests/hooks/example_module.py0000644002342000234200000000101414474143462022436 0ustar00jarrodjarrod"""Test module for hook.""" # numpydoc ignore=ES01,SA01 def some_function(name): """Welcome to some function.""" pass class MyClass: """This is MyClass.""" def __init__(self): pass def __repr__(self): # numpydoc ignore=GL08 pass def do_something(self, *args, **kwargs): """ Do something. Parameters ---------- *args """ pass def process(self): """Process stuff.""" pass class NewClass: pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501234.0 numpydoc-1.6.0/numpydoc/tests/hooks/test_utils.py0000644002342000234200000000200314474143462021634 0ustar00jarrodjarrod"""Test utility functions for hooks.""" from pathlib import Path import pytest from numpydoc.hooks import utils @pytest.mark.parametrize( ["reason_file", "files", "expected_reason"], [ (None, None, "current directory"), (None, ["x.py"], "file system root"), (".git", ["x.py"], "version control"), ("pyproject.toml", ["x.py"], "pyproject.toml"), ("setup.cfg", ["x.py"], "setup.cfg"), ], ) def test_find_project_root(tmp_path, request, reason_file, files, expected_reason): """Test the process of finding the project root.""" if reason_file: (tmp_path / reason_file).touch() if files: expected_dir = Path("/") if expected_reason == "file system root" else tmp_path for file in files: (tmp_path / file).touch() else: expected_dir = request.config.rootdir root_dir, reason = utils.find_project_root(files if not files else [tmp_path]) assert reason == expected_reason assert root_dir == expected_dir ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501234.0 numpydoc-1.6.0/numpydoc/tests/hooks/test_validate_hook.py0000644002342000234200000004667014474143462023327 0ustar00jarrodjarrod"""Test the numpydoc validate pre-commit hook.""" import inspect from pathlib import Path import pytest from numpydoc.hooks.validate_docstrings import main @pytest.fixture def example_module(request): fullpath = ( Path(request.config.rootdir) / "numpydoc" / "tests" / "hooks" / "example_module.py" ) return str(fullpath.relative_to(request.config.rootdir)) @pytest.mark.parametrize("config", [None, "fake_dir"]) def test_validate_hook(example_module, config, capsys): """Test that a file is correctly processed in the absence of config files.""" expected = inspect.cleandoc( """ +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | file | item | check | description | +===========================================+=====================================+=========+====================================================+ | numpydoc/tests/hooks/example_module.py:1 | example_module | EX01 | No examples section found | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:4 | example_module.some_function | ES01 | No extended summary found | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:4 | example_module.some_function | PR01 | Parameters {'name'} not documented | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:4 | example_module.some_function | SA01 | See Also section not found | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:4 | example_module.some_function | EX01 | No examples section found | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:9 | example_module.MyClass | ES01 | No extended summary found | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:9 | example_module.MyClass | SA01 | See Also section not found | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:9 | example_module.MyClass | EX01 | No examples section found | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:12 | example_module.MyClass.__init__ | GL08 | The object does not have a docstring | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:18 | example_module.MyClass.do_something | ES01 | No extended summary found | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:18 | example_module.MyClass.do_something | PR01 | Parameters {'**kwargs'} not documented | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:18 | example_module.MyClass.do_something | PR07 | Parameter "*args" has no description | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:18 | example_module.MyClass.do_something | SA01 | See Also section not found | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:18 | example_module.MyClass.do_something | EX01 | No examples section found | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:28 | example_module.MyClass.process | SS05 | Summary must start with infinitive verb, not third | | | | | person (e.g. use "Generate" instead of | | | | | "Generates") | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:28 | example_module.MyClass.process | ES01 | No extended summary found | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:28 | example_module.MyClass.process | SA01 | See Also section not found | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:28 | example_module.MyClass.process | EX01 | No examples section found | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:33 | example_module.NewClass | GL08 | The object does not have a docstring | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ """ ) args = [example_module] if config: args.append(f"--{config=}") return_code = main(args) assert return_code == 1 assert capsys.readouterr().err.rstrip() == expected def test_validate_hook_with_ignore(example_module, capsys): """ Test that a file is correctly processed in the absence of config files with command line ignore options. """ expected = inspect.cleandoc( """ +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | file | item | check | description | +===========================================+=====================================+=========+====================================================+ | numpydoc/tests/hooks/example_module.py:4 | example_module.some_function | PR01 | Parameters {'name'} not documented | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:12 | example_module.MyClass.__init__ | GL08 | The object does not have a docstring | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:18 | example_module.MyClass.do_something | PR01 | Parameters {'**kwargs'} not documented | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:18 | example_module.MyClass.do_something | PR07 | Parameter "*args" has no description | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:28 | example_module.MyClass.process | SS05 | Summary must start with infinitive verb, not third | | | | | person (e.g. use "Generate" instead of | | | | | "Generates") | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ | numpydoc/tests/hooks/example_module.py:33 | example_module.NewClass | GL08 | The object does not have a docstring | +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ """ ) return_code = main([example_module, "--ignore", "ES01", "SA01", "EX01"]) assert return_code == 1 assert capsys.readouterr().err.rstrip() == expected def test_validate_hook_with_toml_config(example_module, tmp_path, capsys): """ Test that a file is correctly processed with the config coming from a pyproject.toml file. """ with open(tmp_path / "pyproject.toml", "w") as config_file: config_file.write( inspect.cleandoc( """ [tool.numpydoc_validation] checks = [ "all", "EX01", "SA01", "ES01", ] exclude = '\\.__init__$' override_SS05 = [ '^Process', '^Assess', '^Access', ] """ ) ) expected = inspect.cleandoc( """ +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ | file | item | check | description | +===========================================+=====================================+=========+========================================+ | numpydoc/tests/hooks/example_module.py:4 | example_module.some_function | PR01 | Parameters {'name'} not documented | +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ | numpydoc/tests/hooks/example_module.py:18 | example_module.MyClass.do_something | PR01 | Parameters {'**kwargs'} not documented | +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ | numpydoc/tests/hooks/example_module.py:18 | example_module.MyClass.do_something | PR07 | Parameter "*args" has no description | +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ | numpydoc/tests/hooks/example_module.py:33 | example_module.NewClass | GL08 | The object does not have a docstring | +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ """ ) return_code = main([example_module, "--config", str(tmp_path)]) assert return_code == 1 assert capsys.readouterr().err.rstrip() == expected def test_validate_hook_with_setup_cfg(example_module, tmp_path, capsys): """ Test that a file is correctly processed with the config coming from a setup.cfg file. """ with open(tmp_path / "setup.cfg", "w") as config_file: config_file.write( inspect.cleandoc( """ [tool:numpydoc_validation] checks = all,EX01,SA01,ES01 exclude = \\.__init__$ override_SS05 = ^Process,^Assess,^Access """ ) ) expected = inspect.cleandoc( """ +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ | file | item | check | description | +===========================================+=====================================+=========+========================================+ | numpydoc/tests/hooks/example_module.py:4 | example_module.some_function | PR01 | Parameters {'name'} not documented | +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ | numpydoc/tests/hooks/example_module.py:18 | example_module.MyClass.do_something | PR01 | Parameters {'**kwargs'} not documented | +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ | numpydoc/tests/hooks/example_module.py:18 | example_module.MyClass.do_something | PR07 | Parameter "*args" has no description | +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ | numpydoc/tests/hooks/example_module.py:33 | example_module.NewClass | GL08 | The object does not have a docstring | +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ """ ) return_code = main([example_module, "--config", str(tmp_path)]) assert return_code == 1 assert capsys.readouterr().err.rstrip() == expected def test_validate_hook_help(capsys): """Test that help section is displaying.""" with pytest.raises(SystemExit): return_code = main(["--help"]) assert return_code == 0 out = capsys.readouterr().out assert "--ignore" in out assert "--config" in out def test_validate_hook_exclude_option_pyproject(example_module, tmp_path, capsys): """ Test that a file is correctly processed with the config coming from a pyproject.toml file and exclusions provided. """ with open(tmp_path / "pyproject.toml", "w") as config_file: config_file.write( inspect.cleandoc( r""" [tool.numpydoc_validation] checks = [ "all", "EX01", "SA01", "ES01", ] exclude = [ '\.do_something$', '\.__init__$', ] override_SS05 = [ '^Process', '^Assess', '^Access', ] """ ) ) expected = inspect.cleandoc( """ +-------------------------------------------+------------------------------+---------+--------------------------------------+ | file | item | check | description | +===========================================+==============================+=========+======================================+ | numpydoc/tests/hooks/example_module.py:4 | example_module.some_function | PR01 | Parameters {'name'} not documented | +-------------------------------------------+------------------------------+---------+--------------------------------------+ | numpydoc/tests/hooks/example_module.py:33 | example_module.NewClass | GL08 | The object does not have a docstring | +-------------------------------------------+------------------------------+---------+--------------------------------------+ """ ) return_code = main([example_module, "--config", str(tmp_path)]) assert return_code == 1 assert capsys.readouterr().err.rstrip() == expected def test_validate_hook_exclude_option_setup_cfg(example_module, tmp_path, capsys): """ Test that a file is correctly processed with the config coming from a setup.cfg file and exclusions provided. """ with open(tmp_path / "setup.cfg", "w") as config_file: config_file.write( inspect.cleandoc( """ [tool:numpydoc_validation] checks = all,EX01,SA01,ES01 exclude = \\.NewClass$,\\.__init__$ override_SS05 = ^Process,^Assess,^Access """ ) ) expected = inspect.cleandoc( """ +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ | file | item | check | description | +===========================================+=====================================+=========+========================================+ | numpydoc/tests/hooks/example_module.py:4 | example_module.some_function | PR01 | Parameters {'name'} not documented | +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ | numpydoc/tests/hooks/example_module.py:18 | example_module.MyClass.do_something | PR01 | Parameters {'**kwargs'} not documented | +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ | numpydoc/tests/hooks/example_module.py:18 | example_module.MyClass.do_something | PR07 | Parameter "*args" has no description | +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ """ ) return_code = main([example_module, "--config", str(tmp_path)]) assert return_code == 1 assert capsys.readouterr().err.rstrip() == expected ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501234.0 numpydoc-1.6.0/numpydoc/tests/test_docscrape.py0000644002342000234200000011002214474143462021315 0ustar00jarrodjarrodfrom 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_returns_with_roles_no_names(): """Make sure colons that are part of sphinx roles are not misinterpreted as type separator in returns section. See gh-428.""" docstring = NumpyDocString( """ Returns ------- str or :class:`NumpyDocString` """ ) expected = "str or :class:`NumpyDocString`" # not "str or : class:... assert docstring["Returns"][0].type == expected assert expected in str(docstring) 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_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() self.numpydoc_validation_overrides = dict() 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) def test__error_location_no_name_attr(): """ Ensure that NumpyDocString._error_location doesn't fail when self._obj does not have a __name__ attr. See gh-362 """ from collections.abc import Callable # Create a Callable that doesn't have a __name__ attribute class Foo: def __call__(self): pass foo = Foo() # foo is a Callable, but no a function instance assert isinstance(foo, Callable) # Create an NumpyDocString instance to call the _error_location method nds = get_doc_object(foo) msg = "Potentially wrong underline length.*Foo.*" with pytest.raises(ValueError, match=msg): nds._error_location(msg=msg) def test_class_docstring_cached_property(): """Ensure that properties marked with the `cached_property` decorator are listed in the Methods section. See gh-432.""" from functools import cached_property class Foo: _x = [1, 2, 3] @cached_property def val(self): return self._x class_docstring = get_doc_object(Foo) assert len(class_docstring["Attributes"]) == 1 assert class_docstring["Attributes"][0].name == "val" if __name__ == "__main__": import pytest pytest.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501234.0 numpydoc-1.6.0/numpydoc/tests/test_full.py0000644002342000234200000000726114474143462020326 0ustar00jarrodjarrodimport os.path as op import re import shutil from packaging import version import pytest import sphinx from sphinx.application import Sphinx from sphinx.util.docutils import docutils_namespace from docutils import __version__ as docutils_version # 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) 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) 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) 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)) as fid: html = fid.read() # TODO: This check can be removed when the minimum supported docutils version # for numpydoc is docutils>=0.18 pattern = ( 'role="doc-backlink"' if version.parse(docutils_version) >= version.parse("0.18") else 'class="fn-backref"' ) reference_list = re.findall(rf'(.*)<\/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=1693501235.0 numpydoc-1.6.0/numpydoc/tests/test_main.py0000644002342000234200000000541414474143463020307 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=1695679785.0 numpydoc-1.6.0/numpydoc/tests/test_numpydoc.py0000644002342000234200000002232314504402451021204 0ustar00jarrodjarrodimport pytest from collections import defaultdict from io import StringIO from pathlib import PosixPath from copy import deepcopy from docutils import nodes from numpydoc.numpydoc import ( mangle_docstrings, _clean_text_signature, update_config, clean_backrefs, ) from numpydoc.xref import DEFAULT_LINKS from sphinx.ext.autodoc import ALL from sphinx.util import logging class MockConfig: numpydoc_use_plots = False 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_citation_re = "[a-z0-9_.-]+" numpydoc_attributes_as_param_list = True numpydoc_validation_checks = set() numpydoc_validation_exclude = set() numpydoc_validation_overrides = dict() 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_basic(): 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_mangle_docstrings_inherited_class_members(): # if subclass docs are rendered, this PosixPath should have Path.samefile p = """ A top section before .. autoclass:: pathlib.PosixPath """ lines = p.split("\n") app = MockApp() mangle_docstrings(app, "class", "pathlib.PosixPath", PosixPath, {}, lines) lines = [x.strip() for x in lines] assert "samefile" in lines app.config.numpydoc_show_inherited_class_members = False lines = p.split("\n") mangle_docstrings(app, "class", "pathlib.PosixPath", PosixPath, {}, lines) lines = [x.strip() for x in lines] assert "samefile" not in lines app.config.numpydoc_show_inherited_class_members = dict() lines = p.split("\n") mangle_docstrings(app, "class", "pathlib.PosixPath", PosixPath, {}, lines) lines = [x.strip() for x in lines] assert "samefile" in lines app.config.numpydoc_show_inherited_class_members = defaultdict(lambda: False) lines = p.split("\n") mangle_docstrings(app, "class", "pathlib.PosixPath", PosixPath, {}, lines) lines = [x.strip() for x in lines] assert "samefile" not 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 ({"SA01", "EX01"}, ("SA01", "EX01"), []), # Validation on with only one activated check ({"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() == "" @pytest.mark.parametrize("overrides", [{}, {"SS02"}, {"SS02", "SS03"}]) def test_mangle_docstrings_overrides(overrides): def process_something_noop_function(): """Process something.""" app = MockApp() app.config.numpydoc_validation_checks = {"all"} app.config.numpydoc_validation_overrides = { check: [r"^Process "] # overrides are regex on docstring content for check in overrides } update_config(app) # Setup for catching warnings status, warning = StringIO(), StringIO() logging.setup(app, status, warning) # Run mangle docstrings on process_something_noop_function mangle_docstrings( app, "function", process_something_noop_function.__name__, process_something_noop_function, None, process_something_noop_function.__doc__.split("\n"), ) findings = warning.getvalue() assert " EX01: " in findings # should always be there for check in overrides: assert f" {check}: " not in findings 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=r"\['shouldnt-be-a-str'\]"): update_config(app) def test_clean_backrefs(): """Check ids are not cleaned from inline backrefs.""" par = nodes.paragraph(rawsource="", text="") inline_ref = nodes.inline(rawsource="", text="", ids=["id1"]) inline_ref += nodes.reference(rawsource="", text="[1]", refid="r123-1") citation = nodes.citation( rawsource="", docname="index", backrefs=["id1"], ids=["r123-1"] ) citation += nodes.label("1") citation += nodes.paragraph(rawsource="", text="Author. Title.") par += inline_ref par += citation clean_backrefs(app=MockApp(), doc=par, docname="index") assert "id1" in citation["backrefs"] if __name__ == "__main__": import pytest pytest.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1695679785.0 numpydoc-1.6.0/numpydoc/tests/test_validate.py0000644002342000234200000012364414504402451021147 0ustar00jarrodjarrodimport pytest import sys import warnings from functools import cached_property from inspect import getsourcelines, getsourcefile from numpydoc import validate import numpydoc.tests validate_one = validate.validate ALL_CHECKS = set(validate.ERROR_MSGS.keys()) @pytest.mark.parametrize( ["checks", "expected"], [ [{"all"}, ALL_CHECKS], [set(), set()], [{"EX01"}, {"EX01"}], [{"EX01", "SA01"}, {"EX01", "SA01"}], [{"all", "EX01", "SA01"}, ALL_CHECKS - {"EX01", "SA01"}], [{"all", "PR01"}, ALL_CHECKS - {"PR01"}], ], ) def test_utils_get_validation_checks(checks, expected): """Ensure check selection is working.""" assert validate.get_validation_checks(checks) == expected @pytest.mark.parametrize( "checks", [ {"every"}, {None}, {"SM10"}, {"EX01", "SM10"}, ], ) def test_get_validation_checks_validity(checks): """Ensure that invalid checks are flagged.""" with pytest.raises(ValueError, match="Unrecognized validation code"): _ = validate.get_validation_checks(checks) class _DummyList(list): """Dummy list class to test validation.""" def test_no_file(): """Test that validation can be done on functions made on the fly.""" # Just a smoke test for now, will have a None filename validate.validate("numpydoc.tests.test_validate._DummyList.clear") @pytest.mark.parametrize( ["file_contents", "expected"], [ ["class MyClass:\n pass", {}], ["class MyClass: # numpydoc ignore=EX01\n pass", {1: ["EX01"]}], [ "class MyClass: # numpydoc ignore= EX01,SA01\n pass", {1: ["EX01", "SA01"]}, ], [ "class MyClass:\n def my_method(): # numpydoc ignore:EX01\n pass", {2: ["EX01"]}, ], [ "class MyClass:\n def my_method(): # numpydoc ignore: EX01,PR01\n pass", {2: ["EX01", "PR01"]}, ], [ "class MyClass: # numpydoc ignore=GL08\n def my_method(): # numpydoc ignore:EX01,PR01\n pass", {1: ["GL08"], 2: ["EX01", "PR01"]}, ], ], ) def test_extract_ignore_validation_comments(tmp_path, file_contents, expected): """Test that extraction of validation ignore comments is working.""" filepath = tmp_path / "ignore_comments.py" with open(filepath, "w") as file: file.write(file_contents) assert validate.extract_ignore_validation_comments(filepath) == expected 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 """ def parameters_with_trailing_underscores(self, str_): r""" Ensure PR01 and PR02 errors are not raised with trailing underscores. Parameters with trailing underscores need to be escaped to render properly in the documentation since trailing underscores are used to create links. Doing so without also handling the change in the validation logic makes it impossible to both pass validation and render correctly. Parameters ---------- str\_ : str Some text. See Also -------- related : Something related. Examples -------- >>> result = 1 + 1 """ pass def parameter_with_wrong_types_as_substrings(self, a, b, c, d, e, f): r""" Ensure PR06 doesn't fail when non-preferable types are substrings. While PR06 checks for parameter types which contain non-preferable type names like integer (int), string (str), and boolean (bool), PR06 should not fail if those types are used only as susbtrings in, for example, custom type names. Parameters ---------- a : Myint Some text. b : intClass Some text. c : Mystring Some text. d : stringClass Some text. e : Mybool Some text. f : boolClass Some text. See Also -------- related : Something related. Examples -------- >>> result = 1 + 1 """ pass 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", "parameters_with_trailing_underscores", "parameter_with_wrong_types_as_substrings", ], ) 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 warnings.catch_warnings(record=True) 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"]) def decorator(x): """Test decorator.""" return x @decorator @decorator class DecoratorClass: """ Class and methods with decorators. * `DecoratorClass` has two decorators. * `DecoratorClass.test_no_decorator` has no decorator. * `DecoratorClass.test_property` has a `@property` decorator. * `DecoratorClass.test_cached_property` has a `@cached_property` decorator. * `DecoratorClass.test_three_decorators` has three decorators. `Validator.source_file_def_line` should return the `def` or `class` line number, not the line of the first decorator. """ def test_no_decorator(self): """Test method without decorators.""" pass @property def test_property(self): """Test property method.""" pass @cached_property def test_cached_property(self): """Test property method.""" pass @decorator @decorator @decorator def test_three_decorators(self): """Test method with three decorators.""" pass 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) # inspect.getsourcelines does not return class decorators for Python 3.8. This was # fixed starting with 3.9: https://github.com/python/cpython/issues/60060. @pytest.mark.parametrize( ["decorated_obj", "def_line"], [ [ "numpydoc.tests.test_validate.DecoratorClass", getsourcelines(DecoratorClass)[-1] + (2 if sys.version_info.minor > 8 else 0), ], [ "numpydoc.tests.test_validate.DecoratorClass.test_no_decorator", getsourcelines(DecoratorClass.test_no_decorator)[-1], ], [ "numpydoc.tests.test_validate.DecoratorClass.test_property", getsourcelines(DecoratorClass.test_property.fget)[-1] + 1, ], [ "numpydoc.tests.test_validate.DecoratorClass.test_cached_property", getsourcelines(DecoratorClass.test_cached_property.func)[-1] + 1, ], [ "numpydoc.tests.test_validate.DecoratorClass.test_three_decorators", getsourcelines(DecoratorClass.test_three_decorators)[-1] + 3, ], ], ) def test_source_file_def_line_with_decorators(self, decorated_obj, def_line): doc = numpydoc.validate.Validator( numpydoc.docscrape.get_doc_object( numpydoc.validate.Validator._load_obj(decorated_obj) ) ) assert doc.source_file_def_line == def_line @pytest.mark.parametrize( ["property", "file_name"], [ [ "numpydoc.tests.test_validate.DecoratorClass.test_property", getsourcefile(DecoratorClass.test_property.fget), ], [ "numpydoc.tests.test_validate.DecoratorClass.test_cached_property", getsourcefile(DecoratorClass.test_cached_property.func), ], ], ) def test_source_file_name_with_properties(self, property, file_name): doc = numpydoc.validate.Validator( numpydoc.docscrape.get_doc_object( numpydoc.validate.Validator._load_obj(property) ) ) assert doc.source_file_name == file_name ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501234.0 numpydoc-1.6.0/numpydoc/tests/test_xref.py0000644002342000234200000001421514474143462020325 0ustar00jarrodjarrodimport 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=1695687844.3811562 numpydoc-1.6.0/numpydoc/tests/tinybuild/0000755002342000234200000000000014504422244017740 5ustar00jarrodjarrod././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501234.0 numpydoc-1.6.0/numpydoc/tests/tinybuild/Makefile0000644002342000234200000000037114474143462021411 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=1693501234.0 numpydoc-1.6.0/numpydoc/tests/tinybuild/conf.py0000644002342000234200000000123614474143462021251 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=1693501234.0 numpydoc-1.6.0/numpydoc/tests/tinybuild/index.rst0000644002342000234200000000022214474143462021605 0ustar00jarrodjarrodnumpydoc_test_module ==================== .. automodule:: numpydoc_test_module :no-members: :no-inherited-members: :no-special-members: ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501234.0 numpydoc-1.6.0/numpydoc/tests/tinybuild/numpydoc_test_module.py0000644002342000234200000000163514474143462024571 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=1695679785.0 numpydoc-1.6.0/numpydoc/validate.py0000644002342000234200000006323014504402451016740 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. """ from copy import deepcopy from typing import Dict, List, Set, Optional import ast import collections import functools import importlib import inspect import os import pydoc import re import textwrap import tokenize 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", ] # NOTE: The following comment is a sentinel for embedding in the docs - do not # modify/remove # start-err-msg 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", } # end-err-msg # NOTE: The above comment is a sentinel for embedding in the docs - do not # modify/remove # Ignore these when evaluating end-of-line-"." checks IGNORE_STARTS = (" ", "* ", "- ") # inline comments that can suppress individual checks per line IGNORE_COMMENT_PATTERN = re.compile("(?:.* numpydoc ignore[=|:] ?)(.+)") # This function gets called once per function/method to be validated. # We have to balance memory usage with performance here. It shouldn't be too # bad to store these `dict`s (they should be rare), but to be safe let's keep # the limit low-ish. This was set by looking at scipy, numpy, matplotlib, # and pandas and they had between ~500 and ~1300 .py files as of 2023-08-16. @functools.lru_cache(maxsize=2000) def extract_ignore_validation_comments( filepath: Optional[os.PathLike], ) -> Dict[int, List[str]]: """ Extract inline comments indicating certain validation checks should be ignored. Parameters ---------- filepath : os.PathLike Path to the file being inspected. Returns ------- dict[int, list[str]] Mapping of line number to a list of checks to ignore. """ numpydoc_ignore_comments = {} try: file = open(filepath) except (OSError, TypeError): # can be None, nonexistent, or unreadable return numpydoc_ignore_comments with file: last_declaration = 1 declarations = ["def", "class"] for token in tokenize.generate_tokens(file.readline): if token.type == tokenize.NAME and token.string in declarations: last_declaration = token.start[0] if token.type == tokenize.COMMENT: match = re.match(IGNORE_COMMENT_PATTERN, token.string) if match: rules = match.group(1).split(",") numpydoc_ignore_comments[last_declaration] = rules return numpydoc_ignore_comments def get_validation_checks(validation_checks: Set[str]) -> Set[str]: """ Get the set of validation checks to report on. Parameters ---------- validation_checks : set[str] A set of validation checks to report on. If the set is ``{"all"}``, all checks will be reported. If the set contains just specific checks, only those will be reported on. If the set contains both ``"all"`` and specific checks, all checks except those included in the set will be reported on. Returns ------- set[str] The set of validation checks to report on. """ valid_error_codes = set(ERROR_MSGS.keys()) if "all" in validation_checks: block = deepcopy(validation_checks) validation_checks = valid_error_codes - block # Ensure that the validation check set contains only valid error codes invalid_error_codes = 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}" ) return validation_checks 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 is_generator_function(self): return inspect.isgeneratorfunction(self.obj) @property def source_file_name(self): """ File name where the object is implemented (e.g. pandas/core/frame.py). """ try: if isinstance(self.code_obj, property): fname = inspect.getsourcefile(self.code_obj.fget) elif isinstance(self.code_obj, functools.cached_property): fname = inspect.getsourcefile(self.code_obj.func) else: 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: if isinstance(self.code_obj, property): sourcelines = inspect.getsourcelines(self.code_obj.fget) elif isinstance(self.code_obj, functools.cached_property): sourcelines = inspect.getsourcelines(self.code_obj.func) else: sourcelines = inspect.getsourcelines(self.code_obj) # getsourcelines will return the line of the first decorator found for the # current function. We have to find the def declaration after that. def_line = next( i for i, x in enumerate( re.match("^ *(def|class) ", s) for s in sourcelines[0] ) if x is not None ) return sourcelines[-1] + def_line 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(param.replace("\\", "") for param in 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, validator_cls=None, **validator_kwargs): """ 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'. validator_cls : Validator, optional The Validator class to use. By default, :class:`Validator` will be used. **validator_kwargs Keyword arguments to pass to ``validator_cls`` upon initialization. Note that ``obj_name`` will be passed as a named argument as well. 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 not validator_cls: if isinstance(obj_name, str): doc = Validator(get_doc_object(Validator._load_obj(obj_name))) else: doc = Validator(obj_name) else: doc = validator_cls(obj_name=obj_name, **validator_kwargs) # lineno is only 0 if we have a module docstring in the file and we are # validating that, so we change to 1 for readability of the output ignore_validation_comments = extract_ignore_validation_comments( doc.source_file_name ).get(doc.source_file_def_line or 1, []) errs = [] if not doc.raw_doc: if "GL08" not in ignore_validation_comments: 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 set(re.split(r"\W", 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 doc.is_generator_function: 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")) errs = [err for err in errs if err[0] not in ignore_validation_comments] 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=1693501234.0 numpydoc-1.6.0/numpydoc/xref.py0000644002342000234200000001461514474143462016130 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=1695687844.3801563 numpydoc-1.6.0/numpydoc.egg-info/0000755002342000234200000000000014504422244016265 5ustar00jarrodjarrod././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1695687844.0 numpydoc-1.6.0/numpydoc.egg-info/PKG-INFO0000644002342000234200000001015614504422244017365 0ustar00jarrodjarrodMetadata-Version: 2.1 Name: numpydoc Version: 1.6.0 Summary: Sphinx extension to support docstrings in Numpy format Author-email: Pauli Virtanen and others License: Copyright (C) 2008-2023 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. Project-URL: Homepage, https://numpydoc.readthedocs.io Project-URL: Source, https://github.com/numpy/numpydoc/ Keywords: sphinx,numpy 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.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Requires-Python: >=3.8 Description-Content-Type: text/x-rst License-File: LICENSE.txt Requires-Dist: sphinx>=5 Requires-Dist: Jinja2>=2.10 Requires-Dist: tabulate>=0.8.10 Requires-Dist: tomli>=1.1.0; python_version < "3.11" Provides-Extra: developer Requires-Dist: pre-commit>=3.3; extra == "developer" Requires-Dist: tomli; python_version < "3.11" and extra == "developer" Provides-Extra: doc Requires-Dist: numpy>=1.22; extra == "doc" Requires-Dist: matplotlib>=3.5; extra == "doc" Requires-Dist: pydata-sphinx-theme>=0.13.3; extra == "doc" Requires-Dist: sphinx>=7; extra == "doc" Provides-Extra: test Requires-Dist: pytest; extra == "test" Requires-Dist: pytest-cov; extra == "test" Requires-Dist: matplotlib; extra == "test" ===================================== 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.8+ and sphinx 5+. 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=1695687844.0 numpydoc-1.6.0/numpydoc.egg-info/SOURCES.txt0000644002342000234200000000241314504422244020151 0ustar00jarrodjarrodLICENSE.txt MANIFEST.in README.rst RELEASE.rst pyproject.toml 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/validation.rst numpydoc/__init__.py numpydoc/__main__.py numpydoc/_version.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/entry_points.txt numpydoc.egg-info/requires.txt numpydoc.egg-info/top_level.txt numpydoc/hooks/__init__.py numpydoc/hooks/utils.py numpydoc/hooks/validate_docstrings.py 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/hooks/__init__.py numpydoc/tests/hooks/example_module.py numpydoc/tests/hooks/test_utils.py numpydoc/tests/hooks/test_validate_hook.py numpydoc/tests/tinybuild/Makefile numpydoc/tests/tinybuild/conf.py numpydoc/tests/tinybuild/index.rst numpydoc/tests/tinybuild/numpydoc_test_module.py requirements/default.txt requirements/developer.txt requirements/doc.txt requirements/test.txt././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1695687844.0 numpydoc-1.6.0/numpydoc.egg-info/dependency_links.txt0000644002342000234200000000000114504422244022333 0ustar00jarrodjarrod ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1695687844.0 numpydoc-1.6.0/numpydoc.egg-info/entry_points.txt0000644002342000234200000000012014504422244021554 0ustar00jarrodjarrod[console_scripts] validate-docstrings = numpydoc.hooks.validate_docstrings:main ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1695687844.0 numpydoc-1.6.0/numpydoc.egg-info/requires.txt0000644002342000234200000000040714504422244020666 0ustar00jarrodjarrodsphinx>=5 Jinja2>=2.10 tabulate>=0.8.10 [:python_version < "3.11"] tomli>=1.1.0 [developer] pre-commit>=3.3 [developer:python_version < "3.11"] tomli [doc] numpy>=1.22 matplotlib>=3.5 pydata-sphinx-theme>=0.13.3 sphinx>=7 [test] pytest pytest-cov matplotlib ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1695687844.0 numpydoc-1.6.0/numpydoc.egg-info/top_level.txt0000644002342000234200000000001114504422244021007 0ustar00jarrodjarrodnumpydoc ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501235.0 numpydoc-1.6.0/pyproject.toml0000644002342000234200000000366314474143463015672 0ustar00jarrodjarrod[build-system] build-backend = 'setuptools.build_meta' requires = ['setuptools>=61.2'] [project] name = 'numpydoc' description = 'Sphinx extension to support docstrings in Numpy format' readme = 'README.rst' requires-python = '>=3.8' dynamic = ['version'] keywords = [ 'sphinx', 'numpy', ] 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.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', ] dependencies = [ 'sphinx>=5', 'Jinja2>=2.10', 'tabulate>=0.8.10', "tomli>=1.1.0;python_version<'3.11'", ] [[project.authors]] name = 'Pauli Virtanen and others' email = 'pav@iki.fi' [project.license] file = 'LICENSE.txt' [project.urls] Homepage = 'https://numpydoc.readthedocs.io' Source = 'https://github.com/numpy/numpydoc/' [project.optional-dependencies] developer = [ 'pre-commit>=3.3', "tomli; python_version < '3.11'", ] doc = [ 'numpy>=1.22', 'matplotlib>=3.5', 'pydata-sphinx-theme>=0.13.3', 'sphinx>=7', ] test = [ 'pytest', 'pytest-cov', 'matplotlib', ] [project.scripts] validate-docstrings = 'numpydoc.hooks.validate_docstrings:main' [tool.setuptools] include-package-data = false packages = [ 'numpydoc', 'numpydoc.hooks', ] [tool.setuptools.dynamic.version] attr = 'numpydoc.__version__' [tool.setuptools.package-data] numpydoc = [ 'tests/test_*.py', 'tests/tinybuild/Makefile', 'tests/tinybuild/index.rst', 'tests/tinybuild/*.py', 'templates/*.rst', ] [tool.pytest.ini_options] addopts = ''' --showlocals --doctest-modules -ra --cov-report= --cov=numpydoc --junit-xml=junit-results.xml --ignore=doc/ --ignore=tools/''' junit_family = 'xunit2' ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1695687844.3811562 numpydoc-1.6.0/requirements/0000755002342000234200000000000014504422244015460 5ustar00jarrodjarrod././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501235.0 numpydoc-1.6.0/requirements/default.txt0000644002342000234200000000030714474143463017656 0ustar00jarrodjarrod# Generated via tools/generate_requirements.py and pre-commit hook. # Do not edit this file; modify pyproject.toml instead. sphinx>=5 Jinja2>=2.10 tabulate>=0.8.10 tomli>=1.1.0;python_version<'3.11' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501235.0 numpydoc-1.6.0/requirements/developer.txt0000644002342000234200000000025314474143463020217 0ustar00jarrodjarrod# Generated via tools/generate_requirements.py and pre-commit hook. # Do not edit this file; modify pyproject.toml instead. pre-commit>=3.3 tomli; python_version < '3.11' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501235.0 numpydoc-1.6.0/requirements/doc.txt0000644002342000234200000000027614474143463017004 0ustar00jarrodjarrod# Generated via tools/generate_requirements.py and pre-commit hook. # Do not edit this file; modify pyproject.toml instead. numpy>=1.22 matplotlib>=3.5 pydata-sphinx-theme>=0.13.3 sphinx>=7 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693501235.0 numpydoc-1.6.0/requirements/test.txt0000644002342000234200000000023114474143463017205 0ustar00jarrodjarrod# Generated via tools/generate_requirements.py and pre-commit hook. # Do not edit this file; modify pyproject.toml instead. pytest pytest-cov matplotlib ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1695687844.3821564 numpydoc-1.6.0/setup.cfg0000644002342000234200000000004614504422244014556 0ustar00jarrodjarrod[egg_info] tag_build = tag_date = 0