docsig 0.62.1__tar.gz → 0.64.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.64.0}/PKG-INFO +4 -4
- {docsig-0.62.1 → docsig-0.64.0}/README.rst +3 -3
- {docsig-0.62.1 → docsig-0.64.0}/docsig/_config.py +1 -1
- {docsig-0.62.1 → docsig-0.64.0}/docsig/_core.py +98 -86
- {docsig-0.62.1 → docsig-0.64.0}/docsig/_files.py +11 -20
- {docsig-0.62.1 → docsig-0.64.0}/docsig/_module.py +24 -41
- {docsig-0.62.1 → docsig-0.64.0}/docsig/_report.py +10 -0
- {docsig-0.62.1 → docsig-0.64.0}/docsig/_stub.py +37 -54
- {docsig-0.62.1 → docsig-0.64.0}/docsig/_utils.py +0 -10
- {docsig-0.62.1 → docsig-0.64.0}/docsig/_version.py +1 -1
- {docsig-0.62.1 → docsig-0.64.0}/docsig/messages.py +6 -0
- {docsig-0.62.1 → docsig-0.64.0}/docsig/plugin.py +3 -3
- {docsig-0.62.1 → docsig-0.64.0}/pyproject.toml +2 -2
- {docsig-0.62.1 → docsig-0.64.0}/LICENSE +0 -0
- {docsig-0.62.1 → docsig-0.64.0}/docsig/__init__.py +0 -0
- {docsig-0.62.1 → docsig-0.64.0}/docsig/__main__.py +0 -0
- {docsig-0.62.1 → docsig-0.64.0}/docsig/_decorators.py +0 -0
- {docsig-0.62.1 → docsig-0.64.0}/docsig/_directives.py +0 -0
- {docsig-0.62.1 → docsig-0.64.0}/docsig/_hooks.py +0 -0
- {docsig-0.62.1 → docsig-0.64.0}/docsig/_main.py +0 -0
- {docsig-0.62.1 → docsig-0.64.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.64.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.64.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.64.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.64.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.64.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.64.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.64.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,
|
|
@@ -118,23 +133,44 @@ def _run_check( # pylint: disable=too-many-arguments,too-many-locals
|
|
|
118
133
|
)
|
|
119
134
|
|
|
120
135
|
|
|
121
|
-
def
|
|
136
|
+
def _from_file(
|
|
137
|
+
root: _Path,
|
|
122
138
|
messages: _Messages,
|
|
123
139
|
ignore_args: bool,
|
|
124
140
|
ignore_kwargs: bool,
|
|
125
141
|
check_class_constructor,
|
|
126
|
-
verbose: bool,
|
|
127
|
-
root: _Path | None = None,
|
|
128
|
-
string: str | None = None,
|
|
129
142
|
) -> _Parent:
|
|
130
|
-
parent = _Parent()
|
|
131
143
|
try:
|
|
132
|
-
|
|
133
|
-
|
|
144
|
+
string = root.read_text(encoding="utf-8")
|
|
145
|
+
parent = _from_str(
|
|
146
|
+
messages=messages,
|
|
147
|
+
string=string,
|
|
148
|
+
root=root,
|
|
149
|
+
ignore_args=ignore_args,
|
|
150
|
+
ignore_kwargs=ignore_kwargs,
|
|
151
|
+
check_class_constructor=check_class_constructor,
|
|
152
|
+
)
|
|
153
|
+
except UnicodeDecodeError as err:
|
|
154
|
+
logger = _logging.getLogger(__package__)
|
|
155
|
+
logger.debug(_FILE_INFO, root, str(err).replace("\n", " "))
|
|
156
|
+
parent = _Parent(error=_Error.UNICODE)
|
|
134
157
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
158
|
+
if parent.error is not None and not root.name.endswith(".py"):
|
|
159
|
+
parent = _Parent()
|
|
160
|
+
|
|
161
|
+
return parent
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _from_str( # pylint: disable=too-many-arguments
|
|
165
|
+
string: str,
|
|
166
|
+
messages: _Messages,
|
|
167
|
+
ignore_args: bool,
|
|
168
|
+
ignore_kwargs: bool,
|
|
169
|
+
check_class_constructor,
|
|
170
|
+
root: _Path | None = None,
|
|
171
|
+
) -> _Parent:
|
|
172
|
+
logger = _logging.getLogger(__package__)
|
|
173
|
+
try:
|
|
138
174
|
parent = _Parent(
|
|
139
175
|
_ast.parse(string),
|
|
140
176
|
_Directives.from_text(string, messages),
|
|
@@ -143,29 +179,17 @@ def _parse_ast( # pylint: disable=too-many-arguments
|
|
|
143
179
|
ignore_kwargs,
|
|
144
180
|
check_class_constructor,
|
|
145
181
|
)
|
|
146
|
-
|
|
147
|
-
_FILE_INFO
|
|
148
|
-
path=root or "stdin", msg="Parsing Python code successful"
|
|
149
|
-
),
|
|
150
|
-
verbose,
|
|
151
|
-
)
|
|
152
|
-
return parent
|
|
153
|
-
except (_ast.AstroidSyntaxError, UnicodeDecodeError) as err:
|
|
154
|
-
msg = str(err).replace("\n", " ")
|
|
155
|
-
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
|
-
parent = _Parent(error=_Error.SYNTAX)
|
|
159
|
-
|
|
160
|
-
_vprint(
|
|
161
|
-
_FILE_INFO.format(path=root or "stdin", msg=msg),
|
|
162
|
-
verbose,
|
|
182
|
+
logger.debug(
|
|
183
|
+
_FILE_INFO, root or "stdin", "Parsing Python code successful"
|
|
163
184
|
)
|
|
185
|
+
except _ast.AstroidSyntaxError as err:
|
|
186
|
+
logger.debug(_FILE_INFO, root or "stdin", str(err).replace("\n", " "))
|
|
187
|
+
parent = _Parent(error=_Error.SYNTAX)
|
|
164
188
|
|
|
165
189
|
return parent
|
|
166
190
|
|
|
167
191
|
|
|
168
|
-
def _get_failures( # pylint: disable=too-many-
|
|
192
|
+
def _get_failures( # pylint: disable=too-many-arguments
|
|
169
193
|
module: _Parent,
|
|
170
194
|
check_class: bool,
|
|
171
195
|
check_class_constructor: bool,
|
|
@@ -209,8 +233,10 @@ def _get_failures( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
209
233
|
|
|
210
234
|
def _report(
|
|
211
235
|
failures: _Failures, path: str | None = None, no_ansi: bool = False
|
|
212
|
-
) ->
|
|
236
|
+
) -> int:
|
|
237
|
+
retcodes = [0]
|
|
213
238
|
for failure in failures:
|
|
239
|
+
retcodes.append(failure.retcode)
|
|
214
240
|
module = f"{path}:" if path is not None else ""
|
|
215
241
|
header = f"{module}{failure.lineno} in {failure.name}"
|
|
216
242
|
if not no_ansi and _sys.stdout.isatty():
|
|
@@ -229,6 +255,8 @@ def _report(
|
|
|
229
255
|
if item.hint:
|
|
230
256
|
print(f" hint: {item.hint}")
|
|
231
257
|
|
|
258
|
+
return max(retcodes)
|
|
259
|
+
|
|
232
260
|
|
|
233
261
|
def runner( # pylint: disable=too-many-locals,too-many-arguments
|
|
234
262
|
file: str | _Path,
|
|
@@ -246,7 +274,6 @@ def runner( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
246
274
|
ignore_typechecker: bool = False,
|
|
247
275
|
check_protected_class_methods: bool = False,
|
|
248
276
|
no_ansi: bool = False,
|
|
249
|
-
verbose: bool = False,
|
|
250
277
|
target: _Messages | None = None,
|
|
251
278
|
) -> _Failures:
|
|
252
279
|
"""Per path runner.
|
|
@@ -269,37 +296,31 @@ def runner( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
269
296
|
:param check_protected_class_methods: Check public methods belonging
|
|
270
297
|
to protected classes.
|
|
271
298
|
:param no_ansi: Disable ANSI output.
|
|
272
|
-
:param verbose: increase output verbosity.
|
|
273
299
|
:param target: List of errors to target.
|
|
274
300
|
:return: Exit status for whether test failed or not.
|
|
275
301
|
"""
|
|
276
|
-
|
|
277
|
-
|
|
302
|
+
module = _from_file(
|
|
303
|
+
_Path(file),
|
|
278
304
|
disable or _Messages(),
|
|
279
305
|
ignore_args,
|
|
280
306
|
ignore_kwargs,
|
|
281
307
|
check_class_constructor,
|
|
282
|
-
verbose,
|
|
283
|
-
root=_Path(file),
|
|
284
308
|
)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
)
|
|
301
|
-
|
|
302
|
-
return failures
|
|
309
|
+
return _get_failures(
|
|
310
|
+
module,
|
|
311
|
+
check_class,
|
|
312
|
+
check_class_constructor,
|
|
313
|
+
check_dunders,
|
|
314
|
+
check_nested,
|
|
315
|
+
check_overridden,
|
|
316
|
+
check_protected,
|
|
317
|
+
check_property_returns,
|
|
318
|
+
ignore_no_params,
|
|
319
|
+
ignore_typechecker,
|
|
320
|
+
check_protected_class_methods,
|
|
321
|
+
no_ansi,
|
|
322
|
+
target or _Messages(),
|
|
323
|
+
)
|
|
303
324
|
|
|
304
325
|
|
|
305
326
|
@_decorators.parse_msgs
|
|
@@ -367,6 +388,7 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
367
388
|
:param excludes: Files or dirs to exclude from checks.
|
|
368
389
|
:return: Exit status for whether test failed or not.
|
|
369
390
|
"""
|
|
391
|
+
setup_logger(verbose)
|
|
370
392
|
if list_checks:
|
|
371
393
|
return int(bool(_print_checks())) # type: ignore
|
|
372
394
|
|
|
@@ -375,13 +397,12 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
375
397
|
patterns.append(exclude)
|
|
376
398
|
|
|
377
399
|
if string is None:
|
|
378
|
-
|
|
400
|
+
retcodes = [0]
|
|
379
401
|
paths = _Paths(
|
|
380
|
-
*
|
|
402
|
+
*path,
|
|
381
403
|
patterns=patterns,
|
|
382
404
|
excludes=excludes or [],
|
|
383
405
|
include_ignored=include_ignored,
|
|
384
|
-
verbose=verbose,
|
|
385
406
|
)
|
|
386
407
|
for file in paths:
|
|
387
408
|
failures = runner(
|
|
@@ -400,41 +421,32 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
400
421
|
ignore_typechecker,
|
|
401
422
|
check_protected_class_methods,
|
|
402
423
|
no_ansi,
|
|
403
|
-
verbose,
|
|
404
424
|
target,
|
|
405
425
|
)
|
|
406
|
-
|
|
407
|
-
_report(failures, str(file), no_ansi)
|
|
408
|
-
retcode = 1
|
|
426
|
+
retcodes.append(_report(failures, str(file), no_ansi))
|
|
409
427
|
|
|
410
|
-
return
|
|
428
|
+
return max(retcodes)
|
|
411
429
|
|
|
412
|
-
module =
|
|
430
|
+
module = _from_str(
|
|
431
|
+
string,
|
|
413
432
|
disable or _Messages(),
|
|
414
433
|
ignore_args,
|
|
415
434
|
ignore_kwargs,
|
|
416
435
|
check_class_constructor,
|
|
417
|
-
verbose,
|
|
418
|
-
string=string,
|
|
419
436
|
)
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
if failures:
|
|
437
|
-
_report(failures, no_ansi=no_ansi)
|
|
438
|
-
return 1
|
|
439
|
-
|
|
440
|
-
return 0
|
|
437
|
+
failures = _get_failures(
|
|
438
|
+
module,
|
|
439
|
+
check_class,
|
|
440
|
+
check_class_constructor,
|
|
441
|
+
check_dunders,
|
|
442
|
+
check_nested,
|
|
443
|
+
check_overridden,
|
|
444
|
+
check_protected,
|
|
445
|
+
check_property_returns,
|
|
446
|
+
ignore_no_params,
|
|
447
|
+
ignore_typechecker,
|
|
448
|
+
check_protected_class_methods,
|
|
449
|
+
no_ansi,
|
|
450
|
+
target or _Messages(),
|
|
451
|
+
)
|
|
452
|
+
return _report(failures, no_ansi=no_ansi)
|
|
@@ -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,36 +76,31 @@ 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
|
-
*paths: _Path,
|
|
83
|
+
*paths: _Path | str,
|
|
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
|
-
self._excludes = excludes
|
|
90
|
+
self._excludes = excludes or []
|
|
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
|
-
self._populate(path)
|
|
95
|
+
self._populate(_Path(path))
|
|
99
96
|
|
|
100
97
|
for path in list(self):
|
|
101
98
|
if str(path) != "." and (
|
|
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():
|
|
@@ -24,6 +24,7 @@ class Error(_Enum):
|
|
|
24
24
|
"""Represents an unrecoverable error."""
|
|
25
25
|
|
|
26
26
|
SYNTAX = 1
|
|
27
|
+
UNICODE = 2
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
class _Imports(_t.Dict[str, str]):
|
|
@@ -60,6 +61,9 @@ class Parent(_t.List["Parent"]):
|
|
|
60
61
|
super().__init__()
|
|
61
62
|
self._name = "module"
|
|
62
63
|
self._error = error
|
|
64
|
+
self._ignore_args = ignore_args
|
|
65
|
+
self._ignore_kwargs = ignore_kwargs
|
|
66
|
+
self._check_class_constructor = check_class_constructor
|
|
63
67
|
if node is None:
|
|
64
68
|
if not isinstance(self, Function) and error is not None:
|
|
65
69
|
self.append(Function(path, error=error))
|
|
@@ -67,28 +71,13 @@ class Parent(_t.List["Parent"]):
|
|
|
67
71
|
self._name = node.name
|
|
68
72
|
self._overloads = _Overloads()
|
|
69
73
|
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
|
-
)
|
|
78
|
-
|
|
79
|
-
def _parse_imports(self, names: list[tuple[str, str | None]]) -> None:
|
|
80
|
-
for name in names:
|
|
81
|
-
original, alias = name
|
|
82
|
-
self._imports[original] = alias or original
|
|
74
|
+
self._parse_ast(node, directives or _Directives(), path)
|
|
83
75
|
|
|
84
|
-
def _parse_ast(
|
|
76
|
+
def _parse_ast(
|
|
85
77
|
self,
|
|
86
78
|
node: _ast.Module | _ast.ClassDef | _ast.FunctionDef | _ast.NodeNG,
|
|
87
79
|
directives: _Directives,
|
|
88
80
|
path: _Path | None = None,
|
|
89
|
-
ignore_args: bool = False,
|
|
90
|
-
ignore_kwargs: bool = False,
|
|
91
|
-
check_class_constructor: bool = False,
|
|
92
81
|
) -> None:
|
|
93
82
|
# need to keep track of `comments` as, even though they are
|
|
94
83
|
# resolved in directives object, they are needed to notify user
|
|
@@ -104,7 +93,9 @@ class Parent(_t.List["Parent"]):
|
|
|
104
93
|
comments.extend(parent_comments)
|
|
105
94
|
disabled.extend(parent_disabled)
|
|
106
95
|
if isinstance(subnode, (_ast.Import, _ast.ImportFrom)):
|
|
107
|
-
|
|
96
|
+
for name in subnode.names:
|
|
97
|
+
original, alias = name
|
|
98
|
+
self._imports[original] = alias or original
|
|
108
99
|
elif isinstance(subnode, _ast.FunctionDef):
|
|
109
100
|
func = Function(
|
|
110
101
|
subnode,
|
|
@@ -112,9 +103,9 @@ class Parent(_t.List["Parent"]):
|
|
|
112
103
|
directives,
|
|
113
104
|
disabled,
|
|
114
105
|
path,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
106
|
+
self._ignore_args,
|
|
107
|
+
self._ignore_kwargs,
|
|
108
|
+
self._check_class_constructor,
|
|
118
109
|
self._imports,
|
|
119
110
|
)
|
|
120
111
|
if func.isoverloaded:
|
|
@@ -137,21 +128,14 @@ class Parent(_t.List["Parent"]):
|
|
|
137
128
|
subnode,
|
|
138
129
|
directives,
|
|
139
130
|
path,
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
131
|
+
self._ignore_args,
|
|
132
|
+
self._ignore_kwargs,
|
|
133
|
+
self._check_class_constructor,
|
|
143
134
|
self._imports,
|
|
144
135
|
)
|
|
145
136
|
)
|
|
146
137
|
else:
|
|
147
|
-
self._parse_ast(
|
|
148
|
-
subnode,
|
|
149
|
-
directives,
|
|
150
|
-
path,
|
|
151
|
-
ignore_args,
|
|
152
|
-
ignore_kwargs,
|
|
153
|
-
check_class_constructor,
|
|
154
|
-
)
|
|
138
|
+
self._parse_ast(subnode, directives, path)
|
|
155
139
|
|
|
156
140
|
@property
|
|
157
141
|
def isprotected(self) -> bool:
|
|
@@ -216,13 +200,14 @@ class Function(Parent):
|
|
|
216
200
|
self._parent = node.parent.frame()
|
|
217
201
|
self._decorators = node.decorators
|
|
218
202
|
self._lineno = node.lineno
|
|
203
|
+
if self.ismethod and not self.isstaticmethod:
|
|
204
|
+
if node.args.posonlyargs:
|
|
205
|
+
node.args.posonlyargs.pop(0)
|
|
206
|
+
elif node.args.args:
|
|
207
|
+
node.args.args.pop(0)
|
|
208
|
+
|
|
219
209
|
self._signature = self._signature.from_ast(
|
|
220
|
-
node
|
|
221
|
-
node.returns,
|
|
222
|
-
self.ismethod,
|
|
223
|
-
self.isstaticmethod,
|
|
224
|
-
ignore_args,
|
|
225
|
-
ignore_kwargs,
|
|
210
|
+
node, ignore_args, ignore_kwargs
|
|
226
211
|
)
|
|
227
212
|
if self.isinit and not check_class_constructor:
|
|
228
213
|
# docstring for __init__ is expected on the class
|
|
@@ -232,9 +217,7 @@ class Function(Parent):
|
|
|
232
217
|
relevant_doc_node = node.doc_node
|
|
233
218
|
|
|
234
219
|
if relevant_doc_node is not None:
|
|
235
|
-
self._docstring = self.docstring.from_ast(
|
|
236
|
-
relevant_doc_node, ignore_kwargs
|
|
237
|
-
)
|
|
220
|
+
self._docstring = self.docstring.from_ast(relevant_doc_node)
|
|
238
221
|
|
|
239
222
|
def __len__(self) -> int:
|
|
240
223
|
"""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,9 @@ 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
|
|
242
|
+
if self._func.error == _Error.UNICODE:
|
|
243
|
+
self._add(_E[902])
|
|
239
244
|
|
|
240
245
|
@property
|
|
241
246
|
def name(self) -> str:
|
|
@@ -247,6 +252,11 @@ class Failure(_t.List[Failed]):
|
|
|
247
252
|
"""Function line number."""
|
|
248
253
|
return self._func.lineno
|
|
249
254
|
|
|
255
|
+
@property
|
|
256
|
+
def retcode(self) -> int:
|
|
257
|
+
"""How to exit the program."""
|
|
258
|
+
return self._retcode
|
|
259
|
+
|
|
250
260
|
|
|
251
261
|
class Failures(_t.List[Failure]):
|
|
252
262
|
"""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.64.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.64.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
|