diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index 3efb5ad..06f5937 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -66,13 +66,11 @@ jobs: python: 3.9 toxenv: py39-test-alldeps toxargs: -v --develop - toxposargs: --open-files - name: Python 3.8 with oldest supported version of all dependencies os: ubuntu-18.04 python: 3.8 toxenv: py38-test-oldestdeps-alldeps-cov-clocale - toxposargs: --remote-data=github - name: Python 3.9 with all optional dependencies (Windows) os: windows-latest diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..fa93290 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,4 @@ +_build/ +api/ + +ZENODO.rst diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..fb03f26 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,133 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest + +#This is needed with git because git doesn't create a dir if it's empty +$(shell [ -d "_static" ] || mkdir -p _static) + +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 " text to make text files" + @echo " man to make manual pages" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + +clean: + -rm -rf $(BUILDDIR) + -rm -rf api + -rm -rf generated + +html: + $(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/Astropy.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Astropy.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/Astropy" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Astropy" + @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." + +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." + +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: + @echo "Run 'python setup.py test' in the root directory to run doctests " \ + @echo "in the documentation." diff --git a/docs/_static/theme.css b/docs/_static/theme.css new file mode 100644 index 0000000..6ce3703 --- /dev/null +++ b/docs/_static/theme.css @@ -0,0 +1,62 @@ +span#logotext2 { + color: #764099; +} + +/* Taken from NumPy */ + +@import url('https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;0,900;1,400;1,700;1,900&family=Open+Sans:ital,wght@0,400;0,600;1,400;1,600&display=swap'); + +.navbar-brand img { + height: 60px; +} +.navbar-brand { + height: 75px; +} + +body { + font-family: 'Open Sans', sans-serif; + color:#4A4A4A; /* numpy.org body color */ +} + +pre, code { + font-size: 100%; + line-height: 155%; +} + +div.output_area div[class*=highlight] pre { + white-space: pre-wrap; +} + +/* Make output lighter gray and no italics for the love of all that is holy! */ +.highlight .go { + color: #555555; + font-style: normal; +} + +/* OMG why would you bold numbers */ +.highlight .mf, .highlight .mi { + font-weight: 300; +} + +h1 { + font-family: "Lato", sans-serif; + color: #013243; /* warm black */ +} + + +h2 { + color: #4d77cf; /* han blue */ + letter-spacing: -.03em; +} + +h3 { + color: #013243; /* warm black */ + letter-spacing: -.03em; +} + +/* Override some aspects of the pydata-sphinx-theme: taken from Pandas */ + +:root { + /* Use softer blue from bootstrap's default info color */ + --pst-color-info: 23, 162, 184; +} diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..3789e23 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,195 @@ +# Load all of the global Astropy configuration + +from __future__ import annotations + +# STDLIB +import datetime +import pathlib +import sys +from importlib import import_module +from importlib.metadata import version as get_version + +# THIRD PARTY +import tomli + + +def get_authors() -> set[str]: + """Get author information from ``pyproject.toml``s. + + Returns + ------- + set[str] + The authors. + """ + + authors: set[str] = set() + cfg = pathlib.Path(__file__).parent.parent / "pyproject.toml" + + with cfg.open("rb") as f: + toml = tomli.load(f) + + project = dict(toml["project"]) + authors.update({d["name"] for d in project["authors"]}) + + return authors + + +# -- General configuration ---------------------------------------------------- + +# By default, highlight as Python 3. +highlight_language = "python3" + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ["_build", "**.ipynb_checkpoints"] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = ".rst" + +# Sphinx extensions +extensions = [ + "sphinx.ext.doctest", + "sphinx_automodapi.automodapi", + "pytest_doctestplus.sphinx.doctestplus", +] + +autosummary_generate = True + +automodapi_toctreedirnm = "api" + +# The reST default role (used for this markup: `text`) to use for all +# documents. Set to the "smart" one. +default_role = "obj" + +# Class documentation should contain *both* the class docstring and +# the __init__ docstring +autoclass_content = "both" + +# This is added to the end of RST files - a good place to put substitutions to +# be used globally. +rst_epilog = """ +.. |NumPyOverloader| replace:: :class:`~overload_numpy.NumPyOverloader` +""" + +# intersphinx +intersphinx_mapping = { + "python": ( + "https://docs.python.org/3/", + (None, "http://data.astropy.org/intersphinx/python3.inv"), + ), +} + +# Show / hide TODO blocks +todo_include_todos = True + +doctest_global_setup = """ +import sys + +python_version = sys.version_info +""" + + +# -- NumpyDoc Configuration ------------------------ + +# Don't show summaries of the members in each class along with the +# class' docstring +numpydoc_show_class_members = True + +# Whether to create cross-references for the parameter types in the +# Parameters, Other Parameters, Returns and Yields sections of the docstring. +numpydoc_xref_param_type = True + +# 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. This can be overwritten or modified in packages and is provided here for +# convenience. +numpydoc_xref_ignore = { + "or", + "of", + "thereof", + "default", + "optional", + "keyword-only", + "instance", + "type", + "class", + "subclass", + "method", +} + +# Mappings to fully qualified paths (or correct ReST references) for the +# aliases/shortcuts used when specifying the types of parameters. +# Numpy provides some defaults +# https://github.com/numpy/numpydoc/blob/b352cd7635f2ea7748722f410a31f937d92545cc/numpydoc/xref.py#L62-L94 +numpydoc_xref_aliases = { + # Python terms + "function": ":term:`python:function`", + "iterator": ":term:`python:iterator`", + "mapping": ":term:`python:mapping`", +} + +# -- Project information ------------------------------------------------------ + +# This does not *have* to match the package name, but typically does +project = "overload_numpy" +author = ", ".join(get_authors()) +copyright = f"{datetime.datetime.now().year}, {author}" + +import_module(project) +package = sys.modules[project] + +# The short X.Y version. +version = get_version("overload_numpy").split("-", 1)[0] +# The full version, including alpha/beta/rc tags. +release = get_version("overload_numpy") + + +# -- Options for HTML output --------------------------------------------------- + +html_theme = "pydata_sphinx_theme" + +# html_logo = '_static/.png' + +# html_theme_options = { +# "logo_link": "index", +# "icon_links": [ +# { +# "name": "GitHub", +# "url": "", +# "icon": "fab fa-github-square", +# }, +# ], +# } + +# Custom sidebar templates, maps document names to template names. +html_sidebars = {"**": ["search-field.html", "sidebar-nav-bs.html"]} + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = '' + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = str(docs_root / '_static' / 'X.ico') + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +# html_last_updated_fmt = '' + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +# html_title = "overload_numpy.[X]" + +# Output file base name for HTML help builder. +htmlhelp_basename = project + "doc" + +# Static files to copy after template files +html_static_path = ["_static"] +html_css_files = ["theme.css"] diff --git a/docs/index.rst b/docs/index.rst index 0dc01f1..ea2e68c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,3 +1,106 @@ ############## Overload NumPy ############## + + +Quick Worked Example +-------------------- + +.. testsetup:: + + >>> from __future__ import annotations + +First, some imports: + + >>> from dataclasses import dataclass + >>> from typing import ClassVar + >>> import numpy as np + >>> from overload_numpy import NumPyOverloader, NDFunctionMixin + +Now we can define a ``overload_numpy.NumPyOverloader`` instance: + + >>> VEC_FUNCS = NumPyOverloader() + +The overloads apply to an array wrapping class. Let's define one: + + >>> @dataclass + ... class Vector1D(NDFunctionMixin): + ... '''A simple array wrapper.''' + ... x: np.ndarray + ... NP_FUNC_OVERLOADS: ClassVar[NumPyOverloader] = VEC_FUNCS + +Now ``numpy`` functions can be overloaded and registered for ``Vector1D``. + + >>> @VEC_FUNCS.implements(np.concatenate, Vector1D) + ... def concatenate(vec1ds: 'tuple[Vector1D, ...]') -> Vector1D: + ... return Vector1D(np.concatenate(tuple(v.x for v in vec1ds))) + +Time to check this works: + + >>> vec1d = Vector1D(np.arange(3)) + >>> newvec = np.concatenate((vec1d, vec1d)) + >>> newvec + Vector1D(x=array([0, 1, 2, 0, 1, 2])) + + +Overloading Subclasses +---------------------- + +What if we defined a subclass of ``Vector1D``? + + >>> @dataclass + ... class Vector2D(Vector1D): + ... '''A simple 2-array wrapper.''' + ... y: np.ndarray + +The overload for :func:`numpy.concatenate` registered on ``Vector1D`` will not +work correctly for ``Vector2D``. :class:`~overload_numpy.NumPyOverloader` +supports single-dispatch on the calling type for the overload, so overload can +be customized for subclasses. + + >>> @VEC_FUNCS.implements(np.concatenate, Vector2D) + ... def concatenate(vec2ds: 'tuple[Vector2D, ...]') -> Vector2D: + ... print("using Vector2D implementation...") + ... return Vector2D(np.concatenate(tuple(v.x for v in vec2ds)), + ... np.concatenate(tuple(v.y for v in vec2ds))) + +Checking this works: + + >>> vec2d = Vector2D(np.arange(3), np.arange(3, 6)) + >>> newvec = np.concatenate((vec2d, vec2d)) + using Vector2D implementation... + >>> newvec + Vector2D(x=array([0, 1, 2, 0, 1, 2]), y=array([3, 4, 5, 3, 4, 5])) + + +That works great! But rather than defining a new implementation for each +subclass, let's see how we could write a more broadly applicable overload: + + >>> from dataclasses import fields + >>> from typing import TypeVar + >>> V = TypeVar("V", bound="Vector1D") + + >>> @VEC_FUNCS.implements(np.concatenate, Vector2D) # overriding + ... def concatenate(vecs: 'tuple[V, ...]') -> V: + ... VT = type(vecs[0]) + ... if not all(isinstance(v, VT) for v in vecs): # make sure all 1 type + ... return NotImplemented + ... return VT(*(np.concatenate(tuple(getattr(v, f.name) for v in vecs)) + ... for f in fields(VT))) + +Checking this works: + + >>> newvec = np.concatenate((vec2d, vec2d)) + >>> newvec + Vector2D(x=array([0, 1, 2, 0, 1, 2]), y=array([3, 4, 5, 3, 4, 5])) + + + >>> @dataclass + ... class Vector3D(Vector2D): + ... '''A simple 3-array wrapper.''' + ... z: np.ndarray + + >>> vec3d = Vector3D(np.arange(2), np.arange(3, 5), np.arange(6, 8)) + >>> newvec = np.concatenate((vec3d, vec3d)) + >>> newvec + Vector3D(x=array([0, 1, 0, 1]), y=array([3, 4, 3, 4]), z=array([6, 7, 6, 7])) diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..aa93013 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,172 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% +) + +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. changes to make an overview over all changed/added/deprecated items + 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%\* + del /q /s api + del /q /s generated + goto end +) + +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\Astropy.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Astropy.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" == "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" == "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 +) + +:end diff --git a/docs/references.txt b/docs/references.txt new file mode 100644 index 0000000..b49146b --- /dev/null +++ b/docs/references.txt @@ -0,0 +1 @@ +.. _Python: http://www.python.org diff --git a/pyproject.toml b/pyproject.toml index 53604d4..9d3166d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,7 @@ "IPython", "jupyter_client", "nbsphinx", + "pydata-sphinx-theme", "sphinx", "sphinx-astropy", "sphinxcontrib.bibtex < 2.0.0", @@ -174,7 +175,7 @@ [tool.pytest.ini_options] - testpaths = ["src/overload_numpy", "docs"] + testpaths = ["tests", "src/overload_numpy", "docs"] astropy_header = "True" doctest_plus = "enabled" text_file_format = "rst" diff --git a/tests/conftest.py b/tests/conftest.py index 9ab6827..3d1c612 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,6 +11,7 @@ # STDLIB import os +from typing import Any # THIRD PARTY import pytest @@ -51,3 +52,19 @@ def pytest_configure(config: pytest.Config) -> None: packagename = os.path.basename(os.path.dirname(__file__)) TESTED_VERSIONS[packagename] = version("overload_numpy") + + +@pytest.fixture(autouse=True) # type: ignore +def add_numpy(doctest_namespace: dict[str, Any]) -> None: + """Add NumPy to Pytest. + + Parameters + ---------- + doctest_namespace : namespace + + """ + # THIRD PARTY + import numpy + + # add to namespace + doctest_namespace["np"] = numpy