diff --git a/.circleci/config.yml b/.circleci/config.yml index 66a60c8d..98cae9bc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -20,7 +20,7 @@ jobs: python3 -m venv venv source venv/bin/activate python -m pip install --upgrade pip wheel setuptools - python -m pip install --upgrade -r requirements/doc.txt + python -m pip install --upgrade --group doc python -m pip list - save_cache: key: pip-cache diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6d52fc24..0353ba98 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,7 +5,7 @@ jobs: name: Run CircleCI artifacts redirector steps: - name: GitHub Action step - uses: scientific-python/circleci-artifacts-redirector-action@4e13a10d89177f4bfc8007a7064bdbeda848d8d1 # v1.0.0 + uses: scientific-python/circleci-artifacts-redirector-action@7eafdb60666f57706a5525a2f5eb76224dc8779b # v1.1.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} api-token: ${{ secrets.CIRCLECI_ARTIFACT_REDIRECTOR_TOKEN }} diff --git a/.github/workflows/milestone-merged-prs.yml b/.github/workflows/milestone-merged-prs.yml index 71ae037c..f455839d 100644 --- a/.github/workflows/milestone-merged-prs.yml +++ b/.github/workflows/milestone-merged-prs.yml @@ -12,7 +12,7 @@ jobs: name: attach to PR runs-on: ubuntu-latest steps: - - uses: scientific-python/attach-next-milestone-action@bc07be829f693829263e57d5e8489f4e57d3d420 + - uses: scientific-python/attach-next-milestone-action@c9cfab10ad0c67fed91b01103db26b7f16634639 with: token: ${{ secrets.MILESTONE_LABELER_TOKEN }} force: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3320cf96..dc0b7a30 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,10 +12,10 @@ jobs: name: sdist and wheels runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: fetch-depth: 0 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 with: python-version: "3.12" - name: Build wheels @@ -24,7 +24,7 @@ jobs: pip install -U build twine wheel python -m build --sdist --wheel - run: twine check --strict dist/* - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: dist path: dist @@ -40,8 +40,8 @@ jobs: # IMPORTANT: this permission is mandatory for trusted publishing id-token: write steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: dist path: dist - - uses: pypa/gh-action-pypi-publish@release/v1 + - uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8eed0ce9..b463ee42 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: os: [Ubuntu] - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] sphinx-version: ["sphinx==6.0", "sphinx==6.2", "sphinx==7.0", "sphinx>=7.3"] include: @@ -38,12 +38,12 @@ jobs: run: | python -m pip install --upgrade pip wheel setuptools python -m pip install codecov - python -m pip install ${{ matrix.sphinx-version }} + python -m pip install "${{ matrix.sphinx-version }}" python -m pip list - name: Install run: | - python -m pip install .[test,doc] + python -m pip install . --group test --group doc pip list - name: Run test suite @@ -78,7 +78,7 @@ jobs: strategy: matrix: os: [ubuntu] - python-version: ["3.11", "3.12"] + python-version: ["3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 @@ -95,7 +95,7 @@ jobs: - name: Install run: | - python -m pip install .[test,doc] + python -m pip install . --group test --group doc pip list - name: Run test suite diff --git a/.gitignore b/.gitignore index 26f7400b..60696edd 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ doc/_build numpydoc/tests/tinybuild/_build numpydoc/tests/tinybuild/generated MANIFEST +node_modules diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 425c165f..1fa15182 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: 2c9f875913ee60ca25ce70243dc24d5b6415598c # frozen: v4.6.0 + rev: cef0300fd0fc4d2a87a85fa2093c6b283ea36f4b # frozen: v5.0.0 hooks: - id: check-added-large-files - id: check-ast @@ -18,27 +18,19 @@ repos: - id: trailing-whitespace - repo: https://github.com/pre-commit/mirrors-prettier - rev: ffb6a759a979008c0e6dff86e39f4745a2d9eac4 # frozen: v3.1.0 + rev: f12edd9c7be1c20cfa42420fd0e6df71e42b51ea # frozen: v4.0.0-alpha.8 hooks: - id: prettier types_or: [yaml, toml, markdown, css, scss, javascript, json] args: [--prose-wrap=preserve] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "1dc9eb131c2ea4816c708e4d85820d2cc8542683" # frozen: v0.5.0 + rev: "971923581912ef60a6b70dbf0c3e9a39563c9d47" # frozen: v0.11.4 hooks: - id: ruff args: ["--fix", "--show-fixes", "--exit-non-zero-on-fix"] - id: ruff-format - - repo: local - hooks: - - id: generate_requirements.py - name: generate_requirements.py - language: system - entry: python tools/generate_requirements.py - files: "pyproject.toml|requirements/.*\\.txt|tools/generate_requirements.py" - ci: autofix_prs: false autofix_commit_msg: | diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 2244d460..69493db7 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -2,6 +2,6 @@ name: numpydoc-validation description: This hook validates that docstrings in committed files adhere to numpydoc standards. entry: numpydoc lint - require_serial: true + require_serial: false language: python types: [python] diff --git a/.readthedocs.yaml b/.readthedocs.yaml index a124b7ca..ebc00e82 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -6,29 +6,22 @@ version: 2 # Set the OS, Python version and other tools you might need build: - os: ubuntu-22.04 + os: ubuntu-24.04 tools: - python: "3.11" + python: "3.13" # You can also specify other tool versions: # nodejs: "19" # rust: "1.64" # golang: "1.19" + jobs: + install: + - python -m pip install --upgrade pip wheel setuptools + - python -m pip install . --group doc # Build documentation in the "doc/" directory with Sphinx sphinx: configuration: doc/conf.py - # Optionally build your docs in additional formats such as PDF and ePub # formats: # - pdf # - epub - -# Optional but recommended, declare the Python requirements required -# to build your documentation -# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html -python: - install: - - method: pip - path: . - extra_requirements: - - doc diff --git a/MANIFEST.in b/MANIFEST.in index d1508eee..0a6df3ac 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,7 +2,6 @@ include MANIFEST.in include *.txt include *.rst recursive-include doc * -recursive-include requirements * recursive-include numpydoc * # Exclude what we don't want to include diff --git a/doc/format.rst b/doc/format.rst index 92cc2d36..3b3693a4 100644 --- a/doc/format.rst +++ b/doc/format.rst @@ -225,11 +225,11 @@ description, they can be combined:: Input arrays, description of `x1`, `x2`. When documenting variable length positional, or keyword arguments, leave the -leading star(s) in front of the name:: +leading star(s) in front of the name and do not specify a type:: - *args : tuple + *args Additional arguments should be passed as keyword arguments - **kwargs : dict, optional + **kwargs Extra arguments to `metric`: refer to each metric documentation for a list of all possible arguments. @@ -557,7 +557,9 @@ 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. +constructor's parameters. While repetition is unnecessary, a docstring for +the class constructor (``__init__``) can, optionally, be added to provide +detailed initialization documentation. An **Attributes** section, located below the :ref:`Parameters ` section, may be used to describe non-method attributes of the class:: @@ -767,9 +769,9 @@ leads to the following style recommendations: enclosed within ````double backticks````. A more extensive example of reST markup can be found in `this example -document `_; +document `_; the `quick reference -`_ is +`_ is useful while editing. Line spacing and indentation are significant and should be carefully diff --git a/doc/install.rst b/doc/install.rst index 6282ffed..c72d777f 100644 --- a/doc/install.rst +++ b/doc/install.rst @@ -9,6 +9,7 @@ This extension requires Python 3.9+, sphinx 6+ and is available from: * `numpydoc on PyPI `_ * `numpydoc on GitHub `_ +* `numpydoc on conda-forge `_ `'numpydoc'` should be added to the ``extensions`` option in your Sphinx ``conf.py``. ``'sphinx.ext.autosummary'`` will automatically be loaded diff --git a/doc/release/notes.rst b/doc/release/notes.rst index ad1ccda1..74406b6a 100644 --- a/doc/release/notes.rst +++ b/doc/release/notes.rst @@ -1,3 +1,82 @@ +1.9.0 +===== + +We're happy to announce the release of numpydoc 1.9.0! + +Enhancements +------------ + +- ignore some errors at module level (`#593 `_). +- Rework hook output to remove the table (`#611 `_). +- Switch to storing AST nodes on the stack for more accurate method signature check and easy access to parent nodes (`#623 `_). + +Bug Fixes +--------- + +- MAINT: Changed class constructor __init__ GL08 reporting (`#592 `_). +- BUG: Correct functionality of numpydoc SS05 (`#613 `_). +- Specity the types of ``numpydoc_xref_ignore`` option (`#631 `_). + +Documentation +------------- + +- DOC: Do not use types for *args, **kwargs (`#585 `_). +- mention conda-forge in installation docs (`#595 `_). +- Fix typo in validation.rst (`#605 `_). +- Fix broken link in ``format.rst`` (`#628 `_). + +Maintenance +----------- + +- CI: use hashes for actions' versions in publishing job (`#579 `_). +- Bump the actions group with 2 updates (`#581 `_). +- Bump pypa/gh-action-pypi-publish from 1.10.0 to 1.10.2 in the actions group (`#582 `_). +- [pre-commit.ci] pre-commit autoupdate (`#583 `_). +- MAINT: Add _exception_on_warning to MockApp (`#586 `_). +- Bump the actions group across 1 directory with 2 updates (`#590 `_). +- don't pass maxsplit as positional arg (`#596 `_). +- [pre-commit.ci] pre-commit autoupdate (`#598 `_). +- Add Python 3.13 support (`#599 `_). +- Update readthedocs config (`#600 `_). +- Bump the actions group with 2 updates (`#603 `_). +- Bump the actions group with 3 updates (`#609 `_). +- [pre-commit.ci] pre-commit autoupdate (`#614 `_). +- Bump actions/download-artifact from 4.2.1 to 4.3.0 in the actions group (`#620 `_). +- Bump scientific-python/circleci-artifacts-redirector-action from 1.0.0 to 1.1.0 in the actions group (`#627 `_). +- Switch to dependency groups (`#626 `_). +- Fix pip setup command in github workflow (`#629 `_). + +Contributors +------------ + +13 authors added to this release (alphabetically): + +- Brigitta Sipőcz (`@bsipocz `_) +- Daniel McCloy (`@drammock `_) +- Eric Larson (`@larsoner `_) +- Gilles Peiffer (`@Peiffap `_) +- Jarrod Millman (`@jarrodmillman `_) +- Lucas Colley (`@lucascolley `_) +- Matt Gebert (`@mattgebert `_) +- Maxine Hartnett (`@maxinelasp `_) +- Ross Barnowski (`@rossbar `_) +- Stefan van der Walt (`@stefanv `_) +- Stefanie Molin (`@stefmolin `_) +- Tim Hoffmann (`@timhoffm `_) +- Yuki Kobayashi (`@koyuki7w `_) + +7 reviewers added to this release (alphabetically): + +- Charles Harris (`@charris `_) +- Eric Larson (`@larsoner `_) +- Jarrod Millman (`@jarrodmillman `_) +- Lucas Colley (`@lucascolley `_) +- Matt Gebert (`@mattgebert `_) +- Ross Barnowski (`@rossbar `_) +- Stefan van der Walt (`@stefanv `_) + +_These lists are automatically generated, and may not be complete or may contain duplicates._ + 1.8.0 ===== diff --git a/doc/validation.rst b/doc/validation.rst index aa9d5236..3d2babae 100644 --- a/doc/validation.rst +++ b/doc/validation.rst @@ -183,6 +183,9 @@ inline comments: def __init__(self): # numpydoc ignore=GL08 pass +Note that a properly formatted :ref:`class ` docstring +silences ``GL08`` for an ``__init__`` constructor without a docstring. + This is supported by the :ref:`CLI `, :ref:`pre-commit hook `, and :ref:`Sphinx extension `. diff --git a/numpydoc/__main__.py b/numpydoc/__main__.py index b3b6d159..8cd0943e 100644 --- a/numpydoc/__main__.py +++ b/numpydoc/__main__.py @@ -1,6 +1,6 @@ """ -Implementing `python -m numpydoc` functionality. -""" +Implementing `python -m numpydoc` functionality +""" # '.' omitted at end of docstring for testing purposes! from .cli import main diff --git a/numpydoc/_version.py b/numpydoc/_version.py index 29654eec..0a0a43a5 100644 --- a/numpydoc/_version.py +++ b/numpydoc/_version.py @@ -1 +1 @@ -__version__ = "1.8.0" +__version__ = "1.9.0" diff --git a/numpydoc/cli.py b/numpydoc/cli.py index ef8cff2c..53335fdf 100644 --- a/numpydoc/cli.py +++ b/numpydoc/cli.py @@ -104,10 +104,11 @@ def _parse_config(s): nargs="*", help=( f"""Check codes to ignore.{ - ' Currently ignoring the following from ' - f'{Path(project_root_from_cwd) / config_file}: {ignored_checks_text}' - 'Values provided here will be in addition to the above, unless an alternate config is provided.' - if ignored_checks else '' + " Currently ignoring the following from " + f"{Path(project_root_from_cwd) / config_file}: {ignored_checks_text}" + "Values provided here will be in addition to the above, unless an alternate config is provided." + if ignored_checks + else "" }""" ), ) diff --git a/numpydoc/docscrape.py b/numpydoc/docscrape.py index bfc2840e..54863d5f 100644 --- a/numpydoc/docscrape.py +++ b/numpydoc/docscrape.py @@ -232,8 +232,7 @@ def _parse_param_list(self, content, single_element_is_type=False): # 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] + header = header.removesuffix(" :") if single_element_is_type: arg_name, arg_type = "", header else: diff --git a/numpydoc/docscrape_sphinx.py b/numpydoc/docscrape_sphinx.py index 9b1ccf78..3b2f325a 100644 --- a/numpydoc/docscrape_sphinx.py +++ b/numpydoc/docscrape_sphinx.py @@ -160,7 +160,7 @@ def _process_param(self, param, desc, fake_autosummary): 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] + desc = re.split(r"\n\s*\n", obj_doc.strip(), maxsplit=1)[0] # XXX: Should this have DOTALL? # It does not in autosummary m = re.search(r"^([A-Z].*?\.)(?:\s|$)", " ".join(desc.split())) diff --git a/numpydoc/hooks/validate_docstrings.py b/numpydoc/hooks/validate_docstrings.py index 562a1f09..aa318e47 100644 --- a/numpydoc/hooks/validate_docstrings.py +++ b/numpydoc/hooks/validate_docstrings.py @@ -14,8 +14,6 @@ from pathlib import Path from typing import Any, Dict, List, Tuple, Union -from tabulate import tabulate - from .. import docscrape, validate from .utils import find_project_root @@ -35,7 +33,12 @@ class AstValidator(validate.Validator): """ def __init__( - self, *, ast_node: ast.AST, filename: os.PathLike, obj_name: str + self, + *, + ast_node: ast.AST, + filename: os.PathLike, + obj_name: str, + ancestry: list[ast.AST], ) -> None: self.node: ast.AST = ast_node self.raw_doc: str = ast.get_docstring(self.node, clean=False) or "" @@ -48,6 +51,8 @@ def __init__( self.is_class: bool = isinstance(ast_node, ast.ClassDef) self.is_module: bool = isinstance(ast_node, ast.Module) + self.ancestry: list[ast.AST] = ancestry + @staticmethod def _load_obj(name): raise NotImplementedError("AstValidator does not support this method.") @@ -60,6 +65,10 @@ def name(self) -> str: def is_function_or_method(self) -> bool: return isinstance(self.node, (ast.FunctionDef, ast.AsyncFunctionDef)) + @property + def is_mod(self) -> bool: + return self.is_module + @property def is_generator_function(self) -> bool: if not self.is_function_or_method: @@ -89,7 +98,7 @@ def source_file_def_line(self) -> int: @property def signature_parameters(self) -> Tuple[str]: - def extract_signature(node): + def extract_signature(node, parent): args_node = node.args params = [] for arg_type in ["posonlyargs", "args", "vararg", "kwonlyargs", "kwarg"]: @@ -102,17 +111,21 @@ def extract_signature(node): else: params.extend([arg.arg for arg in entries]) params = tuple(params) - if params and params[0] in {"self", "cls"}: + if ( + params + and params[0] in {"self", "cls"} + and isinstance(parent, ast.ClassDef) + ): return params[1:] return params params = tuple() if self.is_function_or_method: - params = extract_signature(self.node) + params = extract_signature(self.node, self.ancestry[-1]) elif self.is_class: for child in self.node.body: if isinstance(child, ast.FunctionDef) and child.name == "__init__": - params = extract_signature(child) + params = extract_signature(child, self.node) return params @property @@ -142,9 +155,23 @@ def __init__( self.config: dict = config self.filepath: str = filepath self.module_name: str = Path(self.filepath).stem - self.stack: list[str] = [] + self.stack: list[ast.AST] = [] self.findings: list = [] + @property + def node_name(self) -> str: + """ + Get the full name of the current node in the stack. + + Returns + ------- + str + The full name of the current node in the stack. + """ + return ".".join( + [getattr(node, "name", self.module_name) for node in self.stack] + ) + def _ignore_issue(self, node: ast.AST, check: str) -> bool: """ Check whether the issue should be ignored. @@ -183,13 +210,17 @@ def _get_numpydoc_issues(self, node: ast.AST) -> None: node : ast.AST The node under inspection. """ - name = ".".join(self.stack) + name = self.node_name report = validate.validate( - name, AstValidator, ast_node=node, filename=self.filepath + name, + AstValidator, + ast_node=node, + filename=self.filepath, + ancestry=self.stack[:-1], ) self.findings.extend( [ - [f'{self.filepath}:{report["file_line"]}', name, check, description] + [f"{self.filepath}:{report['file_line']}", name, check, description] for check, description in report["errors"] if not self._ignore_issue(node, check) ] @@ -207,13 +238,11 @@ def visit(self, node: ast.AST) -> None: 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 - ) + self.stack.append(node) if not ( self.config["exclude"] - and re.search(self.config["exclude"], ".".join(self.stack)) + and re.search(self.config["exclude"], self.node_name) ): self._get_numpydoc_issues(node) @@ -367,19 +396,12 @@ def run_hook( config_options = parse_config(config or project_root) config_options["checks"] -= set(ignore or []) - findings = [] + findings = False for file in 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 file_issues := process_file(file, config_options): + findings = True + + for line, obj, check, description in file_issues: + print(f"\n{line}: {check} {description}", file=sys.stderr) + + return int(findings) diff --git a/numpydoc/numpydoc.py b/numpydoc/numpydoc.py index a04443b3..9a920e35 100644 --- a/numpydoc/numpydoc.py +++ b/numpydoc/numpydoc.py @@ -296,7 +296,7 @@ def setup(app, get_doc_object_=get_doc_object): 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_xref_ignore", set(), True, types=[set, str]) 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) diff --git a/numpydoc/tests/hooks/example_module.py b/numpydoc/tests/hooks/example_module.py index 87e9b52d..9f75bdf0 100644 --- a/numpydoc/tests/hooks/example_module.py +++ b/numpydoc/tests/hooks/example_module.py @@ -23,8 +23,8 @@ def do_something(self, *args, **kwargs): *args """ - def process(self): - """Process stuff.""" + def create(self): + """Creates stuff.""" class NewClass: diff --git a/numpydoc/tests/hooks/test_validate_hook.py b/numpydoc/tests/hooks/test_validate_hook.py index 9a208cb0..47f315c2 100644 --- a/numpydoc/tests/hooks/test_validate_hook.py +++ b/numpydoc/tests/hooks/test_validate_hook.py @@ -8,7 +8,7 @@ from numpydoc.hooks.validate_docstrings import run_hook -@pytest.fixture() +@pytest.fixture def example_module(request): fullpath = ( Path(request.config.rootdir) @@ -26,55 +26,47 @@ def test_validate_hook(example_module, config, capsys): 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:8 | example_module.MyClass | ES01 | No extended summary found | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ - | numpydoc/tests/hooks/example_module.py:8 | example_module.MyClass | SA01 | See Also section not found | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ - | numpydoc/tests/hooks/example_module.py:8 | example_module.MyClass | EX01 | No examples section found | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ - | numpydoc/tests/hooks/example_module.py:11 | example_module.MyClass.__init__ | GL08 | The object does not have a docstring | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ - | numpydoc/tests/hooks/example_module.py:17 | example_module.MyClass.do_something | ES01 | No extended summary found | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ - | numpydoc/tests/hooks/example_module.py:17 | example_module.MyClass.do_something | PR01 | Parameters {'**kwargs'} not documented | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ - | numpydoc/tests/hooks/example_module.py:17 | example_module.MyClass.do_something | PR07 | Parameter "*args" has no description | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ - | numpydoc/tests/hooks/example_module.py:17 | example_module.MyClass.do_something | SA01 | See Also section not found | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ - | numpydoc/tests/hooks/example_module.py:17 | example_module.MyClass.do_something | EX01 | No examples section found | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ - | numpydoc/tests/hooks/example_module.py:26 | 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:26 | example_module.MyClass.process | ES01 | No extended summary found | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ - | numpydoc/tests/hooks/example_module.py:26 | example_module.MyClass.process | SA01 | See Also section not found | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ - | numpydoc/tests/hooks/example_module.py:26 | example_module.MyClass.process | EX01 | No examples section found | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ - | numpydoc/tests/hooks/example_module.py:30 | example_module.NewClass | GL08 | The object does not have a docstring | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ + numpydoc/tests/hooks/example_module.py:4: ES01 No extended summary found + + numpydoc/tests/hooks/example_module.py:4: PR01 Parameters {'name'} not documented + + numpydoc/tests/hooks/example_module.py:4: SA01 See Also section not found + + numpydoc/tests/hooks/example_module.py:4: EX01 No examples section found + + numpydoc/tests/hooks/example_module.py:8: ES01 No extended summary found + + numpydoc/tests/hooks/example_module.py:8: SA01 See Also section not found + + numpydoc/tests/hooks/example_module.py:8: EX01 No examples section found + + numpydoc/tests/hooks/example_module.py:11: GL08 The object does not have a docstring + + numpydoc/tests/hooks/example_module.py:17: ES01 No extended summary found + + numpydoc/tests/hooks/example_module.py:17: PR01 Parameters {'**kwargs'} not documented + + numpydoc/tests/hooks/example_module.py:17: PR07 Parameter "*args" has no description + + numpydoc/tests/hooks/example_module.py:17: SA01 See Also section not found + + numpydoc/tests/hooks/example_module.py:17: EX01 No examples section found + + numpydoc/tests/hooks/example_module.py:26: SS05 Summary must start with infinitive verb, not third person (e.g. use "Generate" instead of "Generates") + + numpydoc/tests/hooks/example_module.py:26: ES01 No extended summary found + + numpydoc/tests/hooks/example_module.py:26: SA01 See Also section not found + + numpydoc/tests/hooks/example_module.py:26: EX01 No examples section found + + numpydoc/tests/hooks/example_module.py:30: GL08 The object does not have a docstring """ ) return_code = run_hook([example_module], config=config) assert return_code == 1 - assert capsys.readouterr().err.rstrip() == expected + assert capsys.readouterr().err.strip() == expected def test_validate_hook_with_ignore(example_module, capsys): @@ -85,30 +77,24 @@ def test_validate_hook_with_ignore(example_module, capsys): 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:11 | example_module.MyClass.__init__ | GL08 | The object does not have a docstring | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ - | numpydoc/tests/hooks/example_module.py:17 | example_module.MyClass.do_something | PR01 | Parameters {'**kwargs'} not documented | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ - | numpydoc/tests/hooks/example_module.py:17 | example_module.MyClass.do_something | PR07 | Parameter "*args" has no description | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ - | numpydoc/tests/hooks/example_module.py:26 | 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:30 | example_module.NewClass | GL08 | The object does not have a docstring | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------------------+ + numpydoc/tests/hooks/example_module.py:4: PR01 Parameters {'name'} not documented + + numpydoc/tests/hooks/example_module.py:11: GL08 The object does not have a docstring + + numpydoc/tests/hooks/example_module.py:17: PR01 Parameters {'**kwargs'} not documented + + numpydoc/tests/hooks/example_module.py:17: PR07 Parameter "*args" has no description + + numpydoc/tests/hooks/example_module.py:26: SS05 Summary must start with infinitive verb, not third person (e.g. use "Generate" instead of "Generates") + + numpydoc/tests/hooks/example_module.py:30: GL08 The object does not have a docstring """ ) return_code = run_hook([example_module], ignore=["ES01", "SA01", "EX01"]) assert return_code == 1 - assert capsys.readouterr().err.rstrip() == expected + assert capsys.readouterr().err.strip() == expected def test_validate_hook_with_toml_config(example_module, tmp_path, capsys): @@ -130,9 +116,7 @@ def test_validate_hook_with_toml_config(example_module, tmp_path, capsys): ] exclude = '\\.__init__$' override_SS05 = [ - '^Process', - '^Assess', - '^Access', + '^Creates', ] """ ) @@ -140,23 +124,19 @@ def test_validate_hook_with_toml_config(example_module, tmp_path, capsys): 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:17 | example_module.MyClass.do_something | PR01 | Parameters {'**kwargs'} not documented | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ - | numpydoc/tests/hooks/example_module.py:17 | example_module.MyClass.do_something | PR07 | Parameter "*args" has no description | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ - | numpydoc/tests/hooks/example_module.py:30 | example_module.NewClass | GL08 | The object does not have a docstring | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ + numpydoc/tests/hooks/example_module.py:4: PR01 Parameters {'name'} not documented + + numpydoc/tests/hooks/example_module.py:17: PR01 Parameters {'**kwargs'} not documented + + numpydoc/tests/hooks/example_module.py:17: PR07 Parameter "*args" has no description + + numpydoc/tests/hooks/example_module.py:30: GL08 The object does not have a docstring """ ) return_code = run_hook([example_module], config=tmp_path) assert return_code == 1 - assert capsys.readouterr().err.rstrip() == expected + assert capsys.readouterr().err.strip() == expected def test_validate_hook_with_setup_cfg(example_module, tmp_path, capsys): @@ -172,30 +152,26 @@ def test_validate_hook_with_setup_cfg(example_module, tmp_path, capsys): [tool:numpydoc_validation] checks = all,EX01,SA01,ES01 exclude = \\.__init__$ - override_SS05 = ^Process,^Assess,^Access + override_SS05 = ^Creates """ ) ) 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:17 | example_module.MyClass.do_something | PR01 | Parameters {'**kwargs'} not documented | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ - | numpydoc/tests/hooks/example_module.py:17 | example_module.MyClass.do_something | PR07 | Parameter "*args" has no description | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ - | numpydoc/tests/hooks/example_module.py:30 | example_module.NewClass | GL08 | The object does not have a docstring | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ + numpydoc/tests/hooks/example_module.py:4: PR01 Parameters {'name'} not documented + + numpydoc/tests/hooks/example_module.py:17: PR01 Parameters {'**kwargs'} not documented + + numpydoc/tests/hooks/example_module.py:17: PR07 Parameter "*args" has no description + + numpydoc/tests/hooks/example_module.py:30: GL08 The object does not have a docstring """ ) return_code = run_hook([example_module], config=tmp_path) assert return_code == 1 - assert capsys.readouterr().err.rstrip() == expected + assert capsys.readouterr().err.strip() == expected def test_validate_hook_exclude_option_pyproject(example_module, tmp_path, capsys): @@ -220,9 +196,7 @@ def test_validate_hook_exclude_option_pyproject(example_module, tmp_path, capsys '\.__init__$', ] override_SS05 = [ - '^Process', - '^Assess', - '^Access', + '^Creates', ] """ ) @@ -230,19 +204,15 @@ def test_validate_hook_exclude_option_pyproject(example_module, tmp_path, capsys 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:30 | example_module.NewClass | GL08 | The object does not have a docstring | - +-------------------------------------------+------------------------------+---------+--------------------------------------+ + numpydoc/tests/hooks/example_module.py:4: PR01 Parameters {'name'} not documented + + numpydoc/tests/hooks/example_module.py:30: GL08 The object does not have a docstring """ ) return_code = run_hook([example_module], config=tmp_path) assert return_code == 1 - assert capsys.readouterr().err.rstrip() == expected + assert capsys.readouterr().err.strip() == expected def test_validate_hook_exclude_option_setup_cfg(example_module, tmp_path, capsys): @@ -258,25 +228,21 @@ def test_validate_hook_exclude_option_setup_cfg(example_module, tmp_path, capsys [tool:numpydoc_validation] checks = all,EX01,SA01,ES01 exclude = \\.NewClass$,\\.__init__$ - override_SS05 = ^Process,^Assess,^Access + override_SS05 = ^Creates """ ) ) 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:17 | example_module.MyClass.do_something | PR01 | Parameters {'**kwargs'} not documented | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ - | numpydoc/tests/hooks/example_module.py:17 | example_module.MyClass.do_something | PR07 | Parameter "*args" has no description | - +-------------------------------------------+-------------------------------------+---------+----------------------------------------+ + numpydoc/tests/hooks/example_module.py:4: PR01 Parameters {'name'} not documented + + numpydoc/tests/hooks/example_module.py:17: PR01 Parameters {'**kwargs'} not documented + + numpydoc/tests/hooks/example_module.py:17: PR07 Parameter "*args" has no description """ ) return_code = run_hook([example_module], config=tmp_path) assert return_code == 1 - assert capsys.readouterr().err.rstrip() == expected + assert capsys.readouterr().err.strip() == expected diff --git a/numpydoc/tests/test_docscrape.py b/numpydoc/tests/test_docscrape.py index 4cafc762..f10adc45 100644 --- a/numpydoc/tests/test_docscrape.py +++ b/numpydoc/tests/test_docscrape.py @@ -1596,8 +1596,8 @@ def __init__(self, a, b): self.numpydoc_validation_overrides = dict() xref_aliases_complete = deepcopy(DEFAULT_LINKS) - for key in xref_aliases: - xref_aliases_complete[key] = xref_aliases[key] + for key, val in xref_aliases.items(): + xref_aliases_complete[key] = val config = Config(xref_aliases, xref_aliases_complete) app = namedtuple("config", "config")(config) update_config(app) diff --git a/numpydoc/tests/test_main.py b/numpydoc/tests/test_main.py index 12c06840..e07e7df2 100644 --- a/numpydoc/tests/test_main.py +++ b/numpydoc/tests/test_main.py @@ -117,26 +117,14 @@ def test_validate_perfect_docstring(): assert exit_status == 0 -@pytest.mark.parametrize("args", [[], ["--ignore", "ES01", "SA01", "EX01"]]) +@pytest.mark.parametrize("args", [[], ["--ignore", "SS03"]]) def test_lint(capsys, args): argv = ["lint", "numpydoc/__main__.py"] + args if args: expected = "" expected_status = 0 else: - expected = inspect.cleandoc( - """ - +------------------------+----------+---------+----------------------------+ - | file | item | check | description | - +========================+==========+=========+============================+ - | numpydoc/__main__.py:1 | __main__ | ES01 | No extended summary found | - +------------------------+----------+---------+----------------------------+ - | numpydoc/__main__.py:1 | __main__ | SA01 | See Also section not found | - +------------------------+----------+---------+----------------------------+ - | numpydoc/__main__.py:1 | __main__ | EX01 | No examples section found | - +------------------------+----------+---------+----------------------------+ - """ - ) + expected = "numpydoc/__main__.py:1: SS03 Summary does not end with a period" expected_status = 1 return_status = numpydoc.cli.main(argv) diff --git a/numpydoc/tests/test_numpydoc.py b/numpydoc/tests/test_numpydoc.py index 30e6f602..bedef5fa 100644 --- a/numpydoc/tests/test_numpydoc.py +++ b/numpydoc/tests/test_numpydoc.py @@ -36,6 +36,7 @@ class MockConfig: class MockBuilder: config = MockConfig() + _translator = None class MockApp: @@ -49,6 +50,7 @@ def __init__(self): self.verbosity = 2 self._warncount = 0 self.warningiserror = False + self._exception_on_warning = False def test_mangle_docstrings_basic(): @@ -142,7 +144,7 @@ def test_clean_text_signature(): assert _clean_text_signature("func($self, *args)") == "func(*args)" -@pytest.fixture() +@pytest.fixture def f(): def _function_without_seealso_and_examples(): """ diff --git a/numpydoc/tests/test_validate.py b/numpydoc/tests/test_validate.py index 8b40794f..9f0f7942 100644 --- a/numpydoc/tests/test_validate.py +++ b/numpydoc/tests/test_validate.py @@ -1198,6 +1198,113 @@ def missing_whitespace_after_comma(self): """ +class ConstructorDocumentedInClassAndInit: + """ + Class to test constructor documented via class and constructor docstrings. + + A case where both the class docstring and the constructor docstring are + defined. + + Parameters + ---------- + param1 : int + Description of param1. + + See Also + -------- + otherclass : A class that does something else. + + Examples + -------- + This is an example of how to use ConstructorDocumentedInClassAndInit. + """ + + def __init__(self, param1: int) -> None: + """ + Constructor docstring with additional information. + + Extended information. + + Parameters + ---------- + param1 : int + Description of param1 with extra details. + + See Also + -------- + otherclass : A class that does something else. + + Examples + -------- + This is an example of how to use ConstructorDocumentedInClassAndInit. + """ + + +class ConstructorDocumentedInClass: + """ + Class to test constructor documented via class docstring. + + Useful to ensure that validation of `__init__` does not signal GL08, + when the class docstring properly documents the `__init__` constructor. + + Parameters + ---------- + param1 : int + Description of param1. + + See Also + -------- + otherclass : A class that does something else. + + Examples + -------- + This is an example of how to use ConstructorDocumentedInClass. + """ + + def __init__(self, param1: int) -> None: + pass + + +class ConstructorDocumentedInClassWithNoParameters: + """ + Class to test constructor documented via class docstring with no parameters. + + Useful to ensure that validation of `__init__` does not signal GL08, + when the class docstring properly documents the `__init__` constructor. + + See Also + -------- + otherclass : A class that does something else. + + Examples + -------- + This is an example of how to use ConstructorDocumentedInClassWithNoParameters. + """ + + def __init__(self) -> None: + pass + + +class IncompleteConstructorDocumentedInClass: + """ + Class to test an incomplete constructor docstring. + + This class does not properly document parameters. + Unnecessary extended summary. + + See Also + -------- + otherclass : A class that does something else. + + Examples + -------- + This is an example of how to use IncompleteConstructorDocumentedInClass. + """ + + def __init__(self, param1: int): + pass + + class TestValidator: def _import_path(self, klass=None, func=None): """ @@ -1536,6 +1643,40 @@ def test_bad_docstrings(self, capsys, klass, func, msgs): for msg in msgs: assert msg in " ".join(err[1] for err in result["errors"]) + @pytest.mark.parametrize( + "klass,exp_init_codes,exc_init_codes,exp_klass_codes", + [ + ("ConstructorDocumentedInClass", tuple(), ("GL08",), tuple()), + ("ConstructorDocumentedInClassAndInit", tuple(), ("GL08",), tuple()), + ( + "ConstructorDocumentedInClassWithNoParameters", + tuple(), + ("GL08",), + tuple(), + ), + ( + "IncompleteConstructorDocumentedInClass", + ("GL08",), + tuple(), + ("PR01"), # Parameter not documented in class constructor + ), + ], + ) + def test_constructor_docstrings( + self, klass, exp_init_codes, exc_init_codes, exp_klass_codes + ): + # First test the class docstring itself, checking expected_klass_codes match + result = validate_one(self._import_path(klass=klass)) + for err in result["errors"]: + assert err[0] in exp_klass_codes + + # Then test the constructor docstring + result = validate_one(self._import_path(klass=klass, func="__init__")) + for code in exp_init_codes: + assert code in " ".join(err[0] for err in result["errors"]) + for code in exc_init_codes: + assert code not in " ".join(err[0] for err in result["errors"]) + def decorator(x): """Test decorator.""" diff --git a/numpydoc/validate.py b/numpydoc/validate.py index 138e9d48..d0debfa2 100644 --- a/numpydoc/validate.py +++ b/numpydoc/validate.py @@ -80,8 +80,7 @@ "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", + "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", @@ -277,6 +276,10 @@ def type(self): def is_function_or_method(self): return inspect.isfunction(self.obj) + @property + def is_mod(self): + return inspect.ismodule(self.obj) + @property def is_generator_function(self): return inspect.isgeneratorfunction(_unwrap(self.obj)) @@ -633,7 +636,29 @@ def validate(obj_name, validator_cls=None, **validator_kwargs): errs = [] if not doc.raw_doc: - if "GL08" not in ignore_validation_comments: + report_GL08: bool = True + # Check if the object is a class and has a docstring in the constructor + # Also check if code_obj is defined, as undefined for the AstValidator in validate_docstrings.py. + if ( + doc.name.endswith(".__init__") + and doc.is_function_or_method + and hasattr(doc, "code_obj") + ): + cls_name = doc.code_obj.__qualname__.split(".")[0] + cls = Validator._load_obj(f"{doc.code_obj.__module__}.{cls_name}") + # cls = Validator._load_obj(f"{doc.name[:-9]}.{cls_name}") ## Alternative + cls_doc = Validator(get_doc_object(cls)) + + # Parameter_mismatches, PR01, PR02, PR03 are checked for the class docstring. + # If cls_doc has PR01, PR02, PR03 errors, i.e. invalid class docstring, + # then we also report missing constructor docstring, GL08. + report_GL08 = len(cls_doc.parameter_mismatches) > 0 + + # Check if GL08 is to be ignored: + if "GL08" in ignore_validation_comments: + report_GL08 = False + # Add GL08 error? + if report_GL08: errs.append(error("GL08")) return { "type": doc.type, @@ -685,12 +710,18 @@ def validate(obj_name, validator_cls=None, **validator_kwargs): 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": + # Heuristic to check for infinitive verbs - shouldn't end in "s" + elif ( + doc.is_function_or_method + and len(doc.summary.split(" ")[0]) > 1 + and doc.summary.split(" ")[0][-1] == "s" + and doc.summary.split(" ")[0][-2] != "s" + ): errs.append(error("SS05")) if doc.num_summary_lines > 1: errs.append(error("SS06")) - if not doc.extended_summary: + if not doc.is_mod and not doc.extended_summary: errs.append(("ES01", "No extended summary found")) # PR01: Parameters not documented @@ -742,20 +773,21 @@ def validate(obj_name, validator_cls=None, **validator_kwargs): 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.is_mod: + 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")) + if not doc.examples: + errs.append(error("EX01")) errs = [err for err in errs if err[0] not in ignore_validation_comments] diff --git a/pyproject.toml b/pyproject.toml index 24dec3ee..b6acaa4c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,11 +24,11 @@ classifiers = [ 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', 'Topic :: Documentation', ] dependencies = [ 'sphinx>=6', - 'tabulate>=0.8.10', "tomli>=1.1.0;python_version<'3.11'", ] @@ -43,10 +43,12 @@ file = 'LICENSE.txt' Homepage = 'https://numpydoc.readthedocs.io' Source = 'https://github.com/numpy/numpydoc/' -[project.optional-dependencies] -developer = [ +[dependency-groups] +dev = [ 'pre-commit>=3.3', "tomli; python_version < '3.11'", + { include-group = "doc" }, + { include-group = "test" } ] doc = [ 'numpy>=1.22', diff --git a/requirements/default.txt b/requirements/default.txt deleted file mode 100644 index 5a1986a1..00000000 --- a/requirements/default.txt +++ /dev/null @@ -1,5 +0,0 @@ -# Generated via tools/generate_requirements.py and pre-commit hook. -# Do not edit this file; modify pyproject.toml instead. -sphinx>=6 -tabulate>=0.8.10 -tomli>=1.1.0;python_version<'3.11' diff --git a/requirements/developer.txt b/requirements/developer.txt deleted file mode 100644 index dbeefe57..00000000 --- a/requirements/developer.txt +++ /dev/null @@ -1,4 +0,0 @@ -# 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' diff --git a/requirements/doc.txt b/requirements/doc.txt deleted file mode 100644 index 950966b6..00000000 --- a/requirements/doc.txt +++ /dev/null @@ -1,7 +0,0 @@ -# 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 -intersphinx_registry diff --git a/requirements/test.txt b/requirements/test.txt deleted file mode 100644 index 2a9e670f..00000000 --- a/requirements/test.txt +++ /dev/null @@ -1,5 +0,0 @@ -# Generated via tools/generate_requirements.py and pre-commit hook. -# Do not edit this file; modify pyproject.toml instead. -pytest -pytest-cov -matplotlib diff --git a/tools/generate_requirements.py b/tools/generate_requirements.py deleted file mode 100755 index 388d2ccc..00000000 --- a/tools/generate_requirements.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python -"""Generate requirements/*.txt files from pyproject.toml.""" - -import sys -from pathlib import Path - -try: # standard module since Python 3.11 - import tomllib as toml -except ImportError: - try: # available for older Python via pip - import tomli as toml - except ImportError: - sys.exit("Please install `tomli` first: `pip install tomli`") - -script_pth = Path(__file__) -repo_dir = script_pth.parent.parent -script_relpth = script_pth.relative_to(repo_dir) -header = [ - f"# Generated via {script_relpth.as_posix()} and pre-commit hook.", - "# Do not edit this file; modify pyproject.toml instead.", -] - - -def generate_requirement_file(name: str, req_list: list[str]) -> None: - req_fname = repo_dir / "requirements" / f"{name}.txt" - req_fname.write_text("\n".join(header + req_list) + "\n") - - -def main() -> None: - pyproject = toml.loads((repo_dir / "pyproject.toml").read_text()) - - generate_requirement_file("default", pyproject["project"]["dependencies"]) - - for key, opt_list in pyproject["project"]["optional-dependencies"].items(): - generate_requirement_file(key, opt_list) - - -if __name__ == "__main__": - main() pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy