docsig 0.90.3__tar.gz → 0.91.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.90.3 → docsig-0.91.1}/PKG-INFO +8 -8
- {docsig-0.90.3 → docsig-0.91.1}/README.rst +7 -7
- docsig-0.90.3/docsig/_report.py → docsig-0.91.1/docsig/_checker.py +34 -168
- {docsig-0.90.3 → docsig-0.91.1}/docsig/_config.py +8 -7
- {docsig-0.90.3 → docsig-0.91.1}/docsig/_core.py +3 -3
- {docsig-0.90.3 → docsig-0.91.1}/docsig/_decorators.py +1 -1
- docsig-0.91.1/docsig/_diagnostic.py +155 -0
- {docsig-0.90.3 → docsig-0.91.1}/docsig/_main.py +0 -1
- {docsig-0.90.3 → docsig-0.91.1}/docsig/_parsers.py +3 -3
- docsig-0.91.1/docsig/_report.py +94 -0
- {docsig-0.90.3 → docsig-0.91.1}/docsig/_stub.py +2 -16
- docsig-0.90.3/docsig/_check.py → docsig-0.91.1/docsig/_traverse.py +5 -3
- {docsig-0.90.3 → docsig-0.91.1}/docsig/_version.py +1 -1
- {docsig-0.90.3 → docsig-0.91.1}/pyproject.toml +3 -3
- {docsig-0.90.3 → docsig-0.91.1}/LICENSE +0 -0
- {docsig-0.90.3 → docsig-0.91.1}/LICENSES/SPHINX_LICENSE +0 -0
- {docsig-0.90.3 → docsig-0.91.1}/docsig/__init__.py +0 -0
- {docsig-0.90.3 → docsig-0.91.1}/docsig/__main__.py +0 -0
- {docsig-0.90.3 → docsig-0.91.1}/docsig/_directives.py +0 -0
- {docsig-0.90.3 → docsig-0.91.1}/docsig/_files.py +0 -0
- {docsig-0.90.3 → docsig-0.91.1}/docsig/_hooks.py +0 -0
- {docsig-0.90.3 → docsig-0.91.1}/docsig/_module.py +0 -0
- {docsig-0.90.3 → docsig-0.91.1}/docsig/_utils.py +0 -0
- {docsig-0.90.3 → docsig-0.91.1}/docsig/_vendor/sphinx/ext/napoleon.py +0 -0
- {docsig-0.90.3 → docsig-0.91.1}/docsig/messages.py +0 -0
- {docsig-0.90.3 → docsig-0.91.1}/docsig/plugin/__init__.py +0 -0
- {docsig-0.90.3 → docsig-0.91.1}/docsig/plugin/_flake8.py +0 -0
- {docsig-0.90.3 → docsig-0.91.1}/docsig/plugin/_validate_pyproject.py +0 -0
- {docsig-0.90.3 → docsig-0.91.1}/docsig/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: docsig
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.91.1
|
|
4
4
|
Summary: Check signature params for proper documentation
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -59,7 +59,7 @@ Description-Content-Type: text/x-rst
|
|
|
59
59
|
:target: https://docsig.io/en/latest/?badge=latest
|
|
60
60
|
:alt: readthedocs.org
|
|
61
61
|
.. |python3.10| image:: https://img.shields.io/badge/python-3.10-blue.svg
|
|
62
|
-
:target: https://www.python.org/downloads/release/python-
|
|
62
|
+
:target: https://www.python.org/downloads/release/python-310
|
|
63
63
|
:alt: python3.10
|
|
64
64
|
.. |Black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
|
65
65
|
:target: https://github.com/psf/black
|
|
@@ -80,7 +80,7 @@ Description-Content-Type: text/x-rst
|
|
|
80
80
|
:target: https://github.com/jshwi/docsig/actions/workflows/build-intellij-plugin.yaml
|
|
81
81
|
:alt: Build
|
|
82
82
|
.. |VS Code Build| image:: https://github.com/jshwi/docsig/actions/workflows/build-vscode-extension.yaml/badge.svg
|
|
83
|
-
:target: https://github.com/jshwi/docsig/actions/workflows/build-vscode-extension.yaml
|
|
83
|
+
:target: https://github.com/jshwi/docsig/actions/workflows/build-vscode-extension.yaml
|
|
84
84
|
:alt: Build
|
|
85
85
|
.. |VS Code Extension| image:: https://img.shields.io/badge/vscode%20extension-v1.0.1-007ACC
|
|
86
86
|
:target: https://marketplace.visualstudio.com/items?itemName=jshwi.docsig
|
|
@@ -125,14 +125,14 @@ Commandline
|
|
|
125
125
|
[--check-protected-class-methods] [--ignore-args] [--ignore-kwargs]
|
|
126
126
|
[--ignore-no-params] [-d LIST] [-t LIST] [-e PATTERN] [-E PATH [PATH ...]]
|
|
127
127
|
[-i] [-s STR]
|
|
128
|
-
[path
|
|
128
|
+
[path ...]
|
|
129
129
|
|
|
130
130
|
Check signature params for proper documentation
|
|
131
131
|
|
|
132
132
|
positional arguments:
|
|
133
133
|
path directories or files to check
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
options:
|
|
136
136
|
-h, --help show this help message and exit
|
|
137
137
|
-V, --version show program's version number and exit
|
|
138
138
|
-l, --list-checks display a list of all checks and their messages
|
|
@@ -206,7 +206,7 @@ ensure your installation has registered `docsig`
|
|
|
206
206
|
.. code-block:: console
|
|
207
207
|
|
|
208
208
|
$ flake8 --version
|
|
209
|
-
7.3.0 (docsig: 0.
|
|
209
|
+
7.3.0 (docsig: 0.91.1, mccabe: 0.7.0, pycodestyle: 2.14.0, pyflakes: 3.4.0) CPython 3.10.19 on Darwin
|
|
210
210
|
|
|
211
211
|
And now use `flake8` to lint your files
|
|
212
212
|
|
|
@@ -292,7 +292,7 @@ Standalone
|
|
|
292
292
|
|
|
293
293
|
repos:
|
|
294
294
|
- repo: https://github.com/jshwi/docsig
|
|
295
|
-
rev: v0.
|
|
295
|
+
rev: v0.91.1
|
|
296
296
|
hooks:
|
|
297
297
|
- id: docsig
|
|
298
298
|
args:
|
|
@@ -311,7 +311,7 @@ or integrated with ``flake8``
|
|
|
311
311
|
hooks:
|
|
312
312
|
- id: flake8
|
|
313
313
|
additional_dependencies:
|
|
314
|
-
- docsig==0.
|
|
314
|
+
- docsig==0.91.1
|
|
315
315
|
args:
|
|
316
316
|
- "--sig-check-class"
|
|
317
317
|
- "--sig-check-dunders"
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
:target: https://docsig.io/en/latest/?badge=latest
|
|
32
32
|
:alt: readthedocs.org
|
|
33
33
|
.. |python3.10| image:: https://img.shields.io/badge/python-3.10-blue.svg
|
|
34
|
-
:target: https://www.python.org/downloads/release/python-
|
|
34
|
+
:target: https://www.python.org/downloads/release/python-310
|
|
35
35
|
:alt: python3.10
|
|
36
36
|
.. |Black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
|
37
37
|
:target: https://github.com/psf/black
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
:target: https://github.com/jshwi/docsig/actions/workflows/build-intellij-plugin.yaml
|
|
53
53
|
:alt: Build
|
|
54
54
|
.. |VS Code Build| image:: https://github.com/jshwi/docsig/actions/workflows/build-vscode-extension.yaml/badge.svg
|
|
55
|
-
:target: https://github.com/jshwi/docsig/actions/workflows/build-vscode-extension.yaml
|
|
55
|
+
:target: https://github.com/jshwi/docsig/actions/workflows/build-vscode-extension.yaml
|
|
56
56
|
:alt: Build
|
|
57
57
|
.. |VS Code Extension| image:: https://img.shields.io/badge/vscode%20extension-v1.0.1-007ACC
|
|
58
58
|
:target: https://marketplace.visualstudio.com/items?itemName=jshwi.docsig
|
|
@@ -97,14 +97,14 @@ Commandline
|
|
|
97
97
|
[--check-protected-class-methods] [--ignore-args] [--ignore-kwargs]
|
|
98
98
|
[--ignore-no-params] [-d LIST] [-t LIST] [-e PATTERN] [-E PATH [PATH ...]]
|
|
99
99
|
[-i] [-s STR]
|
|
100
|
-
[path
|
|
100
|
+
[path ...]
|
|
101
101
|
|
|
102
102
|
Check signature params for proper documentation
|
|
103
103
|
|
|
104
104
|
positional arguments:
|
|
105
105
|
path directories or files to check
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
options:
|
|
108
108
|
-h, --help show this help message and exit
|
|
109
109
|
-V, --version show program's version number and exit
|
|
110
110
|
-l, --list-checks display a list of all checks and their messages
|
|
@@ -178,7 +178,7 @@ ensure your installation has registered `docsig`
|
|
|
178
178
|
.. code-block:: console
|
|
179
179
|
|
|
180
180
|
$ flake8 --version
|
|
181
|
-
7.3.0 (docsig: 0.
|
|
181
|
+
7.3.0 (docsig: 0.91.1, mccabe: 0.7.0, pycodestyle: 2.14.0, pyflakes: 3.4.0) CPython 3.10.19 on Darwin
|
|
182
182
|
|
|
183
183
|
And now use `flake8` to lint your files
|
|
184
184
|
|
|
@@ -264,7 +264,7 @@ Standalone
|
|
|
264
264
|
|
|
265
265
|
repos:
|
|
266
266
|
- repo: https://github.com/jshwi/docsig
|
|
267
|
-
rev: v0.
|
|
267
|
+
rev: v0.91.1
|
|
268
268
|
hooks:
|
|
269
269
|
- id: docsig
|
|
270
270
|
args:
|
|
@@ -283,7 +283,7 @@ or integrated with ``flake8``
|
|
|
283
283
|
hooks:
|
|
284
284
|
- id: flake8
|
|
285
285
|
additional_dependencies:
|
|
286
|
-
- docsig==0.
|
|
286
|
+
- docsig==0.91.1
|
|
287
287
|
args:
|
|
288
288
|
- "--sig-check-class"
|
|
289
289
|
- "--sig-check-dunders"
|
|
@@ -1,24 +1,19 @@
|
|
|
1
1
|
"""
|
|
2
|
-
docsig.
|
|
3
|
-
|
|
2
|
+
docsig._checker
|
|
3
|
+
===============
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
defines Failure (per-function validation results), Failed (one reported
|
|
7
|
-
issue), Failures (sequence of failures), and report() to print them and
|
|
8
|
-
return the highest exit code.
|
|
5
|
+
Run docstring and signature checks for a single function.
|
|
9
6
|
"""
|
|
10
7
|
|
|
11
8
|
import contextlib as _contextlib
|
|
12
|
-
import json as _json
|
|
13
|
-
import os as _os
|
|
14
|
-
import sys as _sys
|
|
15
9
|
import typing as _t
|
|
16
|
-
from dataclasses import dataclass as _dataclass
|
|
17
|
-
from warnings import warn as _warn
|
|
18
10
|
|
|
19
11
|
import astroid as _ast
|
|
20
12
|
|
|
21
13
|
from ._config import Config as _Config
|
|
14
|
+
from ._diagnostic import Collector as _Collector
|
|
15
|
+
from ._diagnostic import Diagnostic as _Diagnostic
|
|
16
|
+
from ._diagnostic import FunctionResult as _FunctionResult
|
|
22
17
|
from ._module import Function as _Function
|
|
23
18
|
from ._stub import UNNAMED as _UNNAMED
|
|
24
19
|
from ._stub import VALID_DESCRIPTION as _VALID_DESCRIPTION
|
|
@@ -27,8 +22,6 @@ from ._stub import Params as _Params
|
|
|
27
22
|
from ._stub import RetType as _RetType
|
|
28
23
|
from ._utils import almost_equal as _almost_equal
|
|
29
24
|
from ._utils import sentence_tokenizer as _sentence_tokenizer
|
|
30
|
-
from .messages import NEW as _NEW
|
|
31
|
-
from .messages import TEMPLATE as _TEMPLATE
|
|
32
25
|
from .messages import E as _E
|
|
33
26
|
from .messages import Message as _Message
|
|
34
27
|
|
|
@@ -36,56 +29,7 @@ _MIN_MATCH = 0.8
|
|
|
36
29
|
_MAX_MATCH = 1.0
|
|
37
30
|
|
|
38
31
|
|
|
39
|
-
|
|
40
|
-
"""Run configured checks for one function and return the result.
|
|
41
|
-
|
|
42
|
-
:param func: Function under check.
|
|
43
|
-
:param config: Configuration object.
|
|
44
|
-
:return: Collected diagnostics for the function.
|
|
45
|
-
"""
|
|
46
|
-
return FunctionChecker(func, config)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
class RetCode:
|
|
50
|
-
"""RetCode object.
|
|
51
|
-
|
|
52
|
-
:param code: Initial return code, if any, otherwise zero.
|
|
53
|
-
"""
|
|
54
|
-
|
|
55
|
-
def __init__(self, code: int = 0) -> None:
|
|
56
|
-
self._data = [code]
|
|
57
|
-
|
|
58
|
-
def add(self, code: int) -> None:
|
|
59
|
-
"""Add a return code.
|
|
60
|
-
|
|
61
|
-
:param code: Return code to add.
|
|
62
|
-
"""
|
|
63
|
-
self._data.append(code)
|
|
64
|
-
|
|
65
|
-
@property
|
|
66
|
-
def result(self) -> int:
|
|
67
|
-
"""Maximum return code."""
|
|
68
|
-
return max(self._data)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
class Failures(list["FunctionChecker"]):
|
|
72
|
-
"""Sequence of Failure instances (one per function checked)."""
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
@_dataclass(frozen=True, order=True)
|
|
76
|
-
class Diagnostic: # pylint: disable=too-few-public-methods
|
|
77
|
-
"""Single reported issue."""
|
|
78
|
-
|
|
79
|
-
name: str
|
|
80
|
-
ref: str
|
|
81
|
-
description: str
|
|
82
|
-
symbolic: str
|
|
83
|
-
lineno: int
|
|
84
|
-
hint: str | None = None
|
|
85
|
-
new: bool = False
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
class FunctionChecker(list[Diagnostic]):
|
|
32
|
+
class FunctionChecker: # pylint: disable=too-few-public-methods
|
|
89
33
|
"""Collect docstring and signature failures for one function.
|
|
90
34
|
|
|
91
35
|
Runs configured checks and appends Failed entries for each
|
|
@@ -96,9 +40,9 @@ class FunctionChecker(list[Diagnostic]):
|
|
|
96
40
|
"""
|
|
97
41
|
|
|
98
42
|
def __init__(self, func: _Function, config: _Config) -> None:
|
|
99
|
-
super().__init__()
|
|
100
|
-
self._retcode = RetCode()
|
|
101
43
|
self._func = func
|
|
44
|
+
self._config = config
|
|
45
|
+
self._diagnostics: list[_Diagnostic] = []
|
|
102
46
|
if config.target:
|
|
103
47
|
self._func.messages.extend(
|
|
104
48
|
i for i in _E.all if i not in config.target
|
|
@@ -113,8 +57,15 @@ class FunctionChecker(list[Diagnostic]):
|
|
|
113
57
|
):
|
|
114
58
|
self._name = f"{self._func.parent.name}.{self._name}"
|
|
115
59
|
|
|
60
|
+
self._collector = _Collector(func, self._name, self._func.lineno)
|
|
61
|
+
|
|
62
|
+
def run(self) -> _FunctionResult:
|
|
63
|
+
"""Run the function checks and return the result.
|
|
64
|
+
|
|
65
|
+
:return: Function result.
|
|
66
|
+
"""
|
|
116
67
|
if self._func.error is not None:
|
|
117
|
-
self.
|
|
68
|
+
self._collector.retcode.add(2)
|
|
118
69
|
self._sig9xx_error()
|
|
119
70
|
else:
|
|
120
71
|
self._sig0xx_config()
|
|
@@ -128,9 +79,9 @@ class FunctionChecker(list[Diagnostic]):
|
|
|
128
79
|
sig = self._func.signature.args.get(index)
|
|
129
80
|
self._sig4xx_parameters(doc, sig)
|
|
130
81
|
|
|
131
|
-
self._sig5xx_returns(
|
|
82
|
+
self._sig5xx_returns(self._config.check.property_returns)
|
|
132
83
|
|
|
133
|
-
self.
|
|
84
|
+
return _FunctionResult(self._name, self._func.lineno, self._collector)
|
|
134
85
|
|
|
135
86
|
def _add(
|
|
136
87
|
self,
|
|
@@ -138,18 +89,7 @@ class FunctionChecker(list[Diagnostic]):
|
|
|
138
89
|
include_hint: bool = False,
|
|
139
90
|
**kwargs: _t.Any,
|
|
140
91
|
) -> None:
|
|
141
|
-
self.
|
|
142
|
-
failed = Diagnostic(
|
|
143
|
-
self._name,
|
|
144
|
-
value.ref,
|
|
145
|
-
value.description.format(**kwargs),
|
|
146
|
-
value.symbolic,
|
|
147
|
-
self.lineno,
|
|
148
|
-
value.hint if include_hint else None,
|
|
149
|
-
value.new,
|
|
150
|
-
)
|
|
151
|
-
if value not in self._func.messages and failed not in self:
|
|
152
|
-
super().append(failed)
|
|
92
|
+
self._collector.add(value, include_hint=include_hint, **kwargs)
|
|
153
93
|
|
|
154
94
|
@staticmethod
|
|
155
95
|
def _normalize_params(from_: _Params, to: _Params) -> None:
|
|
@@ -231,15 +171,17 @@ class FunctionChecker(list[Diagnostic]):
|
|
|
231
171
|
|
|
232
172
|
def _sig2xx_signature(self) -> None:
|
|
233
173
|
if self._func.docstring.args.duplicated:
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
174
|
+
# reduce each duplicate group to one representative without
|
|
175
|
+
# mutating the list during iteration (which skips items)
|
|
176
|
+
seen: set[str | None] = set()
|
|
177
|
+
deduped: list[_Param] = []
|
|
178
|
+
for arg in self._func.docstring.args:
|
|
179
|
+
if arg.name not in seen:
|
|
180
|
+
seen.add(arg.name)
|
|
181
|
+
deduped.append(arg)
|
|
182
|
+
|
|
183
|
+
list.clear(self._func.docstring.args)
|
|
184
|
+
list.extend(self._func.docstring.args, deduped)
|
|
243
185
|
|
|
244
186
|
# duplicate-params-found
|
|
245
187
|
self._add(_E[201])
|
|
@@ -297,6 +239,8 @@ class FunctionChecker(list[Diagnostic]):
|
|
|
297
239
|
not in (
|
|
298
240
|
"`",
|
|
299
241
|
".",
|
|
242
|
+
"!",
|
|
243
|
+
"?",
|
|
300
244
|
)
|
|
301
245
|
):
|
|
302
246
|
self._add(_E[306])
|
|
@@ -371,7 +315,7 @@ class FunctionChecker(list[Diagnostic]):
|
|
|
371
315
|
# invalid-syntax
|
|
372
316
|
if self._func.error is _ast.AstroidSyntaxError:
|
|
373
317
|
self._add(_E[901])
|
|
374
|
-
self.
|
|
318
|
+
self._collector.retcode.add(123)
|
|
375
319
|
# unicode-decode-error
|
|
376
320
|
if self._func.error is UnicodeDecodeError:
|
|
377
321
|
self._add(_E[902])
|
|
@@ -381,81 +325,3 @@ class FunctionChecker(list[Diagnostic]):
|
|
|
381
325
|
# duplicates-found-in-mros
|
|
382
326
|
if self._func.error is _ast.DuplicateBasesError:
|
|
383
327
|
self._add(_E[904])
|
|
384
|
-
|
|
385
|
-
@property
|
|
386
|
-
def name(self) -> str:
|
|
387
|
-
"""Qualified name (Class.method) when nested, else bare name."""
|
|
388
|
-
return self._name
|
|
389
|
-
|
|
390
|
-
@property
|
|
391
|
-
def lineno(self) -> int:
|
|
392
|
-
"""Line number of the function in the source."""
|
|
393
|
-
return self._func.lineno
|
|
394
|
-
|
|
395
|
-
@property
|
|
396
|
-
def retcode(self) -> int:
|
|
397
|
-
"""Exit code (non-zero if any check failed)."""
|
|
398
|
-
return self._retcode.result
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
# TODO: make report json by default and wrap with a reporter for cli
|
|
402
|
-
def report(
|
|
403
|
-
failures: Failures,
|
|
404
|
-
config: _Config,
|
|
405
|
-
file: str | None = None,
|
|
406
|
-
) -> int:
|
|
407
|
-
"""Print failures to stdout and return the highest exit code.
|
|
408
|
-
|
|
409
|
-
Iterates over failures, prints each with path, line header, and
|
|
410
|
-
messages, then returns the maximum retcode (0 or non-zero).
|
|
411
|
-
|
|
412
|
-
:param failures: Failures to print (one Failure per function).
|
|
413
|
-
:param config: Config for ANSI and formatting.
|
|
414
|
-
:param file: Module path when failures came from a file (optional).
|
|
415
|
-
:return: Exit code (non-zero if any check failed).
|
|
416
|
-
"""
|
|
417
|
-
format_json = _os.getenv("_DOCSIG_FORMAT_JSON") is not None
|
|
418
|
-
retcodes = RetCode()
|
|
419
|
-
output = []
|
|
420
|
-
obj = []
|
|
421
|
-
for result in failures:
|
|
422
|
-
retcodes.add(result.retcode)
|
|
423
|
-
path_prefix = f"{file}:" if file is not None else ""
|
|
424
|
-
header = f"{path_prefix}{result.lineno} in {result.name}"
|
|
425
|
-
if not config.no_ansi and _sys.stdout.isatty():
|
|
426
|
-
header = f"\033[35m{header}\033[0m"
|
|
427
|
-
|
|
428
|
-
output.append(header)
|
|
429
|
-
for item in result:
|
|
430
|
-
extra = None
|
|
431
|
-
if item.hint:
|
|
432
|
-
extra = f"hint: {item.hint}"
|
|
433
|
-
|
|
434
|
-
if item.new:
|
|
435
|
-
extra = "warning: please remember to fix this or disable it"
|
|
436
|
-
_warn(_NEW.format(ref=item.ref), FutureWarning, stacklevel=3)
|
|
437
|
-
|
|
438
|
-
msg = _TEMPLATE.format(
|
|
439
|
-
ref=item.ref,
|
|
440
|
-
description=item.description,
|
|
441
|
-
symbolic=item.symbolic,
|
|
442
|
-
)
|
|
443
|
-
output.append(f" {msg}")
|
|
444
|
-
if extra is not None:
|
|
445
|
-
output.append(f" {extra}")
|
|
446
|
-
|
|
447
|
-
obj.append(
|
|
448
|
-
{
|
|
449
|
-
"line": None if result.retcode == 2 else item.lineno,
|
|
450
|
-
"message": msg,
|
|
451
|
-
"exit": result.retcode,
|
|
452
|
-
},
|
|
453
|
-
)
|
|
454
|
-
|
|
455
|
-
if format_json:
|
|
456
|
-
print(_json.dumps(obj).strip()) # pragma: no cover
|
|
457
|
-
|
|
458
|
-
elif output:
|
|
459
|
-
print("\n".join(output))
|
|
460
|
-
|
|
461
|
-
return retcodes.result
|
|
@@ -156,17 +156,10 @@ def build_parser() -> _ArgumentParser:
|
|
|
156
156
|
action="store_true",
|
|
157
157
|
help="check nested functions and classes",
|
|
158
158
|
)
|
|
159
|
-
parser.add_argument(
|
|
160
|
-
"-o",
|
|
161
|
-
action="store_true",
|
|
162
|
-
help=_argparse.SUPPRESS,
|
|
163
|
-
dest="check_overridden",
|
|
164
|
-
)
|
|
165
159
|
parser.add_argument(
|
|
166
160
|
"--check-overridden",
|
|
167
161
|
action="store_true",
|
|
168
162
|
help="check overridden methods",
|
|
169
|
-
dest="check_overridden",
|
|
170
163
|
)
|
|
171
164
|
parser.add_argument(
|
|
172
165
|
"--check-property-returns",
|
|
@@ -332,6 +325,14 @@ class Config:
|
|
|
332
325
|
|
|
333
326
|
Groups check, ignore settings, and run options so the core runner
|
|
334
327
|
takes a single config object instead of many parameters.
|
|
328
|
+
|
|
329
|
+
:param check: Configuration for which docstrings and members to
|
|
330
|
+
check.
|
|
331
|
+
:param ignore: Configuration for which docstring elements to ignore.
|
|
332
|
+
:param filters: Configuration for which paths to filter.
|
|
333
|
+
:param target: List of errors to target.
|
|
334
|
+
:param disable: List of errors to disable.
|
|
335
|
+
:param no_ansi: Disable ANSI output.
|
|
335
336
|
"""
|
|
336
337
|
|
|
337
338
|
check: Check = _field(default_factory=Check)
|
|
@@ -12,17 +12,17 @@ from pathlib import Path as _Path
|
|
|
12
12
|
from pprint import pformat as _pformat
|
|
13
13
|
|
|
14
14
|
from . import _decorators
|
|
15
|
-
from ._check import run_checks as _run_checks
|
|
16
15
|
from ._config import Check as _Check
|
|
17
16
|
from ._config import Config as _Config
|
|
18
17
|
from ._config import Filters as _Filters
|
|
19
18
|
from ._config import Ignore as _Ignore
|
|
19
|
+
from ._diagnostic import Failures as _Failures
|
|
20
|
+
from ._diagnostic import RetCode as _RetCode
|
|
20
21
|
from ._files import Files as _Files
|
|
21
22
|
from ._parsers import parse_from_file as _parse_from_file
|
|
22
23
|
from ._parsers import parse_from_string as _parse_from_string
|
|
23
|
-
from ._report import Failures as _Failures
|
|
24
|
-
from ._report import RetCode as _RetCode
|
|
25
24
|
from ._report import report as _report
|
|
25
|
+
from ._traverse import run_checks as _run_checks
|
|
26
26
|
from ._utils import get_parent_that_has as _get_parent_that_has
|
|
27
27
|
from ._utils import print_checks as _print_checks
|
|
28
28
|
from .messages import E as _E
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""
|
|
2
|
+
docsig._diagnostic
|
|
3
|
+
==================
|
|
4
|
+
|
|
5
|
+
Diagnostic records and per-function check results for docsig runs.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import typing as _t
|
|
9
|
+
from dataclasses import dataclass as _dataclass
|
|
10
|
+
|
|
11
|
+
from ._module import Function as _Function
|
|
12
|
+
from .messages import Message as _Message
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Failures(list["FunctionResult"]):
|
|
16
|
+
"""Sequence of result instances (one per function checked)."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RetCode:
|
|
20
|
+
"""RetCode object.
|
|
21
|
+
|
|
22
|
+
:param code: Initial return code, if any, otherwise zero.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, code: int = 0) -> None:
|
|
26
|
+
self._data = [code]
|
|
27
|
+
|
|
28
|
+
def add(self, code: int) -> None:
|
|
29
|
+
"""Add a return code.
|
|
30
|
+
|
|
31
|
+
:param code: Return code to add.
|
|
32
|
+
"""
|
|
33
|
+
self._data.append(code)
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def result(self) -> int:
|
|
37
|
+
"""Maximum return code."""
|
|
38
|
+
return max(self._data)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@_dataclass(frozen=True, order=True)
|
|
42
|
+
class Diagnostic: # pylint: disable=too-few-public-methods
|
|
43
|
+
"""Single reported issue for one function."""
|
|
44
|
+
|
|
45
|
+
name: str
|
|
46
|
+
ref: str
|
|
47
|
+
description: str
|
|
48
|
+
symbolic: str
|
|
49
|
+
lineno: int
|
|
50
|
+
hint: str | None = None
|
|
51
|
+
new: bool = False
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class Collector:
|
|
55
|
+
"""Collect diagnostics and exit weights for one function.
|
|
56
|
+
|
|
57
|
+
:param func: Function to collect diagnostics for.
|
|
58
|
+
:param qualified_name: Qualified name (Class.method) when nested,
|
|
59
|
+
else bare name.
|
|
60
|
+
:param lineno: Line number of the function in the source.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(
|
|
64
|
+
self,
|
|
65
|
+
func: _Function,
|
|
66
|
+
qualified_name: str,
|
|
67
|
+
lineno: int,
|
|
68
|
+
) -> None:
|
|
69
|
+
self._func = func
|
|
70
|
+
self._qualified_name = qualified_name
|
|
71
|
+
self._lineno = lineno
|
|
72
|
+
self._diagnostics: list[Diagnostic] = []
|
|
73
|
+
self._retcode = RetCode()
|
|
74
|
+
|
|
75
|
+
def add(
|
|
76
|
+
self,
|
|
77
|
+
value: _Message,
|
|
78
|
+
include_hint: bool = False,
|
|
79
|
+
**kwargs: _t.Any,
|
|
80
|
+
) -> None:
|
|
81
|
+
"""Add a diagnostic message.
|
|
82
|
+
|
|
83
|
+
:param value: Message to add.
|
|
84
|
+
:param include_hint: Whether to include the hint.
|
|
85
|
+
:param kwargs: Additional arguments to format the description.
|
|
86
|
+
"""
|
|
87
|
+
self._retcode.add(int(not value.new))
|
|
88
|
+
diagnostic = Diagnostic(
|
|
89
|
+
self._qualified_name,
|
|
90
|
+
value.ref,
|
|
91
|
+
value.description.format(**kwargs),
|
|
92
|
+
value.symbolic,
|
|
93
|
+
self._lineno,
|
|
94
|
+
value.hint if include_hint else None,
|
|
95
|
+
value.new,
|
|
96
|
+
)
|
|
97
|
+
if (
|
|
98
|
+
value not in self._func.messages
|
|
99
|
+
and diagnostic not in self._diagnostics
|
|
100
|
+
):
|
|
101
|
+
self._diagnostics.append(diagnostic)
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def diagnostics(self) -> list[Diagnostic]:
|
|
105
|
+
"""Diagnostics sorted for stable output."""
|
|
106
|
+
return sorted(self._diagnostics)
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def retcode(self) -> RetCode:
|
|
110
|
+
"""Exit code (non-zero if any check failed)."""
|
|
111
|
+
return self._retcode
|
|
112
|
+
|
|
113
|
+
def __bool__(self) -> bool:
|
|
114
|
+
return bool(self._diagnostics)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class FunctionResult:
|
|
118
|
+
"""Diagnostics and exit code for one checked function.
|
|
119
|
+
|
|
120
|
+
:param name: Qualified name (Class.method) when nested, else bare
|
|
121
|
+
name.
|
|
122
|
+
:param lineno: Line number of the function in the source.
|
|
123
|
+
:param collector: Collector for the function.
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
def __init__(
|
|
127
|
+
self,
|
|
128
|
+
name: str,
|
|
129
|
+
lineno: int,
|
|
130
|
+
collector: Collector,
|
|
131
|
+
) -> None:
|
|
132
|
+
self._name = name
|
|
133
|
+
self._lineno = lineno
|
|
134
|
+
self._collector = collector
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def name(self) -> str:
|
|
138
|
+
"""Qualified name (Class.method) when nested, else bare name."""
|
|
139
|
+
return self._name
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def lineno(self) -> int:
|
|
143
|
+
"""Line number of the function in the source."""
|
|
144
|
+
return self._lineno
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def retcode(self) -> int:
|
|
148
|
+
"""Exit code (non-zero if any check failed)."""
|
|
149
|
+
return self._collector.retcode.result
|
|
150
|
+
|
|
151
|
+
def __iter__(self) -> _t.Iterator[Diagnostic]:
|
|
152
|
+
return iter(self._collector.diagnostics)
|
|
153
|
+
|
|
154
|
+
def __bool__(self) -> bool:
|
|
155
|
+
return bool(self._collector)
|
|
@@ -25,9 +25,9 @@ def parse_from_string(
|
|
|
25
25
|
) -> _Parent:
|
|
26
26
|
"""Build a Parent from a string of Python code.
|
|
27
27
|
|
|
28
|
-
Parses AST and comment directives (AST does not include comments,
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
Parses AST and comment directives (AST does not include comments, so
|
|
29
|
+
directives are parsed separately). On syntax error, returns a Parent
|
|
30
|
+
with an error set.
|
|
31
31
|
|
|
32
32
|
:param code: Python source to parse.
|
|
33
33
|
:param config: Configuration object.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""
|
|
2
|
+
docsig._report
|
|
3
|
+
==============
|
|
4
|
+
|
|
5
|
+
Format and print docstring-check diagnostics for CLI and tooling.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json as _json
|
|
9
|
+
import os as _os
|
|
10
|
+
import sys as _sys
|
|
11
|
+
from warnings import warn as _warn
|
|
12
|
+
|
|
13
|
+
from ._checker import FunctionChecker as _FunctionChecker
|
|
14
|
+
from ._config import Config as _Config
|
|
15
|
+
from ._diagnostic import Failures as _Failures
|
|
16
|
+
from ._diagnostic import FunctionResult as _FunctionResult
|
|
17
|
+
from ._diagnostic import RetCode as _RetCode
|
|
18
|
+
from ._module import Function as _Function
|
|
19
|
+
from .messages import NEW as _NEW
|
|
20
|
+
from .messages import TEMPLATE as _TEMPLATE
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def check_function(func: _Function, config: _Config) -> _FunctionResult:
|
|
24
|
+
"""Run configured checks for one function and return the result.
|
|
25
|
+
|
|
26
|
+
:param func: Function under check.
|
|
27
|
+
:param config: Configuration object.
|
|
28
|
+
:return: Collected diagnostics for the function.
|
|
29
|
+
"""
|
|
30
|
+
return _FunctionChecker(func, config).run()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# TODO: make report json by default and wrap with a reporter for cli
|
|
34
|
+
def report(
|
|
35
|
+
failures: _Failures,
|
|
36
|
+
config: _Config,
|
|
37
|
+
file: str | None = None,
|
|
38
|
+
) -> int:
|
|
39
|
+
"""Print failures and return the highest exit code.
|
|
40
|
+
|
|
41
|
+
Iterates over failures, prints each with path, line header, and
|
|
42
|
+
messages, then returns the maximum retcode (0 or non-zero).
|
|
43
|
+
|
|
44
|
+
:param failures: Failures to print (one FunctionResult per
|
|
45
|
+
function).
|
|
46
|
+
:param config: Config for ANSI and formatting.
|
|
47
|
+
:param file: Module path when failures came from a file (optional).
|
|
48
|
+
:return: Exit code (non-zero if any check failed).
|
|
49
|
+
"""
|
|
50
|
+
format_json = _os.getenv("_DOCSIG_FORMAT_JSON") is not None
|
|
51
|
+
retcodes = _RetCode()
|
|
52
|
+
output = []
|
|
53
|
+
obj = []
|
|
54
|
+
for result in failures:
|
|
55
|
+
retcodes.add(result.retcode)
|
|
56
|
+
path_prefix = f"{file}:" if file is not None else ""
|
|
57
|
+
header = f"{path_prefix}{result.lineno} in {result.name}"
|
|
58
|
+
if not config.no_ansi and _sys.stdout.isatty():
|
|
59
|
+
header = f"\033[35m{header}\033[0m"
|
|
60
|
+
|
|
61
|
+
output.append(header)
|
|
62
|
+
for item in result:
|
|
63
|
+
extra = None
|
|
64
|
+
if item.hint:
|
|
65
|
+
extra = f"hint: {item.hint}"
|
|
66
|
+
|
|
67
|
+
if item.new:
|
|
68
|
+
extra = "warning: please remember to fix this or disable it"
|
|
69
|
+
_warn(_NEW.format(ref=item.ref), FutureWarning, stacklevel=3)
|
|
70
|
+
|
|
71
|
+
msg = _TEMPLATE.format(
|
|
72
|
+
ref=item.ref,
|
|
73
|
+
description=item.description,
|
|
74
|
+
symbolic=item.symbolic,
|
|
75
|
+
)
|
|
76
|
+
output.append(f" {msg}")
|
|
77
|
+
if extra is not None:
|
|
78
|
+
output.append(f" {extra}")
|
|
79
|
+
|
|
80
|
+
obj.append(
|
|
81
|
+
{
|
|
82
|
+
"line": None if result.retcode == 2 else item.lineno,
|
|
83
|
+
"message": msg,
|
|
84
|
+
"exit": result.retcode,
|
|
85
|
+
},
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if format_json:
|
|
89
|
+
print(_json.dumps(obj).strip()) # pragma: no cover
|
|
90
|
+
|
|
91
|
+
elif output:
|
|
92
|
+
print("\n".join(output))
|
|
93
|
+
|
|
94
|
+
return retcodes.result
|
|
@@ -179,7 +179,6 @@ class Params(list[Param]):
|
|
|
179
179
|
def __init__(self, ignore: _Ignore) -> None:
|
|
180
180
|
super().__init__()
|
|
181
181
|
self._ignore = ignore
|
|
182
|
-
self._duplicates: list[Param] = []
|
|
183
182
|
|
|
184
183
|
def append(self, value: Param) -> None:
|
|
185
184
|
if not value.isprotected and any(
|
|
@@ -219,20 +218,7 @@ class Params(list[Param]):
|
|
|
219
218
|
|
|
220
219
|
Only names are compared; descriptions are ignored.
|
|
221
220
|
"""
|
|
222
|
-
for
|
|
223
|
-
if v > 1:
|
|
224
|
-
for i in self:
|
|
225
|
-
if i.name == k:
|
|
226
|
-
# record the duplicates for later analysis
|
|
227
|
-
self._duplicates.append(i)
|
|
228
|
-
return True
|
|
229
|
-
|
|
230
|
-
return False
|
|
231
|
-
|
|
232
|
-
@property
|
|
233
|
-
def duplicates(self) -> list[Param]:
|
|
234
|
-
"""Params that share a name."""
|
|
235
|
-
return self._duplicates
|
|
221
|
+
return any(v > 1 for v in _Counter(i.name for i in self).values())
|
|
236
222
|
|
|
237
223
|
|
|
238
224
|
class _Stub:
|
|
@@ -394,7 +380,7 @@ class Docstring(_Stub):
|
|
|
394
380
|
indent_anomaly = cls._indent_anomaly(node.value)
|
|
395
381
|
string = cls._normalize_docstring(node.value)
|
|
396
382
|
match = _re.search(
|
|
397
|
-
r"^[ \t]*:(?:returns?|yields
|
|
383
|
+
r"^[ \t]*:(?:returns?|yields?|rtype):\s*(.*)",
|
|
398
384
|
string,
|
|
399
385
|
_re.IGNORECASE | _re.MULTILINE,
|
|
400
386
|
)
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"""
|
|
2
|
-
docsig.
|
|
3
|
-
|
|
2
|
+
docsig._traverse
|
|
3
|
+
================
|
|
4
|
+
|
|
5
|
+
Traverse modules and run docstring/signature checks per function.
|
|
4
6
|
"""
|
|
5
7
|
|
|
6
8
|
from ._config import Config as _Config
|
|
9
|
+
from ._diagnostic import Failures as _Failures
|
|
7
10
|
from ._module import Function as _Function
|
|
8
11
|
from ._module import Parent as _Parent
|
|
9
|
-
from ._report import Failures as _Failures
|
|
10
12
|
from ._report import check_function as _check_function
|
|
11
13
|
|
|
12
14
|
|
|
@@ -29,7 +29,7 @@ maintainers = [
|
|
|
29
29
|
name = "docsig"
|
|
30
30
|
readme = "README.rst"
|
|
31
31
|
requires-python = ">=3.10"
|
|
32
|
-
version = "0.
|
|
32
|
+
version = "0.91.1"
|
|
33
33
|
|
|
34
34
|
[project.entry-points."flake8.extension"]
|
|
35
35
|
SIG = "docsig.plugin:Flake8"
|
|
@@ -47,7 +47,7 @@ line-length = 79
|
|
|
47
47
|
allow_dirty = true
|
|
48
48
|
commit = true
|
|
49
49
|
commit_args = "-sS"
|
|
50
|
-
current_version = "0.
|
|
50
|
+
current_version = "0.91.1"
|
|
51
51
|
message = "bump: version {current_version} → {new_version}"
|
|
52
52
|
sign_tags = true
|
|
53
53
|
tag = true
|
|
@@ -146,7 +146,7 @@ black = ">=24.4.2,<27.0.0"
|
|
|
146
146
|
bump-my-version = ">=1.4.1,<1.5.0"
|
|
147
147
|
deptry = ">=0.16.1,<0.26.0"
|
|
148
148
|
flynt = "^1.0.1"
|
|
149
|
-
gitpython = "^3.1.
|
|
149
|
+
gitpython = "^3.1.52"
|
|
150
150
|
isort = ">=5.13.2,<9.0.0"
|
|
151
151
|
mypy = ">=2.3.0,<3.0"
|
|
152
152
|
pre-commit = ">=3.3.3,<5.0.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|