docsig 0.86.1__tar.gz → 0.87.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {docsig-0.86.1 → docsig-0.87.1}/LICENSE +5 -0
- docsig-0.87.1/LICENSES/SPHINX_LICENSE +39 -0
- {docsig-0.86.1 → docsig-0.87.1}/PKG-INFO +15 -10
- {docsig-0.86.1 → docsig-0.87.1}/README.rst +11 -5
- {docsig-0.86.1 → docsig-0.87.1}/docsig/_check.py +2 -2
- {docsig-0.86.1 → docsig-0.87.1}/docsig/_config.py +4 -24
- {docsig-0.86.1 → docsig-0.87.1}/docsig/_core.py +8 -4
- {docsig-0.86.1 → docsig-0.87.1}/docsig/_decorators.py +25 -10
- {docsig-0.86.1 → docsig-0.87.1}/docsig/_files.py +4 -12
- {docsig-0.86.1 → docsig-0.87.1}/docsig/_main.py +4 -1
- {docsig-0.86.1 → docsig-0.87.1}/docsig/_module.py +22 -13
- {docsig-0.86.1 → docsig-0.87.1}/docsig/_report.py +90 -29
- {docsig-0.86.1 → docsig-0.87.1}/docsig/_stub.py +6 -4
- {docsig-0.86.1 → docsig-0.87.1}/docsig/_utils.py +23 -0
- docsig-0.87.1/docsig/_vendor/sphinx/ext/napoleon.py +1373 -0
- {docsig-0.86.1 → docsig-0.87.1}/docsig/_version.py +1 -1
- {docsig-0.86.1 → docsig-0.87.1}/pyproject.toml +47 -38
- {docsig-0.86.1 → docsig-0.87.1}/docsig/__init__.py +0 -0
- {docsig-0.86.1 → docsig-0.87.1}/docsig/__main__.py +0 -0
- {docsig-0.86.1 → docsig-0.87.1}/docsig/_directives.py +0 -0
- {docsig-0.86.1 → docsig-0.87.1}/docsig/_hooks.py +0 -0
- {docsig-0.86.1 → docsig-0.87.1}/docsig/_parsers.py +0 -0
- {docsig-0.86.1 → docsig-0.87.1}/docsig/messages.py +0 -0
- {docsig-0.86.1 → docsig-0.87.1}/docsig/plugin/__init__.py +0 -0
- {docsig-0.86.1 → docsig-0.87.1}/docsig/plugin/_flake8.py +0 -0
- {docsig-0.86.1 → docsig-0.87.1}/docsig/plugin/_validate_pyproject.py +0 -0
- {docsig-0.86.1 → docsig-0.87.1}/docsig/py.typed +0 -0
|
@@ -19,3 +19,8 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
This project includes code derived from Sphinx (sphinx.ext.napoleon),
|
|
24
|
+
licensed under the BSD 2-Clause License.
|
|
25
|
+
|
|
26
|
+
See licenses/SPHINX_LICENSE for details.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
This project includes code derived from Sphinx (sphinx.ext.napoleon).
|
|
2
|
+
|
|
3
|
+
Sphinx is a documentation generator: https://www.sphinx-doc.org/
|
|
4
|
+
|
|
5
|
+
The original code is licensed under the BSD 2-Clause License.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
License for Sphinx
|
|
10
|
+
==================
|
|
11
|
+
|
|
12
|
+
Unless otherwise indicated, all code in the Sphinx project is licenced under the
|
|
13
|
+
two clause BSD licence below.
|
|
14
|
+
|
|
15
|
+
Copyright (c) 2007-2025 by the Sphinx team (see AUTHORS file).
|
|
16
|
+
All rights reserved.
|
|
17
|
+
|
|
18
|
+
Redistribution and use in source and binary forms, with or without
|
|
19
|
+
modification, are permitted provided that the following conditions are
|
|
20
|
+
met:
|
|
21
|
+
|
|
22
|
+
* Redistributions of source code must retain the above copyright
|
|
23
|
+
notice, this list of conditions and the following disclaimer.
|
|
24
|
+
|
|
25
|
+
* Redistributions in binary form must reproduce the above copyright
|
|
26
|
+
notice, this list of conditions and the following disclaimer in the
|
|
27
|
+
documentation and/or other materials provided with the distribution.
|
|
28
|
+
|
|
29
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
30
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
31
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
32
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
33
|
+
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
34
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
35
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
36
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
37
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
38
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
39
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: docsig
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.87.1
|
|
4
4
|
Summary: Check signature params for proper documentation
|
|
5
|
-
License: MIT
|
|
5
|
+
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
7
|
+
License-File: LICENSES/SPHINX_LICENSE
|
|
7
8
|
Keywords: check,docs,docstring,params,signature
|
|
8
9
|
Author: jshwi
|
|
9
10
|
Author-email: s.whitlock@live.com
|
|
10
11
|
Maintainer: jshwi
|
|
11
12
|
Maintainer-email: s.whitlock@live.com
|
|
12
|
-
Requires-Python: >=3.10
|
|
13
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
-
Requires-Dist: Sphinx (>=7,<9)
|
|
21
20
|
Requires-Dist: astroid (>=4.0.2,<5.0.0)
|
|
22
21
|
Requires-Dist: pathspec (>=0.12.1,<1.2.0)
|
|
23
22
|
Requires-Dist: tomli (>=2.0.1,<3.0.0)
|
|
@@ -36,7 +35,7 @@ Description-Content-Type: text/x-rst
|
|
|
36
35
|
|
|
37
36
|
|
|
|
38
37
|
|
|
39
|
-
|License| |PyPI| |CI| |CodeQL| |pre-commit.ci status| |codecov.io| |readthedocs.org| |python3.10| |Black| |isort| |pylint| |Security Status|
|
|
38
|
+
|License| |PyPI| |Jetbrains Plugin| |CI| |Jetbrains Build| |CodeQL| |pre-commit.ci status| |codecov.io| |readthedocs.org| |python3.10| |Black| |isort| |pylint| |Security Status|
|
|
40
39
|
|
|
41
40
|
.. |License| image:: https://img.shields.io/badge/License-MIT-yellow.svg
|
|
42
41
|
:target: https://opensource.org/licenses/MIT
|
|
@@ -74,6 +73,12 @@ Description-Content-Type: text/x-rst
|
|
|
74
73
|
.. |Security Status| image:: https://img.shields.io/badge/security-bandit-yellow.svg
|
|
75
74
|
:target: https://github.com/PyCQA/bandit
|
|
76
75
|
:alt: Security Status
|
|
76
|
+
.. |Jetbrains Plugin| image:: https://img.shields.io/jetbrains/plugin/v/32129.svg
|
|
77
|
+
:target: https://plugins.jetbrains.com/plugin/32129
|
|
78
|
+
:alt: Jetbrains Plugin
|
|
79
|
+
.. |Jetbrains Build| image:: https://github.com/jshwi/docsig/actions/workflows/build-intellij-plugin.yaml/badge.svg
|
|
80
|
+
:target: https://github.com/jshwi/docsig/actions/workflows/build-intellij-plugin.yaml
|
|
81
|
+
:alt: Build
|
|
77
82
|
|
|
78
83
|
Check Python signature params for proper documentation
|
|
79
84
|
-------------------------------------------------------
|
|
@@ -87,7 +92,7 @@ Maintain accurate and up-to-date Python documentation by automatically checking
|
|
|
87
92
|
that all parameters in function signatures match their docstring documentation.
|
|
88
93
|
Use docsig as a standalone tool, integrate it with ``flake8``, or add it as a
|
|
89
94
|
``pre-commit`` hook to catch documentation issues before they reach your
|
|
90
|
-
repository.
|
|
95
|
+
repository. **docsig** is also available as a ``PyCharm`` plugin
|
|
91
96
|
|
|
92
97
|
Contributing
|
|
93
98
|
------------
|
|
@@ -195,7 +200,7 @@ ensure your installation has registered `docsig`
|
|
|
195
200
|
.. code-block:: console
|
|
196
201
|
|
|
197
202
|
$ flake8 --version
|
|
198
|
-
7.3.0 (docsig: 0.
|
|
203
|
+
7.3.0 (docsig: 0.87.1, mccabe: 0.7.0, pycodestyle: 2.14.0, pyflakes: 3.4.0) CPython 3.10.19 on Darwin
|
|
199
204
|
|
|
200
205
|
And now use `flake8` to lint your files
|
|
201
206
|
|
|
@@ -281,7 +286,7 @@ Standalone
|
|
|
281
286
|
|
|
282
287
|
repos:
|
|
283
288
|
- repo: https://github.com/jshwi/docsig
|
|
284
|
-
rev: v0.
|
|
289
|
+
rev: v0.87.1
|
|
285
290
|
hooks:
|
|
286
291
|
- id: docsig
|
|
287
292
|
args:
|
|
@@ -300,7 +305,7 @@ or integrated with ``flake8``
|
|
|
300
305
|
hooks:
|
|
301
306
|
- id: flake8
|
|
302
307
|
additional_dependencies:
|
|
303
|
-
- docsig==0.
|
|
308
|
+
- docsig==0.87.1
|
|
304
309
|
args:
|
|
305
310
|
- "--sig-check-class"
|
|
306
311
|
- "--sig-check-dunders"
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
|
|
|
9
9
|
|
|
10
|
-
|License| |PyPI| |CI| |CodeQL| |pre-commit.ci status| |codecov.io| |readthedocs.org| |python3.10| |Black| |isort| |pylint| |Security Status|
|
|
10
|
+
|License| |PyPI| |Jetbrains Plugin| |CI| |Jetbrains Build| |CodeQL| |pre-commit.ci status| |codecov.io| |readthedocs.org| |python3.10| |Black| |isort| |pylint| |Security Status|
|
|
11
11
|
|
|
12
12
|
.. |License| image:: https://img.shields.io/badge/License-MIT-yellow.svg
|
|
13
13
|
:target: https://opensource.org/licenses/MIT
|
|
@@ -45,6 +45,12 @@
|
|
|
45
45
|
.. |Security Status| image:: https://img.shields.io/badge/security-bandit-yellow.svg
|
|
46
46
|
:target: https://github.com/PyCQA/bandit
|
|
47
47
|
:alt: Security Status
|
|
48
|
+
.. |Jetbrains Plugin| image:: https://img.shields.io/jetbrains/plugin/v/32129.svg
|
|
49
|
+
:target: https://plugins.jetbrains.com/plugin/32129
|
|
50
|
+
:alt: Jetbrains Plugin
|
|
51
|
+
.. |Jetbrains Build| image:: https://github.com/jshwi/docsig/actions/workflows/build-intellij-plugin.yaml/badge.svg
|
|
52
|
+
:target: https://github.com/jshwi/docsig/actions/workflows/build-intellij-plugin.yaml
|
|
53
|
+
:alt: Build
|
|
48
54
|
|
|
49
55
|
Check Python signature params for proper documentation
|
|
50
56
|
-------------------------------------------------------
|
|
@@ -58,7 +64,7 @@ Maintain accurate and up-to-date Python documentation by automatically checking
|
|
|
58
64
|
that all parameters in function signatures match their docstring documentation.
|
|
59
65
|
Use docsig as a standalone tool, integrate it with ``flake8``, or add it as a
|
|
60
66
|
``pre-commit`` hook to catch documentation issues before they reach your
|
|
61
|
-
repository.
|
|
67
|
+
repository. **docsig** is also available as a ``PyCharm`` plugin
|
|
62
68
|
|
|
63
69
|
Contributing
|
|
64
70
|
------------
|
|
@@ -166,7 +172,7 @@ ensure your installation has registered `docsig`
|
|
|
166
172
|
.. code-block:: console
|
|
167
173
|
|
|
168
174
|
$ flake8 --version
|
|
169
|
-
7.3.0 (docsig: 0.
|
|
175
|
+
7.3.0 (docsig: 0.87.1, mccabe: 0.7.0, pycodestyle: 2.14.0, pyflakes: 3.4.0) CPython 3.10.19 on Darwin
|
|
170
176
|
|
|
171
177
|
And now use `flake8` to lint your files
|
|
172
178
|
|
|
@@ -252,7 +258,7 @@ Standalone
|
|
|
252
258
|
|
|
253
259
|
repos:
|
|
254
260
|
- repo: https://github.com/jshwi/docsig
|
|
255
|
-
rev: v0.
|
|
261
|
+
rev: v0.87.1
|
|
256
262
|
hooks:
|
|
257
263
|
- id: docsig
|
|
258
264
|
args:
|
|
@@ -271,7 +277,7 @@ or integrated with ``flake8``
|
|
|
271
277
|
hooks:
|
|
272
278
|
- id: flake8
|
|
273
279
|
additional_dependencies:
|
|
274
|
-
- docsig==0.
|
|
280
|
+
- docsig==0.87.1
|
|
275
281
|
args:
|
|
276
282
|
- "--sig-check-class"
|
|
277
283
|
- "--sig-check-dunders"
|
|
@@ -6,8 +6,8 @@ docsig._check
|
|
|
6
6
|
from ._config import Config as _Config
|
|
7
7
|
from ._module import Function as _Function
|
|
8
8
|
from ._module import Parent as _Parent
|
|
9
|
-
from ._report import Failure as _Failure
|
|
10
9
|
from ._report import Failures as _Failures
|
|
10
|
+
from ._report import check_function as _check_function
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def _should_check_function(
|
|
@@ -47,7 +47,7 @@ def _run_check(
|
|
|
47
47
|
parent,
|
|
48
48
|
config,
|
|
49
49
|
):
|
|
50
|
-
failure =
|
|
50
|
+
failure = _check_function(child, config)
|
|
51
51
|
if failure:
|
|
52
52
|
failures.append(failure)
|
|
53
53
|
|
|
@@ -6,7 +6,6 @@ Config dataclasses, pyproject.toml loader, and commandline.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import argparse as _argparse
|
|
9
|
-
import os as _os
|
|
10
9
|
import re as _re
|
|
11
10
|
import typing as _t
|
|
12
11
|
from dataclasses import dataclass as _dataclass
|
|
@@ -15,6 +14,7 @@ from pathlib import Path as _Path
|
|
|
15
14
|
|
|
16
15
|
import tomli as _tomli
|
|
17
16
|
|
|
17
|
+
from ._utils import get_parent_that_has as _get_parent_that_has
|
|
18
18
|
from ._version import __version__
|
|
19
19
|
from .messages import Messages as _Messages
|
|
20
20
|
|
|
@@ -26,31 +26,18 @@ def _split_comma(value: str) -> list[str]:
|
|
|
26
26
|
return [i.replace("\\,", ",") for i in _re.split(r"(?<!\\),", value)]
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
# attempt to locate a pyproject.toml file if one exists in parents
|
|
30
|
-
def _find_pyproject_toml(path: _Path | None = None) -> _Path | None:
|
|
31
|
-
if not path:
|
|
32
|
-
path = _Path.cwd()
|
|
33
|
-
|
|
34
|
-
pyproject_toml = path / PYPROJECT_TOML
|
|
35
|
-
if pyproject_toml.is_file():
|
|
36
|
-
return pyproject_toml
|
|
37
|
-
|
|
38
|
-
if str(path) == _os.path.abspath(_os.sep):
|
|
39
|
-
return None
|
|
40
|
-
|
|
41
|
-
return _find_pyproject_toml(path.parent)
|
|
42
|
-
|
|
43
|
-
|
|
44
29
|
def get_config(prog: str) -> dict[str, _t.Any]:
|
|
45
30
|
"""Return the program's tool-section config from pyproject.toml.
|
|
46
31
|
|
|
47
32
|
:param prog: Program name.
|
|
48
33
|
:return: Config dict, or empty dict if no config is found.
|
|
49
34
|
"""
|
|
50
|
-
|
|
35
|
+
# attempt to locate a pyproject.toml file if one exists in parents
|
|
36
|
+
pyproject_file = _get_parent_that_has(PYPROJECT_TOML)
|
|
51
37
|
if pyproject_file is None:
|
|
52
38
|
return {}
|
|
53
39
|
|
|
40
|
+
pyproject_file /= PYPROJECT_TOML
|
|
54
41
|
return {
|
|
55
42
|
k.replace("-", "_"): v
|
|
56
43
|
for k, v in _tomli.loads(pyproject_file.read_text())
|
|
@@ -220,17 +207,10 @@ def build_parser() -> _ArgumentParser:
|
|
|
220
207
|
action="store_true",
|
|
221
208
|
help="ignore kwargs prefixed with two asterisks",
|
|
222
209
|
)
|
|
223
|
-
parser.add_argument(
|
|
224
|
-
"-i",
|
|
225
|
-
action="store_true",
|
|
226
|
-
help=_argparse.SUPPRESS,
|
|
227
|
-
dest="ignore_no_params",
|
|
228
|
-
)
|
|
229
210
|
parser.add_argument(
|
|
230
211
|
"--ignore-no-params",
|
|
231
212
|
action="store_true",
|
|
232
213
|
help="ignore docstrings where parameters are not documented",
|
|
233
|
-
dest="ignore_no_params",
|
|
234
214
|
)
|
|
235
215
|
parser.add_argument(
|
|
236
216
|
"--ignore-typechecker",
|
|
@@ -21,7 +21,9 @@ from ._files import Files as _Files
|
|
|
21
21
|
from ._parsers import parse_from_file as _parse_from_file
|
|
22
22
|
from ._parsers import parse_from_string as _parse_from_string
|
|
23
23
|
from ._report import Failures as _Failures
|
|
24
|
+
from ._report import RetCode as _RetCode
|
|
24
25
|
from ._report import report as _report
|
|
26
|
+
from ._utils import get_parent_that_has as _get_parent_that_has
|
|
25
27
|
from ._utils import print_checks as _print_checks
|
|
26
28
|
from .messages import E as _E
|
|
27
29
|
from .messages import Messages as _Messages
|
|
@@ -211,6 +213,7 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
211
213
|
logger = _logging.getLogger(__package__)
|
|
212
214
|
logger.debug(_pformat(config))
|
|
213
215
|
if config.list_checks:
|
|
216
|
+
# noinspection PyNoneFunctionAssignment
|
|
214
217
|
return int(bool(_print_checks())) # type: ignore
|
|
215
218
|
|
|
216
219
|
if string:
|
|
@@ -218,11 +221,12 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
218
221
|
failures = _run_checks(module, config)
|
|
219
222
|
return _report(failures, config)
|
|
220
223
|
|
|
221
|
-
retcodes =
|
|
222
|
-
|
|
224
|
+
retcodes = _RetCode()
|
|
225
|
+
repo = _get_parent_that_has(".git/HEAD")
|
|
226
|
+
files = _Files(path, config.filters, repo)
|
|
223
227
|
for file in files:
|
|
224
228
|
failures = runner(file, config)
|
|
225
229
|
retcode = _report(failures, config, str(file))
|
|
226
|
-
retcodes.
|
|
230
|
+
retcodes.add(retcode)
|
|
227
231
|
|
|
228
|
-
return
|
|
232
|
+
return retcodes.result
|
|
@@ -4,10 +4,13 @@ docsig._decorators
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import functools as _functools
|
|
7
|
+
import json as _json
|
|
8
|
+
import os as _os
|
|
7
9
|
import sys as _sys
|
|
8
10
|
import typing as _t
|
|
9
11
|
from pathlib import Path as _Path
|
|
10
12
|
|
|
13
|
+
from ._report import RetCode as _RetCode
|
|
11
14
|
from .messages import E as _E
|
|
12
15
|
|
|
13
16
|
_FuncType = _t.Callable[..., int]
|
|
@@ -35,6 +38,7 @@ def parse_msgs(func: _WrappedFuncType) -> _WrappedFuncType:
|
|
|
35
38
|
return _wrapper
|
|
36
39
|
|
|
37
40
|
|
|
41
|
+
# TODO: make report json by default and wrap with a reporter for cli
|
|
38
42
|
def validate_args(func: _FuncType) -> _WrappedFuncType:
|
|
39
43
|
"""Validate arguments before calling the wrapped function.
|
|
40
44
|
|
|
@@ -51,29 +55,31 @@ def validate_args(func: _FuncType) -> _WrappedFuncType:
|
|
|
51
55
|
|
|
52
56
|
@_functools.wraps(func)
|
|
53
57
|
def _wrapper(*args: str | _Path, **kwargs: _t.Any) -> int:
|
|
54
|
-
|
|
58
|
+
format_json = _os.getenv("_DOCSIG_FORMAT_JSON") is not None
|
|
59
|
+
retcode = _RetCode(2)
|
|
60
|
+
errors = []
|
|
55
61
|
if not kwargs.get("list_checks", False):
|
|
56
62
|
if not args and not kwargs.get("string"):
|
|
57
|
-
|
|
63
|
+
errors.append(
|
|
58
64
|
"the following arguments are required: path(s) or string",
|
|
59
65
|
)
|
|
60
66
|
|
|
61
67
|
for message in kwargs.get("disable") or []:
|
|
62
68
|
if not message.isknown:
|
|
63
|
-
|
|
69
|
+
errors.append(
|
|
64
70
|
f"unknown option to disable '{message.description}'",
|
|
65
71
|
)
|
|
66
72
|
|
|
67
73
|
for message in kwargs.get("target") or []:
|
|
68
74
|
if not message.isknown:
|
|
69
|
-
|
|
75
|
+
errors.append(
|
|
70
76
|
f"unknown option to target '{message.description}'",
|
|
71
77
|
)
|
|
72
78
|
|
|
73
79
|
if kwargs.get("check_class") and kwargs.get(
|
|
74
80
|
"check_class_constructor",
|
|
75
81
|
):
|
|
76
|
-
|
|
82
|
+
errors.append(
|
|
77
83
|
"argument to check class constructor not allowed with"
|
|
78
84
|
" argument to check class",
|
|
79
85
|
)
|
|
@@ -84,13 +90,22 @@ def validate_args(func: _FuncType) -> _WrappedFuncType:
|
|
|
84
90
|
# passing both commandline args as argparse won't
|
|
85
91
|
# allow it, therefore, this must be an issue with
|
|
86
92
|
# the pyproject.toml configuration
|
|
87
|
-
|
|
93
|
+
errors.append(
|
|
88
94
|
"please check your pyproject.toml configuration",
|
|
89
95
|
)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
if errors:
|
|
97
|
+
message = "\n".join(errors)
|
|
98
|
+
if format_json:
|
|
99
|
+
obj = [ # pragma: no cover
|
|
100
|
+
{"line": None, "message": message, "exit": retcode.result},
|
|
101
|
+
]
|
|
102
|
+
if format_json: # pragma: no cover
|
|
103
|
+
print(_json.dumps(obj).strip()) # pragma: no cover
|
|
104
|
+
|
|
105
|
+
else:
|
|
106
|
+
print(message, file=_sys.stderr)
|
|
107
|
+
|
|
108
|
+
return retcode.result
|
|
94
109
|
|
|
95
110
|
return func(*args, **kwargs)
|
|
96
111
|
|
|
@@ -20,18 +20,8 @@ FILE_INFO = "%s: %s"
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class _Gitignore(_PathSpec):
|
|
23
|
-
def
|
|
24
|
-
if (path / ".git" / "HEAD").is_file():
|
|
25
|
-
return path
|
|
26
|
-
|
|
27
|
-
if str(path) == _os.path.abspath(_os.sep):
|
|
28
|
-
return None
|
|
29
|
-
|
|
30
|
-
return self._get_repo_relative_to(path.parent)
|
|
31
|
-
|
|
32
|
-
def __init__(self) -> None:
|
|
23
|
+
def __init__(self, repo: _Path | None = None) -> None:
|
|
33
24
|
patterns = []
|
|
34
|
-
repo = self._get_repo_relative_to(_Path.cwd())
|
|
35
25
|
# only consider gitignore patterns valid if inside a git repo
|
|
36
26
|
# there might be stray gitignore files lying about
|
|
37
27
|
if repo is not None:
|
|
@@ -73,16 +63,18 @@ class Files(list[_Path]):
|
|
|
73
63
|
|
|
74
64
|
:param paths: Path(s) to collect (files or directories).
|
|
75
65
|
:param filters: Filters object.
|
|
66
|
+
:param repo: Path to the repo root.
|
|
76
67
|
"""
|
|
77
68
|
|
|
78
69
|
def __init__(
|
|
79
70
|
self,
|
|
80
71
|
paths: tuple[str | _Path, ...],
|
|
81
72
|
filters: _Filters,
|
|
73
|
+
repo: _Path | None = None,
|
|
82
74
|
) -> None:
|
|
83
75
|
super().__init__()
|
|
84
76
|
self._include_ignored = filters.include_ignored
|
|
85
|
-
self._gitignore = _Gitignore()
|
|
77
|
+
self._gitignore = _Gitignore(repo)
|
|
86
78
|
logger = _logging.getLogger(__package__)
|
|
87
79
|
for path in paths:
|
|
88
80
|
self._populate(_Path(path))
|
|
@@ -5,6 +5,7 @@ docsig._main
|
|
|
5
5
|
CLI entry point that parses args and runs docsig.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import os as _os
|
|
8
9
|
import sys as _sys
|
|
9
10
|
import warnings as _warnings
|
|
10
11
|
|
|
@@ -20,7 +21,6 @@ def _warn_on_deprecated_short_flags() -> None:
|
|
|
20
21
|
"-o": "--check-overridden",
|
|
21
22
|
"-p": "--check-protected",
|
|
22
23
|
"-P": "--check-property-returns",
|
|
23
|
-
"-i": "--ignore-no-params",
|
|
24
24
|
}
|
|
25
25
|
raw_args = _sys.argv[1:]
|
|
26
26
|
expanded_flags = []
|
|
@@ -47,6 +47,9 @@ def main() -> str | int:
|
|
|
47
47
|
|
|
48
48
|
:return: Exit code (non-zero if any check failed).
|
|
49
49
|
"""
|
|
50
|
+
if _os.getenv("_DOCSIG_FORMAT_JSON"):
|
|
51
|
+
_warnings.simplefilter("ignore", FutureWarning) # pragma: no cover
|
|
52
|
+
|
|
50
53
|
_warn_on_deprecated_short_flags()
|
|
51
54
|
a = _parse_args()
|
|
52
55
|
_excepthook(a.no_ansi)
|
|
@@ -57,7 +57,6 @@ class Parent: # pylint: disable=too-many-instance-attributes
|
|
|
57
57
|
_ast.nodes.Module
|
|
58
58
|
| _ast.nodes.ClassDef
|
|
59
59
|
| _ast.nodes.FunctionDef
|
|
60
|
-
| _ast.nodes.NodeNG
|
|
61
60
|
| None
|
|
62
61
|
) = None,
|
|
63
62
|
directives: _Directives | None = None,
|
|
@@ -82,7 +81,7 @@ class Parent: # pylint: disable=too-many-instance-attributes
|
|
|
82
81
|
# later inspected to report on the module error (the
|
|
83
82
|
# report doesn't analyze modules or classes - it
|
|
84
83
|
# analyzes functions)
|
|
85
|
-
self._children.append(Function(
|
|
84
|
+
self._children.append(Function(error=error))
|
|
86
85
|
else:
|
|
87
86
|
self._name = node.name
|
|
88
87
|
self._parse_ast(node, file)
|
|
@@ -101,13 +100,13 @@ class Parent: # pylint: disable=too-many-instance-attributes
|
|
|
101
100
|
# resolved in the directive object, they are needed to notify
|
|
102
101
|
# the user in the case that they are invalid
|
|
103
102
|
parent_comments, parent_disabled = self._directives.get(
|
|
104
|
-
node.lineno,
|
|
103
|
+
node.lineno or 0,
|
|
105
104
|
(_Comments(), _Messages()),
|
|
106
105
|
)
|
|
107
106
|
if hasattr(node, "body"):
|
|
108
107
|
for subnode in node.body:
|
|
109
108
|
comments, disabled = self._directives.get(
|
|
110
|
-
subnode.lineno,
|
|
109
|
+
subnode.lineno or 0,
|
|
111
110
|
(_Comments(), _Messages()),
|
|
112
111
|
)
|
|
113
112
|
comments.extend(parent_comments)
|
|
@@ -119,7 +118,7 @@ class Parent: # pylint: disable=too-many-instance-attributes
|
|
|
119
118
|
for name in subnode.names:
|
|
120
119
|
original, alias = name
|
|
121
120
|
self._imports[original] = alias or original
|
|
122
|
-
elif isinstance(subnode, _ast.FunctionDef):
|
|
121
|
+
elif isinstance(subnode, _ast.nodes.FunctionDef):
|
|
123
122
|
func = Function(
|
|
124
123
|
subnode,
|
|
125
124
|
comments,
|
|
@@ -147,7 +146,7 @@ class Parent: # pylint: disable=too-many-instance-attributes
|
|
|
147
146
|
)
|
|
148
147
|
|
|
149
148
|
self._children.append(func)
|
|
150
|
-
elif isinstance(subnode, _ast.ClassDef):
|
|
149
|
+
elif isinstance(subnode, _ast.nodes.ClassDef):
|
|
151
150
|
self._children.append(
|
|
152
151
|
Parent(
|
|
153
152
|
subnode,
|
|
@@ -192,7 +191,7 @@ class Function(Parent): # pylint: disable=too-many-instance-attributes
|
|
|
192
191
|
# pylint: disable-next=too-many-arguments,too-many-positional-arguments
|
|
193
192
|
def __init__(
|
|
194
193
|
self,
|
|
195
|
-
node: _ast.nodes.FunctionDef |
|
|
194
|
+
node: _ast.nodes.FunctionDef | None = None,
|
|
196
195
|
comments: _Comments | None = None,
|
|
197
196
|
directives: _Directives | None = None,
|
|
198
197
|
messages: _Messages | None = None,
|
|
@@ -216,7 +215,7 @@ class Function(Parent): # pylint: disable=too-many-instance-attributes
|
|
|
216
215
|
self._docstring = _Docstring()
|
|
217
216
|
self._lineno = 0
|
|
218
217
|
self._error = error
|
|
219
|
-
if node is not None:
|
|
218
|
+
if node is not None and node.parent is not None:
|
|
220
219
|
self._parent = node.parent.frame()
|
|
221
220
|
self._decorators = node.decorators
|
|
222
221
|
self._lineno = node.lineno
|
|
@@ -230,7 +229,11 @@ class Function(Parent): # pylint: disable=too-many-instance-attributes
|
|
|
230
229
|
node,
|
|
231
230
|
self._config.ignore,
|
|
232
231
|
)
|
|
233
|
-
if
|
|
232
|
+
if (
|
|
233
|
+
self.isinit
|
|
234
|
+
and not self._config.check.class_constructor
|
|
235
|
+
and not isinstance(self._parent, _ast.nodes.Lambda)
|
|
236
|
+
):
|
|
234
237
|
# docstring for __init__ is expected on the class
|
|
235
238
|
# docstring
|
|
236
239
|
relevant_doc_node = self._parent.doc_node
|
|
@@ -259,7 +262,7 @@ class Function(Parent): # pylint: disable=too-many-instance-attributes
|
|
|
259
262
|
@property
|
|
260
263
|
def ismethod(self) -> bool:
|
|
261
264
|
"""Whether this function is defined in a class (method)."""
|
|
262
|
-
return isinstance(self._parent, _ast.ClassDef)
|
|
265
|
+
return isinstance(self._parent, _ast.nodes.ClassDef)
|
|
263
266
|
|
|
264
267
|
@property
|
|
265
268
|
def isproperty(self) -> bool:
|
|
@@ -282,11 +285,16 @@ class Function(Parent): # pylint: disable=too-many-instance-attributes
|
|
|
282
285
|
@property
|
|
283
286
|
def isoverridden(self) -> bool:
|
|
284
287
|
"""Whether this function overrides a base class method."""
|
|
285
|
-
if
|
|
288
|
+
if (
|
|
289
|
+
self.ismethod
|
|
290
|
+
and not self.isinit
|
|
291
|
+
and self._parent is not None
|
|
292
|
+
and isinstance(self._parent, _ast.nodes.ClassDef)
|
|
293
|
+
):
|
|
286
294
|
for ancestor in self._parent.ancestors():
|
|
287
295
|
if self.name in ancestor and isinstance(
|
|
288
296
|
ancestor[self.name],
|
|
289
|
-
_ast.FunctionDef,
|
|
297
|
+
_ast.nodes.FunctionDef,
|
|
290
298
|
):
|
|
291
299
|
return True
|
|
292
300
|
|
|
@@ -324,6 +332,7 @@ class Function(Parent): # pylint: disable=too-many-instance-attributes
|
|
|
324
332
|
| _ast.nodes.Module
|
|
325
333
|
| _ast.nodes.ClassDef
|
|
326
334
|
| _ast.nodes.Lambda
|
|
335
|
+
| None
|
|
327
336
|
):
|
|
328
337
|
"""Function's parent node."""
|
|
329
338
|
return self._parent
|
|
@@ -331,7 +340,7 @@ class Function(Parent): # pylint: disable=too-many-instance-attributes
|
|
|
331
340
|
@property
|
|
332
341
|
def lineno(self) -> int:
|
|
333
342
|
"""Line number of function declaration."""
|
|
334
|
-
return self._lineno
|
|
343
|
+
return self._lineno or 0
|
|
335
344
|
|
|
336
345
|
@property
|
|
337
346
|
def signature(self) -> _Signature:
|