docsig 0.62.0__tar.gz → 0.63.0__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.62.0 → docsig-0.63.0}/PKG-INFO +5 -5
- {docsig-0.62.0 → docsig-0.63.0}/README.rst +3 -3
- {docsig-0.62.0 → docsig-0.63.0}/docsig/_config.py +1 -1
- {docsig-0.62.0 → docsig-0.63.0}/docsig/_core.py +31 -27
- {docsig-0.62.0 → docsig-0.63.0}/docsig/_files.py +8 -17
- {docsig-0.62.0 → docsig-0.63.0}/docsig/_module.py +20 -35
- {docsig-0.62.0 → docsig-0.63.0}/docsig/_report.py +8 -0
- {docsig-0.62.0 → docsig-0.63.0}/docsig/_stub.py +50 -115
- {docsig-0.62.0 → docsig-0.63.0}/docsig/_utils.py +0 -10
- {docsig-0.62.0 → docsig-0.63.0}/docsig/_version.py +1 -1
- {docsig-0.62.0 → docsig-0.63.0}/docsig/plugin.py +3 -3
- {docsig-0.62.0 → docsig-0.63.0}/pyproject.toml +3 -3
- {docsig-0.62.0 → docsig-0.63.0}/LICENSE +0 -0
- {docsig-0.62.0 → docsig-0.63.0}/docsig/__init__.py +0 -0
- {docsig-0.62.0 → docsig-0.63.0}/docsig/__main__.py +0 -0
- {docsig-0.62.0 → docsig-0.63.0}/docsig/_decorators.py +0 -0
- {docsig-0.62.0 → docsig-0.63.0}/docsig/_directives.py +0 -0
- {docsig-0.62.0 → docsig-0.63.0}/docsig/_hooks.py +0 -0
- {docsig-0.62.0 → docsig-0.63.0}/docsig/_main.py +0 -0
- {docsig-0.62.0 → docsig-0.63.0}/docsig/messages.py +0 -0
- {docsig-0.62.0 → docsig-0.63.0}/docsig/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: docsig
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.63.0
|
|
4
4
|
Summary: Check signature params for proper documentation
|
|
5
5
|
Home-page: https://pypi.org/project/docsig/
|
|
6
6
|
License: MIT
|
|
@@ -20,7 +20,7 @@ Requires-Dist: Sphinx (>=7.0.0,<8.0.0)
|
|
|
20
20
|
Requires-Dist: astroid (>=3.0.1,<4.0.0)
|
|
21
21
|
Requires-Dist: pathspec (>=0.12.1,<0.13.0)
|
|
22
22
|
Requires-Dist: tomli (>=2.0.1,<3.0.0)
|
|
23
|
-
Requires-Dist: wcmatch (>=8.5.2,<
|
|
23
|
+
Requires-Dist: wcmatch (>=8.5.2,<11.0.0)
|
|
24
24
|
Project-URL: Documentation, https://docsig.readthedocs.io/en/latest
|
|
25
25
|
Project-URL: Repository, https://github.com/jshwi/docsig
|
|
26
26
|
Description-Content-Type: text/x-rst
|
|
@@ -180,7 +180,7 @@ ensure your installation has registered `docsig`
|
|
|
180
180
|
.. code-block:: console
|
|
181
181
|
|
|
182
182
|
$ flake8 --version
|
|
183
|
-
7.1.0 (docsig: 0.
|
|
183
|
+
7.1.0 (docsig: 0.63.0, mccabe: 0.7.0, pycodestyle: 2.12.0, pyflakes: 3.2.0) CPython 3.8.13 on Darwin
|
|
184
184
|
|
|
185
185
|
And now use `flake8` to lint your files
|
|
186
186
|
|
|
@@ -269,7 +269,7 @@ Standalone
|
|
|
269
269
|
|
|
270
270
|
repos:
|
|
271
271
|
- repo: https://github.com/jshwi/docsig
|
|
272
|
-
rev: v0.
|
|
272
|
+
rev: v0.63.0
|
|
273
273
|
hooks:
|
|
274
274
|
- id: docsig
|
|
275
275
|
args:
|
|
@@ -288,7 +288,7 @@ or integrated with ``flake8``
|
|
|
288
288
|
hooks:
|
|
289
289
|
- id: flake8
|
|
290
290
|
additional_dependencies:
|
|
291
|
-
- docsig==0.
|
|
291
|
+
- docsig==0.63.0
|
|
292
292
|
args:
|
|
293
293
|
- "--sig-check-class"
|
|
294
294
|
- "--sig-check-dunders"
|
|
@@ -153,7 +153,7 @@ ensure your installation has registered `docsig`
|
|
|
153
153
|
.. code-block:: console
|
|
154
154
|
|
|
155
155
|
$ flake8 --version
|
|
156
|
-
7.1.0 (docsig: 0.
|
|
156
|
+
7.1.0 (docsig: 0.63.0, mccabe: 0.7.0, pycodestyle: 2.12.0, pyflakes: 3.2.0) CPython 3.8.13 on Darwin
|
|
157
157
|
|
|
158
158
|
And now use `flake8` to lint your files
|
|
159
159
|
|
|
@@ -242,7 +242,7 @@ Standalone
|
|
|
242
242
|
|
|
243
243
|
repos:
|
|
244
244
|
- repo: https://github.com/jshwi/docsig
|
|
245
|
-
rev: v0.
|
|
245
|
+
rev: v0.63.0
|
|
246
246
|
hooks:
|
|
247
247
|
- id: docsig
|
|
248
248
|
args:
|
|
@@ -261,7 +261,7 @@ or integrated with ``flake8``
|
|
|
261
261
|
hooks:
|
|
262
262
|
- id: flake8
|
|
263
263
|
additional_dependencies:
|
|
264
|
-
- docsig==0.
|
|
264
|
+
- docsig==0.63.0
|
|
265
265
|
args:
|
|
266
266
|
- "--sig-check-class"
|
|
267
267
|
- "--sig-check-dunders"
|
|
@@ -110,7 +110,7 @@ def parse_args(args: _t.Sequence[str] | None = None) -> _a.Namespace:
|
|
|
110
110
|
:return: Parsed arguments.
|
|
111
111
|
"""
|
|
112
112
|
parser = _ArgumentParser(
|
|
113
|
-
description="Check signature params for proper documentation"
|
|
113
|
+
description="Check signature params for proper documentation"
|
|
114
114
|
)
|
|
115
115
|
parser.add_argument(
|
|
116
116
|
"-V",
|
|
@@ -5,6 +5,7 @@ docsig._core
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations as _
|
|
7
7
|
|
|
8
|
+
import logging as _logging
|
|
8
9
|
import sys as _sys
|
|
9
10
|
from pathlib import Path as _Path
|
|
10
11
|
|
|
@@ -20,7 +21,6 @@ from ._module import Parent as _Parent
|
|
|
20
21
|
from ._report import Failure as _Failure
|
|
21
22
|
from ._report import Failures as _Failures
|
|
22
23
|
from ._utils import print_checks as _print_checks
|
|
23
|
-
from ._utils import vprint as _vprint
|
|
24
24
|
from .messages import TEMPLATE as _TEMPLATE
|
|
25
25
|
from .messages import Messages as _Messages
|
|
26
26
|
|
|
@@ -44,6 +44,21 @@ _DEFAULT_EXCLUDES = """\
|
|
|
44
44
|
"""
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
def setup_logger(verbose: bool) -> None:
|
|
48
|
+
"""Setup docsig logger.
|
|
49
|
+
|
|
50
|
+
Only log if verbose mode is enabled.
|
|
51
|
+
|
|
52
|
+
:param verbose: Whether to enable verbose mode.
|
|
53
|
+
"""
|
|
54
|
+
loglevel = _logging.DEBUG if verbose else _logging.INFO
|
|
55
|
+
logger = _logging.getLogger(__package__)
|
|
56
|
+
logger.setLevel(loglevel)
|
|
57
|
+
if not logger.handlers:
|
|
58
|
+
stream_handler = _logging.StreamHandler(_sys.stdout)
|
|
59
|
+
logger.addHandler(stream_handler)
|
|
60
|
+
|
|
61
|
+
|
|
47
62
|
def _run_check( # pylint: disable=too-many-arguments,too-many-locals
|
|
48
63
|
child: _Parent,
|
|
49
64
|
parent: _Parent,
|
|
@@ -123,10 +138,10 @@ def _parse_ast( # pylint: disable=too-many-arguments
|
|
|
123
138
|
ignore_args: bool,
|
|
124
139
|
ignore_kwargs: bool,
|
|
125
140
|
check_class_constructor,
|
|
126
|
-
verbose: bool,
|
|
127
141
|
root: _Path | None = None,
|
|
128
142
|
string: str | None = None,
|
|
129
143
|
) -> _Parent:
|
|
144
|
+
logger = _logging.getLogger(__package__)
|
|
130
145
|
parent = _Parent()
|
|
131
146
|
try:
|
|
132
147
|
if root is not None:
|
|
@@ -143,29 +158,21 @@ def _parse_ast( # pylint: disable=too-many-arguments
|
|
|
143
158
|
ignore_kwargs,
|
|
144
159
|
check_class_constructor,
|
|
145
160
|
)
|
|
146
|
-
|
|
147
|
-
_FILE_INFO
|
|
148
|
-
path=root or "stdin", msg="Parsing Python code successful"
|
|
149
|
-
),
|
|
150
|
-
verbose,
|
|
161
|
+
logger.debug(
|
|
162
|
+
_FILE_INFO, root or "stdin", "Parsing Python code successful"
|
|
151
163
|
)
|
|
152
164
|
return parent
|
|
153
165
|
except (_ast.AstroidSyntaxError, UnicodeDecodeError) as err:
|
|
154
166
|
msg = str(err).replace("\n", " ")
|
|
155
167
|
if root is not None and root.name.endswith(".py"):
|
|
156
|
-
# pass by silently for files that do not end with .py, may
|
|
157
|
-
# result in a 123 syntax error exit status in the future
|
|
158
168
|
parent = _Parent(error=_Error.SYNTAX)
|
|
159
169
|
|
|
160
|
-
|
|
161
|
-
_FILE_INFO.format(path=root or "stdin", msg=msg),
|
|
162
|
-
verbose,
|
|
163
|
-
)
|
|
170
|
+
logger.debug(_FILE_INFO, root or "stdin", msg)
|
|
164
171
|
|
|
165
172
|
return parent
|
|
166
173
|
|
|
167
174
|
|
|
168
|
-
def _get_failures( # pylint: disable=too-many-
|
|
175
|
+
def _get_failures( # pylint: disable=too-many-arguments
|
|
169
176
|
module: _Parent,
|
|
170
177
|
check_class: bool,
|
|
171
178
|
check_class_constructor: bool,
|
|
@@ -209,8 +216,10 @@ def _get_failures( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
209
216
|
|
|
210
217
|
def _report(
|
|
211
218
|
failures: _Failures, path: str | None = None, no_ansi: bool = False
|
|
212
|
-
) ->
|
|
219
|
+
) -> int:
|
|
220
|
+
retcodes = []
|
|
213
221
|
for failure in failures:
|
|
222
|
+
retcodes.append(failure.retcode)
|
|
214
223
|
module = f"{path}:" if path is not None else ""
|
|
215
224
|
header = f"{module}{failure.lineno} in {failure.name}"
|
|
216
225
|
if not no_ansi and _sys.stdout.isatty():
|
|
@@ -229,6 +238,8 @@ def _report(
|
|
|
229
238
|
if item.hint:
|
|
230
239
|
print(f" hint: {item.hint}")
|
|
231
240
|
|
|
241
|
+
return max(retcodes)
|
|
242
|
+
|
|
232
243
|
|
|
233
244
|
def runner( # pylint: disable=too-many-locals,too-many-arguments
|
|
234
245
|
file: str | _Path,
|
|
@@ -246,7 +257,6 @@ def runner( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
246
257
|
ignore_typechecker: bool = False,
|
|
247
258
|
check_protected_class_methods: bool = False,
|
|
248
259
|
no_ansi: bool = False,
|
|
249
|
-
verbose: bool = False,
|
|
250
260
|
target: _Messages | None = None,
|
|
251
261
|
) -> _Failures:
|
|
252
262
|
"""Per path runner.
|
|
@@ -269,7 +279,6 @@ def runner( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
269
279
|
:param check_protected_class_methods: Check public methods belonging
|
|
270
280
|
to protected classes.
|
|
271
281
|
:param no_ansi: Disable ANSI output.
|
|
272
|
-
:param verbose: increase output verbosity.
|
|
273
282
|
:param target: List of errors to target.
|
|
274
283
|
:return: Exit status for whether test failed or not.
|
|
275
284
|
"""
|
|
@@ -279,7 +288,6 @@ def runner( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
279
288
|
ignore_args,
|
|
280
289
|
ignore_kwargs,
|
|
281
290
|
check_class_constructor,
|
|
282
|
-
verbose,
|
|
283
291
|
root=_Path(file),
|
|
284
292
|
)
|
|
285
293
|
if module or module.error is not None:
|
|
@@ -367,6 +375,7 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
367
375
|
:param excludes: Files or dirs to exclude from checks.
|
|
368
376
|
:return: Exit status for whether test failed or not.
|
|
369
377
|
"""
|
|
378
|
+
setup_logger(verbose)
|
|
370
379
|
if list_checks:
|
|
371
380
|
return int(bool(_print_checks())) # type: ignore
|
|
372
381
|
|
|
@@ -375,13 +384,12 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
375
384
|
patterns.append(exclude)
|
|
376
385
|
|
|
377
386
|
if string is None:
|
|
378
|
-
|
|
387
|
+
retcodes = [0]
|
|
379
388
|
paths = _Paths(
|
|
380
389
|
*tuple(_Path(i) for i in path),
|
|
381
390
|
patterns=patterns,
|
|
382
391
|
excludes=excludes or [],
|
|
383
392
|
include_ignored=include_ignored,
|
|
384
|
-
verbose=verbose,
|
|
385
393
|
)
|
|
386
394
|
for file in paths:
|
|
387
395
|
failures = runner(
|
|
@@ -400,21 +408,18 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
400
408
|
ignore_typechecker,
|
|
401
409
|
check_protected_class_methods,
|
|
402
410
|
no_ansi,
|
|
403
|
-
verbose,
|
|
404
411
|
target,
|
|
405
412
|
)
|
|
406
413
|
if failures:
|
|
407
|
-
_report(failures, str(file), no_ansi)
|
|
408
|
-
retcode = 1
|
|
414
|
+
retcodes.append(_report(failures, str(file), no_ansi))
|
|
409
415
|
|
|
410
|
-
return
|
|
416
|
+
return max(retcodes)
|
|
411
417
|
|
|
412
418
|
module = _parse_ast(
|
|
413
419
|
disable or _Messages(),
|
|
414
420
|
ignore_args,
|
|
415
421
|
ignore_kwargs,
|
|
416
422
|
check_class_constructor,
|
|
417
|
-
verbose,
|
|
418
423
|
string=string,
|
|
419
424
|
)
|
|
420
425
|
if module or module.error is not None:
|
|
@@ -434,7 +439,6 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
434
439
|
target or _Messages(),
|
|
435
440
|
)
|
|
436
441
|
if failures:
|
|
437
|
-
_report(failures, no_ansi=no_ansi)
|
|
438
|
-
return 1
|
|
442
|
+
return _report(failures, no_ansi=no_ansi)
|
|
439
443
|
|
|
440
444
|
return 0
|
|
@@ -5,6 +5,7 @@ docsig._files
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations as _
|
|
7
7
|
|
|
8
|
+
import logging as _logging
|
|
8
9
|
import os as _os
|
|
9
10
|
import re as _re
|
|
10
11
|
import typing as _t
|
|
@@ -14,9 +15,7 @@ from pathspec import PathSpec as _PathSpec
|
|
|
14
15
|
from pathspec.patterns import GitWildMatchPattern as _GitWildMatchPattern
|
|
15
16
|
from wcmatch.pathlib import Path as _WcPath
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
FILE_INFO = "{path}: {msg}"
|
|
18
|
+
FILE_INFO = "%s: %s"
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
class _Gitignore(_PathSpec):
|
|
@@ -68,7 +67,7 @@ def _glob(path: _Path, pattern: str) -> bool:
|
|
|
68
67
|
return _WcPath(str(path)).globmatch(pattern) # type: ignore
|
|
69
68
|
|
|
70
69
|
|
|
71
|
-
class Paths(_t.List[_Path]):
|
|
70
|
+
class Paths(_t.List[_Path]):
|
|
72
71
|
"""Collect a list of valid paths.
|
|
73
72
|
|
|
74
73
|
:param paths: Path(s) to parse ``Module``(s) from.
|
|
@@ -77,23 +76,21 @@ class Paths(_t.List[_Path]): # pylint: disable=too-many-instance-attributes
|
|
|
77
76
|
:param excludes: Files or dirs to exclude from checks.
|
|
78
77
|
:param include_ignored: Check files even if they match a gitignore
|
|
79
78
|
pattern.
|
|
80
|
-
:param verbose: increase output verbosity.
|
|
81
79
|
"""
|
|
82
80
|
|
|
83
|
-
def __init__(
|
|
81
|
+
def __init__(
|
|
84
82
|
self,
|
|
85
83
|
*paths: _Path,
|
|
86
84
|
patterns: list[str],
|
|
87
85
|
excludes: list[str],
|
|
88
86
|
include_ignored: bool = False,
|
|
89
|
-
verbose: bool = False,
|
|
90
87
|
) -> None:
|
|
91
88
|
super().__init__()
|
|
92
89
|
self._patterns = patterns
|
|
93
90
|
self._excludes = excludes
|
|
94
91
|
self._include_ignored = include_ignored
|
|
95
|
-
self._verbose = verbose
|
|
96
92
|
self._gitignore = _Gitignore()
|
|
93
|
+
self._logger = _logging.getLogger(__package__)
|
|
97
94
|
for path in paths:
|
|
98
95
|
self._populate(path)
|
|
99
96
|
|
|
@@ -102,11 +99,8 @@ class Paths(_t.List[_Path]): # pylint: disable=too-many-instance-attributes
|
|
|
102
99
|
any(_re.match(i, str(path)) for i in self._patterns)
|
|
103
100
|
or any(_glob(path, i) for i in self._excludes)
|
|
104
101
|
):
|
|
105
|
-
|
|
106
|
-
FILE_INFO
|
|
107
|
-
path=path, msg="in exclude list, skipping"
|
|
108
|
-
),
|
|
109
|
-
self._verbose,
|
|
102
|
+
self._logger.debug(
|
|
103
|
+
FILE_INFO, path, "in exclude list, skipping"
|
|
110
104
|
)
|
|
111
105
|
self.remove(path)
|
|
112
106
|
|
|
@@ -117,10 +111,7 @@ class Paths(_t.List[_Path]): # pylint: disable=too-many-instance-attributes
|
|
|
117
111
|
raise FileNotFoundError(root)
|
|
118
112
|
|
|
119
113
|
if not self._include_ignored and self._gitignore.match_file(root):
|
|
120
|
-
|
|
121
|
-
FILE_INFO.format(path=root, msg="in gitignore, skipping"),
|
|
122
|
-
self._verbose,
|
|
123
|
-
)
|
|
114
|
+
self._logger.debug(FILE_INFO, root, "in gitignore, skipping")
|
|
124
115
|
return
|
|
125
116
|
|
|
126
117
|
if root.is_file():
|
|
@@ -60,6 +60,9 @@ class Parent(_t.List["Parent"]):
|
|
|
60
60
|
super().__init__()
|
|
61
61
|
self._name = "module"
|
|
62
62
|
self._error = error
|
|
63
|
+
self._ignore_args = ignore_args
|
|
64
|
+
self._ignore_kwargs = ignore_kwargs
|
|
65
|
+
self._check_class_constructor = check_class_constructor
|
|
63
66
|
if node is None:
|
|
64
67
|
if not isinstance(self, Function) and error is not None:
|
|
65
68
|
self.append(Function(path, error=error))
|
|
@@ -67,28 +70,18 @@ class Parent(_t.List["Parent"]):
|
|
|
67
70
|
self._name = node.name
|
|
68
71
|
self._overloads = _Overloads()
|
|
69
72
|
self._imports = imports or _Imports()
|
|
70
|
-
self._parse_ast(
|
|
71
|
-
node,
|
|
72
|
-
directives or _Directives(),
|
|
73
|
-
path,
|
|
74
|
-
ignore_args,
|
|
75
|
-
ignore_kwargs,
|
|
76
|
-
check_class_constructor,
|
|
77
|
-
)
|
|
73
|
+
self._parse_ast(node, directives or _Directives(), path)
|
|
78
74
|
|
|
79
75
|
def _parse_imports(self, names: list[tuple[str, str | None]]) -> None:
|
|
80
76
|
for name in names:
|
|
81
77
|
original, alias = name
|
|
82
78
|
self._imports[original] = alias or original
|
|
83
79
|
|
|
84
|
-
def _parse_ast(
|
|
80
|
+
def _parse_ast(
|
|
85
81
|
self,
|
|
86
82
|
node: _ast.Module | _ast.ClassDef | _ast.FunctionDef | _ast.NodeNG,
|
|
87
83
|
directives: _Directives,
|
|
88
84
|
path: _Path | None = None,
|
|
89
|
-
ignore_args: bool = False,
|
|
90
|
-
ignore_kwargs: bool = False,
|
|
91
|
-
check_class_constructor: bool = False,
|
|
92
85
|
) -> None:
|
|
93
86
|
# need to keep track of `comments` as, even though they are
|
|
94
87
|
# resolved in directives object, they are needed to notify user
|
|
@@ -112,9 +105,9 @@ class Parent(_t.List["Parent"]):
|
|
|
112
105
|
directives,
|
|
113
106
|
disabled,
|
|
114
107
|
path,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
108
|
+
self._ignore_args,
|
|
109
|
+
self._ignore_kwargs,
|
|
110
|
+
self._check_class_constructor,
|
|
118
111
|
self._imports,
|
|
119
112
|
)
|
|
120
113
|
if func.isoverloaded:
|
|
@@ -137,21 +130,14 @@ class Parent(_t.List["Parent"]):
|
|
|
137
130
|
subnode,
|
|
138
131
|
directives,
|
|
139
132
|
path,
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
133
|
+
self._ignore_args,
|
|
134
|
+
self._ignore_kwargs,
|
|
135
|
+
self._check_class_constructor,
|
|
143
136
|
self._imports,
|
|
144
137
|
)
|
|
145
138
|
)
|
|
146
139
|
else:
|
|
147
|
-
self._parse_ast(
|
|
148
|
-
subnode,
|
|
149
|
-
directives,
|
|
150
|
-
path,
|
|
151
|
-
ignore_args,
|
|
152
|
-
ignore_kwargs,
|
|
153
|
-
check_class_constructor,
|
|
154
|
-
)
|
|
140
|
+
self._parse_ast(subnode, directives, path)
|
|
155
141
|
|
|
156
142
|
@property
|
|
157
143
|
def isprotected(self) -> bool:
|
|
@@ -216,13 +202,14 @@ class Function(Parent):
|
|
|
216
202
|
self._parent = node.parent.frame()
|
|
217
203
|
self._decorators = node.decorators
|
|
218
204
|
self._lineno = node.lineno
|
|
205
|
+
if self.ismethod and not self.isstaticmethod:
|
|
206
|
+
if node.args.posonlyargs:
|
|
207
|
+
node.args.posonlyargs.pop(0)
|
|
208
|
+
elif node.args.args:
|
|
209
|
+
node.args.args.pop(0)
|
|
210
|
+
|
|
219
211
|
self._signature = self._signature.from_ast(
|
|
220
|
-
node
|
|
221
|
-
node.returns,
|
|
222
|
-
self.ismethod,
|
|
223
|
-
self.isstaticmethod,
|
|
224
|
-
ignore_args,
|
|
225
|
-
ignore_kwargs,
|
|
212
|
+
node, ignore_args, ignore_kwargs
|
|
226
213
|
)
|
|
227
214
|
if self.isinit and not check_class_constructor:
|
|
228
215
|
# docstring for __init__ is expected on the class
|
|
@@ -232,9 +219,7 @@ class Function(Parent):
|
|
|
232
219
|
relevant_doc_node = node.doc_node
|
|
233
220
|
|
|
234
221
|
if relevant_doc_node is not None:
|
|
235
|
-
self._docstring = self.docstring.from_ast(
|
|
236
|
-
relevant_doc_node, ignore_kwargs
|
|
237
|
-
)
|
|
222
|
+
self._docstring = self.docstring.from_ast(relevant_doc_node)
|
|
238
223
|
|
|
239
224
|
def __len__(self) -> int:
|
|
240
225
|
"""Length of the longest sequence of args."""
|
|
@@ -51,6 +51,7 @@ class Failure(_t.List[Failed]):
|
|
|
51
51
|
ignore_typechecker: bool,
|
|
52
52
|
) -> None:
|
|
53
53
|
super().__init__()
|
|
54
|
+
self._retcode = 0
|
|
54
55
|
self._func = func
|
|
55
56
|
if target:
|
|
56
57
|
self._func.messages.extend(i for i in _E.all if i not in target)
|
|
@@ -79,6 +80,7 @@ class Failure(_t.List[Failed]):
|
|
|
79
80
|
self.sort()
|
|
80
81
|
|
|
81
82
|
def _add(self, value: _Message, hint: bool = False, **kwargs) -> None:
|
|
83
|
+
self._retcode = 1
|
|
82
84
|
failed = Failed(
|
|
83
85
|
self._name,
|
|
84
86
|
value.ref,
|
|
@@ -236,6 +238,7 @@ class Failure(_t.List[Failed]):
|
|
|
236
238
|
# invalid-syntax
|
|
237
239
|
if self._func.error == _Error.SYNTAX:
|
|
238
240
|
self._add(_E[901])
|
|
241
|
+
self._retcode = 123
|
|
239
242
|
|
|
240
243
|
@property
|
|
241
244
|
def name(self) -> str:
|
|
@@ -247,6 +250,11 @@ class Failure(_t.List[Failed]):
|
|
|
247
250
|
"""Function line number."""
|
|
248
251
|
return self._func.lineno
|
|
249
252
|
|
|
253
|
+
@property
|
|
254
|
+
def retcode(self) -> int:
|
|
255
|
+
"""How to exit the program."""
|
|
256
|
+
return self._retcode
|
|
257
|
+
|
|
250
258
|
|
|
251
259
|
class Failures(_t.List[Failure]):
|
|
252
260
|
"""Sequence of failed functions."""
|
|
@@ -15,7 +15,7 @@ import astroid as _ast
|
|
|
15
15
|
import sphinx.ext.napoleon as _s
|
|
16
16
|
|
|
17
17
|
# no function will accidentally have this name
|
|
18
|
-
UNNAMED = -1000
|
|
18
|
+
UNNAMED = "-1000"
|
|
19
19
|
|
|
20
20
|
# an example of valid parameter description
|
|
21
21
|
VALID_DESCRIPTION = " A valid description."
|
|
@@ -103,43 +103,6 @@ class Param(_t.NamedTuple):
|
|
|
103
103
|
return str(self.name).startswith("_")
|
|
104
104
|
|
|
105
105
|
|
|
106
|
-
class _Matches(_t.List[Param]):
|
|
107
|
-
_pattern = _re.compile(":(.*?):")
|
|
108
|
-
|
|
109
|
-
def __init__(
|
|
110
|
-
self, string: str, indent_anomaly: bool, missing_descriptions: bool
|
|
111
|
-
) -> None:
|
|
112
|
-
super().__init__()
|
|
113
|
-
for line in string.splitlines():
|
|
114
|
-
strip_line = line.lstrip()
|
|
115
|
-
match = self._pattern.split(strip_line)[1:]
|
|
116
|
-
if match:
|
|
117
|
-
description = None
|
|
118
|
-
kinds = match[0].split()
|
|
119
|
-
if kinds:
|
|
120
|
-
kind = DocType.from_str(kinds[0])
|
|
121
|
-
|
|
122
|
-
if len(kinds) > 1:
|
|
123
|
-
name = kinds[1]
|
|
124
|
-
else:
|
|
125
|
-
# name could not be parsed
|
|
126
|
-
name = UNNAMED
|
|
127
|
-
|
|
128
|
-
if len(match) > 1:
|
|
129
|
-
second = match[1]
|
|
130
|
-
if second != "" or not missing_descriptions:
|
|
131
|
-
description = second
|
|
132
|
-
|
|
133
|
-
super().append(
|
|
134
|
-
Param(
|
|
135
|
-
kind,
|
|
136
|
-
name,
|
|
137
|
-
description,
|
|
138
|
-
int(indent_anomaly),
|
|
139
|
-
)
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
|
|
143
106
|
class _Params(_t.List[Param]):
|
|
144
107
|
def __init__(
|
|
145
108
|
self, ignore_args: bool = False, ignore_kwargs: bool = False
|
|
@@ -149,15 +112,18 @@ class _Params(_t.List[Param]):
|
|
|
149
112
|
self._ignore_kwargs = ignore_kwargs
|
|
150
113
|
self._duplicates: list[Param] = []
|
|
151
114
|
|
|
152
|
-
# pylint: disable=too-many-boolean-expressions
|
|
153
115
|
def append(self, value: Param) -> None:
|
|
154
|
-
if not value.isprotected and (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
116
|
+
if not value.isprotected and any(
|
|
117
|
+
(
|
|
118
|
+
value.kind == DocType.PARAM,
|
|
119
|
+
(value.kind == DocType.ARG and not self._ignore_args),
|
|
120
|
+
(
|
|
121
|
+
value.kind == DocType.KWARG
|
|
122
|
+
and not (
|
|
123
|
+
self._ignore_kwargs
|
|
124
|
+
or any(i.kind == DocType.KWARG for i in self)
|
|
125
|
+
)
|
|
126
|
+
),
|
|
161
127
|
)
|
|
162
128
|
):
|
|
163
129
|
super().append(value)
|
|
@@ -226,6 +192,7 @@ class _Stub:
|
|
|
226
192
|
class Signature(_Stub):
|
|
227
193
|
"""Represents a function signature.
|
|
228
194
|
|
|
195
|
+
:param rettype: The return type.
|
|
229
196
|
:param returns: Returns declared in signature.
|
|
230
197
|
:param ignore_args: Ignore args prefixed with an asterisk.
|
|
231
198
|
:param ignore_kwargs: Ignore kwargs prefixed with two asterisks.
|
|
@@ -233,51 +200,40 @@ class Signature(_Stub):
|
|
|
233
200
|
|
|
234
201
|
def __init__(
|
|
235
202
|
self,
|
|
236
|
-
|
|
203
|
+
rettype: RetType = RetType.NONE,
|
|
204
|
+
returns: bool = False,
|
|
237
205
|
ignore_args: bool = False,
|
|
238
206
|
ignore_kwargs: bool = False,
|
|
239
207
|
) -> None:
|
|
240
208
|
super().__init__(ignore_args, ignore_kwargs)
|
|
241
|
-
|
|
242
|
-
self.
|
|
243
|
-
self._returns = self._rettype == RetType.SOME
|
|
209
|
+
self._rettype = rettype
|
|
210
|
+
self._returns = returns
|
|
244
211
|
|
|
245
212
|
@classmethod
|
|
246
|
-
def from_ast(
|
|
213
|
+
def from_ast(
|
|
247
214
|
cls,
|
|
248
|
-
|
|
249
|
-
returns: _ast.Module | str,
|
|
250
|
-
ismethod: bool = False,
|
|
251
|
-
isstaticmethod: bool = False,
|
|
215
|
+
node: _ast.Module | _ast.ClassDef | _ast.FunctionDef,
|
|
252
216
|
ignore_args: bool = False,
|
|
253
217
|
ignore_kwargs: bool = False,
|
|
254
218
|
) -> Signature:
|
|
255
219
|
"""Parse signature from ast.
|
|
256
220
|
|
|
257
|
-
:param
|
|
258
|
-
:param returns: Returns declared in signature.
|
|
259
|
-
:param ismethod: Whether this signature belongs to a method.
|
|
260
|
-
:param isstaticmethod: Whether this signature belongs to a
|
|
261
|
-
static method.
|
|
221
|
+
:param node: Abstract syntax tree.
|
|
262
222
|
:param ignore_args: Ignore args prefixed with an asterisk.
|
|
263
223
|
:param ignore_kwargs: Ignore kwargs prefixed with two asterisks.
|
|
264
224
|
:return: Instantiated signature object.
|
|
265
225
|
"""
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
elif arguments.args:
|
|
270
|
-
arguments.args.pop(0)
|
|
271
|
-
|
|
272
|
-
signature = cls(returns, ignore_args, ignore_kwargs)
|
|
226
|
+
rettype = RetType.from_ast(node.returns)
|
|
227
|
+
returns = rettype == RetType.SOME
|
|
228
|
+
signature = cls(rettype, returns, ignore_args, ignore_kwargs)
|
|
273
229
|
for i in [
|
|
274
230
|
a if isinstance(a, Param) else Param(name=a.name)
|
|
275
231
|
for a in [
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
Param(DocType.ARG, name=
|
|
279
|
-
*
|
|
280
|
-
Param(DocType.KWARG, name=
|
|
232
|
+
*node.args.posonlyargs,
|
|
233
|
+
*node.args.args,
|
|
234
|
+
Param(DocType.ARG, name=node.args.vararg),
|
|
235
|
+
*node.args.kwonlyargs,
|
|
236
|
+
Param(DocType.KWARG, name=node.args.kwarg),
|
|
281
237
|
]
|
|
282
238
|
if a is not None and a.name
|
|
283
239
|
]:
|
|
@@ -307,13 +263,11 @@ class Docstring(_Stub):
|
|
|
307
263
|
"""Represents a function docstring.
|
|
308
264
|
|
|
309
265
|
:param string: The raw docstring.
|
|
310
|
-
:param
|
|
311
|
-
:param ignore_kwargs: Ignore kwargs prefixed with two asterisks.
|
|
266
|
+
:param returns: Whether this docstring has a return.
|
|
312
267
|
"""
|
|
313
268
|
|
|
314
269
|
@staticmethod
|
|
315
270
|
def _indent_anomaly(string: str) -> bool:
|
|
316
|
-
leading_spaces = []
|
|
317
271
|
for line in string.splitlines():
|
|
318
272
|
# only check params
|
|
319
273
|
# description or anything else is out of scope
|
|
@@ -322,28 +276,9 @@ class Docstring(_Stub):
|
|
|
322
276
|
if match is not None:
|
|
323
277
|
spaces = len(match.group())
|
|
324
278
|
if spaces > 0:
|
|
325
|
-
|
|
279
|
+
return spaces % 2 != 0
|
|
326
280
|
|
|
327
|
-
|
|
328
|
-
return bool(any(i % 2 != 0 for i in leading_spaces))
|
|
329
|
-
|
|
330
|
-
@staticmethod
|
|
331
|
-
def _missing_descriptions(string: str) -> bool:
|
|
332
|
-
# find out if parameter is missing a description
|
|
333
|
-
new = ""
|
|
334
|
-
for line in string.strip().splitlines()[2:]:
|
|
335
|
-
line = line.lstrip()
|
|
336
|
-
if not line.startswith(":"):
|
|
337
|
-
# it is not a parameter, it is a next line description
|
|
338
|
-
# append the next entry to the same line
|
|
339
|
-
new = f"{new[:-1]} "
|
|
340
|
-
new += f"{line}\n"
|
|
341
|
-
if not new:
|
|
342
|
-
return True
|
|
343
|
-
|
|
344
|
-
# if it ends with a colon, it's a parameter without a
|
|
345
|
-
# description
|
|
346
|
-
return any(i.endswith(":") for i in new.splitlines())
|
|
281
|
+
return False
|
|
347
282
|
|
|
348
283
|
@staticmethod
|
|
349
284
|
def _normalize_docstring(string: str) -> str:
|
|
@@ -362,35 +297,35 @@ class Docstring(_Stub):
|
|
|
362
297
|
)
|
|
363
298
|
|
|
364
299
|
def __init__(
|
|
365
|
-
self,
|
|
366
|
-
string: str | None = None,
|
|
367
|
-
ignore_args: bool = False,
|
|
368
|
-
ignore_kwargs: bool = False,
|
|
300
|
+
self, string: str | None = None, returns: bool = False
|
|
369
301
|
) -> None:
|
|
370
|
-
super().__init__(
|
|
302
|
+
super().__init__()
|
|
371
303
|
self._string = string
|
|
372
|
-
self._returns =
|
|
373
|
-
_re.search(":returns?:", self._string)
|
|
374
|
-
)
|
|
304
|
+
self._returns = returns
|
|
375
305
|
|
|
376
306
|
@classmethod
|
|
377
|
-
def from_ast(
|
|
378
|
-
cls, node: _ast.Const, ignore_kwargs: bool = False
|
|
379
|
-
) -> Docstring:
|
|
307
|
+
def from_ast(cls, node: _ast.Const) -> Docstring:
|
|
380
308
|
"""Parse function docstring from ast.
|
|
381
309
|
|
|
382
310
|
:param node: Docstring ast node.
|
|
383
|
-
:param ignore_kwargs: Ignore kwargs prefixed with two asterisks.
|
|
384
311
|
:return: Instantiated docstring object.
|
|
385
312
|
"""
|
|
313
|
+
indent_anomaly = cls._indent_anomaly(node.value)
|
|
386
314
|
string = cls._normalize_docstring(node.value)
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
315
|
+
returns = bool(_re.search(r":returns?:", string))
|
|
316
|
+
docstring = cls(string, returns)
|
|
317
|
+
for match in _re.findall(r":(.*?):((?:.|\n)*?)(?=\n:|$)", string):
|
|
318
|
+
if match:
|
|
319
|
+
kinds = match[0].split()
|
|
320
|
+
if kinds:
|
|
321
|
+
docstring.args.append(
|
|
322
|
+
Param(
|
|
323
|
+
DocType.from_str(kinds[0]),
|
|
324
|
+
UNNAMED if len(kinds) == 1 else kinds[1],
|
|
325
|
+
match[1] or None,
|
|
326
|
+
int(indent_anomaly),
|
|
327
|
+
)
|
|
328
|
+
)
|
|
394
329
|
|
|
395
330
|
return docstring
|
|
396
331
|
|
|
@@ -26,16 +26,6 @@ def almost_equal(str1: str, str2: str, mini: float, maxi: float) -> bool:
|
|
|
26
26
|
return mini < _SequenceMatcher(a=str1, b=str2).ratio() < maxi
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
def vprint(msg: str, verbose: bool = False) -> None:
|
|
30
|
-
"""Print messages only if verbose is true.
|
|
31
|
-
|
|
32
|
-
:param msg: Message to print.
|
|
33
|
-
:param verbose: Whether verbose mode is enabled.
|
|
34
|
-
"""
|
|
35
|
-
if verbose:
|
|
36
|
-
print(msg)
|
|
37
|
-
|
|
38
|
-
|
|
39
29
|
def pretty_print_error(
|
|
40
30
|
exception_type: _t.Type[BaseException], msg: str, no_ansi: bool
|
|
41
31
|
) -> None:
|
|
@@ -6,7 +6,7 @@ from argparse import Namespace
|
|
|
6
6
|
|
|
7
7
|
from ._config import get_config as _get_config
|
|
8
8
|
from ._config import merge_configs as _merge_configs
|
|
9
|
-
from ._core import runner
|
|
9
|
+
from ._core import runner, setup_logger
|
|
10
10
|
from ._version import __version__
|
|
11
11
|
from .messages import FLAKE8, E
|
|
12
12
|
|
|
@@ -141,10 +141,11 @@ class Docsig:
|
|
|
141
141
|
ref=E[5].ref,
|
|
142
142
|
description=E[5].description,
|
|
143
143
|
symbolic=E[5].symbolic,
|
|
144
|
-
)
|
|
144
|
+
)
|
|
145
145
|
)
|
|
146
146
|
yield 0, 0, line, self.__class__
|
|
147
147
|
else:
|
|
148
|
+
setup_logger(self.a.verbose)
|
|
148
149
|
results = runner(
|
|
149
150
|
self.filename,
|
|
150
151
|
check_class=self.a.check_class,
|
|
@@ -161,7 +162,6 @@ class Docsig:
|
|
|
161
162
|
ignore_args=self.a.ignore_args,
|
|
162
163
|
ignore_kwargs=self.a.ignore_kwargs,
|
|
163
164
|
ignore_typechecker=self.a.ignore_typechecker,
|
|
164
|
-
verbose=self.a.verbose,
|
|
165
165
|
)
|
|
166
166
|
for result in results:
|
|
167
167
|
for info in result:
|
|
@@ -11,7 +11,7 @@ line-length = 79
|
|
|
11
11
|
allow_dirty = true
|
|
12
12
|
commit = true
|
|
13
13
|
commit_args = "-sS"
|
|
14
|
-
current_version = "0.
|
|
14
|
+
current_version = "0.63.0"
|
|
15
15
|
message = "bump: version {current_version} → {new_version}"
|
|
16
16
|
sign_tags = true
|
|
17
17
|
tag = true
|
|
@@ -131,7 +131,7 @@ maintainers = [
|
|
|
131
131
|
name = "docsig"
|
|
132
132
|
readme = "README.rst"
|
|
133
133
|
repository = "https://github.com/jshwi/docsig"
|
|
134
|
-
version = "0.
|
|
134
|
+
version = "0.63.0"
|
|
135
135
|
|
|
136
136
|
[tool.poetry.dependencies]
|
|
137
137
|
Sphinx = "^7.0.0"
|
|
@@ -139,7 +139,7 @@ astroid = "^3.0.1"
|
|
|
139
139
|
pathspec = "^0.12.1"
|
|
140
140
|
python = "^3.8.1"
|
|
141
141
|
tomli = "^2.0.1"
|
|
142
|
-
wcmatch = ">=8.5.2,<
|
|
142
|
+
wcmatch = ">=8.5.2,<11.0.0"
|
|
143
143
|
|
|
144
144
|
[tool.poetry.group.dev.dependencies]
|
|
145
145
|
black = "^24.4.2"
|
|
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
|