diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2f33e32..42e2206 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -78,3 +78,8 @@ repos: hooks: - id: check-dependabot - id: check-github-workflows + + - repo: https://github.com/numpy/numpydoc + rev: "v1.8.0" + hooks: + - id: numpydoc-validation diff --git a/docs/conf.py b/docs/conf.py index f2b4827..38f0c8d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,3 +1,5 @@ +"""Sphinx config.""" + import importlib.metadata from typing import Any diff --git a/pyproject.toml b/pyproject.toml index f59bb23..bac02d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,6 +73,7 @@ pre-commit = "*" pylint = "*" basedmypy = "*" basedpyright = "*" +numpydoc = ">=1.8.0,<2" # import dependencies for mypy: array-api-strict = "*" numpy = "*" @@ -145,13 +146,9 @@ ci-py313 = ["py313", "tests"] minversion = "6.0" addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"] xfail_strict = true -filterwarnings = [ - "error", -] +filterwarnings = ["error"] log_cli_level = "INFO" -testpaths = [ - "tests", -] +testpaths = ["tests"] # Coverage @@ -262,3 +259,20 @@ messages_control.disable = [ "missing-function-docstring", "wrong-import-position", ] + + +# numpydoc + +[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 + '.*test_funcs.*', + '.*test_utils.*', + '.*test_version.*', + '.*test_vendor.*', +] diff --git a/src/array_api_extra/__init__.py b/src/array_api_extra/__init__.py index afd1df1..26dbc28 100644 --- a/src/array_api_extra/__init__.py +++ b/src/array_api_extra/__init__.py @@ -1,3 +1,5 @@ +"""Extra array functions built on top of the array API standard.""" + from ._funcs import atleast_nd, cov, create_diagonal, expand_dims, kron, setdiff1d, sinc __version__ = "0.4.1.dev0" diff --git a/src/array_api_extra/_funcs.py b/src/array_api_extra/_funcs.py index bbbc59c..30e6dc1 100644 --- a/src/array_api_extra/_funcs.py +++ b/src/array_api_extra/_funcs.py @@ -1,3 +1,5 @@ +"""Public API Functions.""" + import warnings from ._lib import _compat, _utils @@ -22,14 +24,15 @@ def atleast_nd(x: Array, /, *, ndim: int, xp: ModuleType | None = None) -> Array Parameters ---------- x : array + Input array. ndim : int The minimum number of dimensions for the result. xp : array_namespace, optional - The standard-compatible namespace for `x`. Default: infer + The standard-compatible namespace for `x`. Default: infer. Returns ------- - res : array + array An array with ``res.ndim`` >= `ndim`. If ``x.ndim`` >= `ndim`, `x` is returned. If ``x.ndim`` < `ndim`, `x` is expanded by prepending new axes @@ -47,7 +50,6 @@ def atleast_nd(x: Array, /, *, ndim: int, xp: ModuleType | None = None) -> Array ... [3, 4]]]) >>> xpx.atleast_nd(x, ndim=1, xp=xp) is x True - """ if xp is None: xp = array_namespace(x) @@ -77,11 +79,11 @@ def cov(m: Array, /, *, xp: ModuleType | None = None) -> Array: Each row of `m` represents a variable, and each column a single observation of all those variables. xp : array_namespace, optional - The standard-compatible namespace for `m`. Default: infer + The standard-compatible namespace for `m`. Default: infer. Returns ------- - res : array + array The covariance matrix of the variables. Examples @@ -104,7 +106,6 @@ def cov(m: Array, /, *, xp: ModuleType | None = None) -> Array: Array([[ 1., -1.], [-1., 1.]], dtype=array_api_strict.float64) - Note that element :math:`C_{0,1}`, which shows the correlation between :math:`x_0` and :math:`x_1`, is negative. @@ -122,7 +123,6 @@ def cov(m: Array, /, *, xp: ModuleType | None = None) -> Array: >>> xpx.cov(y, xp=xp) Array(2.14413333, dtype=array_api_strict.float64) - """ if xp is None: xp = array_namespace(m) @@ -161,17 +161,17 @@ def create_diagonal( Parameters ---------- x : array - A 1-D array + A 1-D array. offset : int, optional Offset from the leading diagonal (default is ``0``). Use positive ints for diagonals above the leading diagonal, and negative ints for diagonals below the leading diagonal. xp : array_namespace, optional - The standard-compatible namespace for `x`. Default: infer + The standard-compatible namespace for `x`. Default: infer. Returns ------- - res : array + array A 2-D array with `x` on the diagonal (offset by `offset`). Examples @@ -191,7 +191,6 @@ def create_diagonal( [2, 0, 0, 0, 0], [0, 4, 0, 0, 0], [0, 0, 8, 0, 0]], dtype=array_api_strict.int64) - """ if xp is None: xp = array_namespace(x) @@ -221,6 +220,7 @@ def expand_dims( Parameters ---------- a : array + Array to have its shape expanded. axis : int or tuple of ints, optional Position(s) in the expanded axes where the new axis (or axes) is/are placed. If multiple positions are provided, they should be unique (note that a position @@ -228,11 +228,11 @@ def expand_dims( that will also result in an error). Default: ``(0,)``. xp : array_namespace, optional - The standard-compatible namespace for `a`. Default: infer + The standard-compatible namespace for `a`. Default: infer. Returns ------- - res : array + array `a` with an expanded shape. Examples @@ -270,7 +270,6 @@ def expand_dims( >>> y Array([[[1], [2]]], dtype=array_api_strict.int64) - """ if xp is None: xp = array_namespace(a) @@ -304,12 +303,13 @@ def kron(a: Array, b: Array, /, *, xp: ModuleType | None = None) -> Array: Parameters ---------- a, b : array + Input arrays. xp : array_namespace, optional - The standard-compatible namespace for `a` and `b`. Default: infer + The standard-compatible namespace for `a` and `b`. Default: infer. Returns ------- - res : array + array The Kronecker product of `a` and `b`. Notes @@ -333,7 +333,6 @@ def kron(a: Array, b: Array, /, *, xp: ModuleType | None = None) -> Array: [ ... ... ], [ a[-1,0]*b, a[-1,1]*b, ... , a[-1,-1]*b ]] - Examples -------- >>> import array_api_strict as xp @@ -352,7 +351,6 @@ def kron(a: Array, b: Array, /, *, xp: ModuleType | None = None) -> Array: [0., 0., 1., 1.], [0., 0., 1., 1.]], dtype=array_api_strict.float64) - >>> a = xp.reshape(xp.arange(100), (2, 5, 2, 5)) >>> b = xp.reshape(xp.arange(24), (2, 3, 4)) >>> c = xpx.kron(a, b, xp=xp) @@ -365,7 +363,6 @@ def kron(a: Array, b: Array, /, *, xp: ModuleType | None = None) -> Array: >>> K = tuple(xp.asarray(I) * xp.asarray(S1) + xp.asarray(J1)) >>> c[K] == a[I]*b[J] Array(True, dtype=array_api_strict.bool) - """ if xp is None: xp = array_namespace(a, b) @@ -424,11 +421,11 @@ def setdiff1d( If ``True``, the input arrays are both assumed to be unique, which can speed up the calculation. Default is ``False``. xp : array_namespace, optional - The standard-compatible namespace for `x1` and `x2`. Default: infer + The standard-compatible namespace for `x1` and `x2`. Default: infer. Returns ------- - res : array + array 1D array of values in `x1` that are not in `x2`. The result is sorted when `assume_unique` is ``False``, but otherwise only sorted if the input is sorted. @@ -442,7 +439,6 @@ def setdiff1d( >>> x2 = xp.asarray([3, 4, 5, 6]) >>> xpx.setdiff1d(x1, x2, xp=xp) Array([1, 2], dtype=array_api_strict.int64) - """ if xp is None: xp = array_namespace(x1, x2) @@ -476,11 +472,11 @@ def sinc(x: Array, /, *, xp: ModuleType | None = None) -> Array: Array (possibly multi-dimensional) of values for which to calculate ``sinc(x)``. Must have a real floating point dtype. xp : array_namespace, optional - The standard-compatible namespace for `x`. Default: infer + The standard-compatible namespace for `x`. Default: infer. Returns ------- - res : array + array ``sinc(x)`` calculated elementwise, which has the same shape as the input. Notes @@ -528,7 +524,6 @@ def sinc(x: Array, /, *, xp: ModuleType | None = None) -> Array: -5.84680802e-02, -8.90384387e-02, -8.40918587e-02, -4.92362781e-02, -3.89817183e-17], dtype=array_api_strict.float64) - """ if xp is None: xp = array_namespace(x) diff --git a/src/array_api_extra/_lib/__init__.py b/src/array_api_extra/_lib/__init__.py index e69de29..d7a7952 100644 --- a/src/array_api_extra/_lib/__init__.py +++ b/src/array_api_extra/_lib/__init__.py @@ -0,0 +1 @@ +"""Modules housing private functions.""" diff --git a/src/array_api_extra/_lib/_compat.py b/src/array_api_extra/_lib/_compat.py index 5d465c6..de7a220 100644 --- a/src/array_api_extra/_lib/_compat.py +++ b/src/array_api_extra/_lib/_compat.py @@ -1,3 +1,4 @@ +"""Acquire helpers from array-api-compat.""" # Allow packages that vendor both `array-api-extra` and # `array-api-compat` to override the import location diff --git a/src/array_api_extra/_lib/_compat.pyi b/src/array_api_extra/_lib/_compat.pyi index 4b90819..2105708 100644 --- a/src/array_api_extra/_lib/_compat.pyi +++ b/src/array_api_extra/_lib/_compat.pyi @@ -1,15 +1,17 @@ +"""Static type stubs for `_compat.py`.""" + from types import ModuleType from ._typing import Array, Device # pylint: disable=missing-class-docstring,unused-argument -class ArrayModule(ModuleType): - def device(self, x: Array, /) -> Device: ... +class ArrayModule(ModuleType): # numpydoc ignore=GL08 + def device(self, x: Array, /) -> Device: ... # numpydoc ignore=GL08 def array_namespace( *xs: Array, api_version: str | None = None, use_compat: bool | None = None, -) -> ArrayModule: ... -def device(x: Array, /) -> Device: ... +) -> ArrayModule: ... # numpydoc ignore=GL08 +def device(x: Array, /) -> Device: ... # numpydoc ignore=GL08 diff --git a/src/array_api_extra/_lib/_typing.py b/src/array_api_extra/_lib/_typing.py index fd3eec4..8aedc44 100644 --- a/src/array_api_extra/_lib/_typing.py +++ b/src/array_api_extra/_lib/_typing.py @@ -1,3 +1,5 @@ +"""Static typing helpers.""" + from types import ModuleType from typing import Any diff --git a/src/array_api_extra/_lib/_utils.py b/src/array_api_extra/_lib/_utils.py index 6e58968..523c21b 100644 --- a/src/array_api_extra/_lib/_utils.py +++ b/src/array_api_extra/_lib/_utils.py @@ -1,3 +1,5 @@ +"""Utility functions used by `array_api_extra/_funcs.py`.""" + from . import _compat from ._typing import Array, ModuleType @@ -12,9 +14,9 @@ def in1d( assume_unique: bool = False, invert: bool = False, xp: ModuleType | None = None, -) -> Array: - """Checks whether each element of an array is also present in a - second array. +) -> Array: # numpydoc ignore=PR01,RT01 + """ + Check whether each element of an array is also present in a second array. Returns a boolean array the same length as `x1` that is True where an element of `x1` is in `x2` and False otherwise. @@ -68,7 +70,7 @@ def mean( axis: int | tuple[int, ...] | None = None, keepdims: bool = False, xp: ModuleType | None = None, -) -> Array: +) -> Array: # numpydoc ignore=PR01,RT01 """ Complex mean, https://github.com/data-apis/array-api/issues/846. """ diff --git a/vendor_tests/__init__.py b/vendor_tests/__init__.py index da33f30..9fd27fd 100644 --- a/vendor_tests/__init__.py +++ b/vendor_tests/__init__.py @@ -1 +1 @@ -# Allow for relative imports in test_vendor.py +"""Allow for relative imports in `test_vendor.py`.""" diff --git a/vendor_tests/_array_api_compat_vendor.py b/vendor_tests/_array_api_compat_vendor.py index e2220df..cd31a9c 100644 --- a/vendor_tests/_array_api_compat_vendor.py +++ b/vendor_tests/_array_api_compat_vendor.py @@ -1,9 +1,10 @@ -# This file is a hook imported by src/array_api_extra/_lib/_compat.py +"""This file is a hook imported by `src/array_api_extra/_lib/_compat.py`.""" + from .array_api_compat import * # noqa: F403 from .array_api_compat import array_namespace as array_namespace_compat # Let unit tests check with `is` that we are picking up the function from this module # and not from the original array_api_compat module. -def array_namespace(*xs, **kwargs): +def array_namespace(*xs, **kwargs): # numpydoc ignore=GL08 return array_namespace_compat(*xs, **kwargs)