docsig 0.62.1__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.1 → docsig-0.63.0}/PKG-INFO +4 -4
- {docsig-0.62.1 → docsig-0.63.0}/README.rst +3 -3
- {docsig-0.62.1 → docsig-0.63.0}/docsig/_config.py +1 -1
- {docsig-0.62.1 → docsig-0.63.0}/docsig/_core.py +31 -27
- {docsig-0.62.1 → docsig-0.63.0}/docsig/_files.py +8 -17
- {docsig-0.62.1 → docsig-0.63.0}/docsig/_module.py +20 -35
- {docsig-0.62.1 → docsig-0.63.0}/docsig/_report.py +8 -0
- {docsig-0.62.1 → docsig-0.63.0}/docsig/_stub.py +37 -54
- {docsig-0.62.1 → docsig-0.63.0}/docsig/_utils.py +0 -10
- {docsig-0.62.1 → docsig-0.63.0}/docsig/_version.py +1 -1
- {docsig-0.62.1 → docsig-0.63.0}/docsig/plugin.py +3 -3
- {docsig-0.62.1 → docsig-0.63.0}/pyproject.toml +2 -2
- {docsig-0.62.1 → docsig-0.63.0}/LICENSE +0 -0
- {docsig-0.62.1 → docsig-0.63.0}/docsig/__init__.py +0 -0
- {docsig-0.62.1 → docsig-0.63.0}/docsig/__main__.py +0 -0
- {docsig-0.62.1 → docsig-0.63.0}/docsig/_decorators.py +0 -0
- {docsig-0.62.1 → docsig-0.63.0}/docsig/_directives.py +0 -0
- {docsig-0.62.1 → docsig-0.63.0}/docsig/_hooks.py +0 -0
- {docsig-0.62.1 → docsig-0.63.0}/docsig/_main.py +0 -0
- {docsig-0.62.1 → docsig-0.63.0}/docsig/messages.py +0 -0
- {docsig-0.62.1 → 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
|
|
@@ -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."""
|
|
@@ -112,15 +112,18 @@ class _Params(_t.List[Param]):
|
|
|
112
112
|
self._ignore_kwargs = ignore_kwargs
|
|
113
113
|
self._duplicates: list[Param] = []
|
|
114
114
|
|
|
115
|
-
# pylint: disable=too-many-boolean-expressions
|
|
116
115
|
def append(self, value: Param) -> None:
|
|
117
|
-
if not value.isprotected and (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
+
),
|
|
124
127
|
)
|
|
125
128
|
):
|
|
126
129
|
super().append(value)
|
|
@@ -189,6 +192,7 @@ class _Stub:
|
|
|
189
192
|
class Signature(_Stub):
|
|
190
193
|
"""Represents a function signature.
|
|
191
194
|
|
|
195
|
+
:param rettype: The return type.
|
|
192
196
|
:param returns: Returns declared in signature.
|
|
193
197
|
:param ignore_args: Ignore args prefixed with an asterisk.
|
|
194
198
|
:param ignore_kwargs: Ignore kwargs prefixed with two asterisks.
|
|
@@ -196,51 +200,40 @@ class Signature(_Stub):
|
|
|
196
200
|
|
|
197
201
|
def __init__(
|
|
198
202
|
self,
|
|
199
|
-
|
|
203
|
+
rettype: RetType = RetType.NONE,
|
|
204
|
+
returns: bool = False,
|
|
200
205
|
ignore_args: bool = False,
|
|
201
206
|
ignore_kwargs: bool = False,
|
|
202
207
|
) -> None:
|
|
203
208
|
super().__init__(ignore_args, ignore_kwargs)
|
|
204
|
-
|
|
205
|
-
self.
|
|
206
|
-
self._returns = self._rettype == RetType.SOME
|
|
209
|
+
self._rettype = rettype
|
|
210
|
+
self._returns = returns
|
|
207
211
|
|
|
208
212
|
@classmethod
|
|
209
|
-
def from_ast(
|
|
213
|
+
def from_ast(
|
|
210
214
|
cls,
|
|
211
|
-
|
|
212
|
-
returns: _ast.Module | str,
|
|
213
|
-
ismethod: bool = False,
|
|
214
|
-
isstaticmethod: bool = False,
|
|
215
|
+
node: _ast.Module | _ast.ClassDef | _ast.FunctionDef,
|
|
215
216
|
ignore_args: bool = False,
|
|
216
217
|
ignore_kwargs: bool = False,
|
|
217
218
|
) -> Signature:
|
|
218
219
|
"""Parse signature from ast.
|
|
219
220
|
|
|
220
|
-
:param
|
|
221
|
-
:param returns: Returns declared in signature.
|
|
222
|
-
:param ismethod: Whether this signature belongs to a method.
|
|
223
|
-
:param isstaticmethod: Whether this signature belongs to a
|
|
224
|
-
static method.
|
|
221
|
+
:param node: Abstract syntax tree.
|
|
225
222
|
:param ignore_args: Ignore args prefixed with an asterisk.
|
|
226
223
|
:param ignore_kwargs: Ignore kwargs prefixed with two asterisks.
|
|
227
224
|
:return: Instantiated signature object.
|
|
228
225
|
"""
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
elif arguments.args:
|
|
233
|
-
arguments.args.pop(0)
|
|
234
|
-
|
|
235
|
-
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)
|
|
236
229
|
for i in [
|
|
237
230
|
a if isinstance(a, Param) else Param(name=a.name)
|
|
238
231
|
for a in [
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
Param(DocType.ARG, name=
|
|
242
|
-
*
|
|
243
|
-
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),
|
|
244
237
|
]
|
|
245
238
|
if a is not None and a.name
|
|
246
239
|
]:
|
|
@@ -270,13 +263,11 @@ class Docstring(_Stub):
|
|
|
270
263
|
"""Represents a function docstring.
|
|
271
264
|
|
|
272
265
|
:param string: The raw docstring.
|
|
273
|
-
:param
|
|
274
|
-
:param ignore_kwargs: Ignore kwargs prefixed with two asterisks.
|
|
266
|
+
:param returns: Whether this docstring has a return.
|
|
275
267
|
"""
|
|
276
268
|
|
|
277
269
|
@staticmethod
|
|
278
270
|
def _indent_anomaly(string: str) -> bool:
|
|
279
|
-
leading_spaces = []
|
|
280
271
|
for line in string.splitlines():
|
|
281
272
|
# only check params
|
|
282
273
|
# description or anything else is out of scope
|
|
@@ -285,10 +276,9 @@ class Docstring(_Stub):
|
|
|
285
276
|
if match is not None:
|
|
286
277
|
spaces = len(match.group())
|
|
287
278
|
if spaces > 0:
|
|
288
|
-
|
|
279
|
+
return spaces % 2 != 0
|
|
289
280
|
|
|
290
|
-
|
|
291
|
-
return bool(any(i % 2 != 0 for i in leading_spaces))
|
|
281
|
+
return False
|
|
292
282
|
|
|
293
283
|
@staticmethod
|
|
294
284
|
def _normalize_docstring(string: str) -> str:
|
|
@@ -307,30 +297,23 @@ class Docstring(_Stub):
|
|
|
307
297
|
)
|
|
308
298
|
|
|
309
299
|
def __init__(
|
|
310
|
-
self,
|
|
311
|
-
string: str | None = None,
|
|
312
|
-
ignore_args: bool = False,
|
|
313
|
-
ignore_kwargs: bool = False,
|
|
300
|
+
self, string: str | None = None, returns: bool = False
|
|
314
301
|
) -> None:
|
|
315
|
-
super().__init__(
|
|
302
|
+
super().__init__()
|
|
316
303
|
self._string = string
|
|
317
|
-
self._returns =
|
|
318
|
-
_re.search(":returns?:", self._string)
|
|
319
|
-
)
|
|
304
|
+
self._returns = returns
|
|
320
305
|
|
|
321
306
|
@classmethod
|
|
322
|
-
def from_ast(
|
|
323
|
-
cls, node: _ast.Const, ignore_kwargs: bool = False
|
|
324
|
-
) -> Docstring:
|
|
307
|
+
def from_ast(cls, node: _ast.Const) -> Docstring:
|
|
325
308
|
"""Parse function docstring from ast.
|
|
326
309
|
|
|
327
310
|
:param node: Docstring ast node.
|
|
328
|
-
:param ignore_kwargs: Ignore kwargs prefixed with two asterisks.
|
|
329
311
|
:return: Instantiated docstring object.
|
|
330
312
|
"""
|
|
331
|
-
string = cls._normalize_docstring(node.value)
|
|
332
|
-
docstring = cls(string, ignore_kwargs)
|
|
333
313
|
indent_anomaly = cls._indent_anomaly(node.value)
|
|
314
|
+
string = cls._normalize_docstring(node.value)
|
|
315
|
+
returns = bool(_re.search(r":returns?:", string))
|
|
316
|
+
docstring = cls(string, returns)
|
|
334
317
|
for match in _re.findall(r":(.*?):((?:.|\n)*?)(?=\n:|$)", string):
|
|
335
318
|
if match:
|
|
336
319
|
kinds = match[0].split()
|
|
@@ -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"
|
|
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
|