docsig 0.58.0__tar.gz → 0.59.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.58.0 → docsig-0.59.0}/PKG-INFO +4 -4
- {docsig-0.58.0 → docsig-0.59.0}/README.rst +3 -3
- {docsig-0.58.0 → docsig-0.59.0}/docsig/_core.py +156 -19
- docsig-0.59.0/docsig/_files.py +118 -0
- {docsig-0.58.0 → docsig-0.59.0}/docsig/_module.py +13 -198
- {docsig-0.58.0 → docsig-0.59.0}/docsig/_version.py +1 -1
- {docsig-0.58.0 → docsig-0.59.0}/docsig/plugin.py +44 -27
- {docsig-0.58.0 → docsig-0.59.0}/pyproject.toml +2 -2
- {docsig-0.58.0 → docsig-0.59.0}/LICENSE +0 -0
- {docsig-0.58.0 → docsig-0.59.0}/docsig/__init__.py +0 -0
- {docsig-0.58.0 → docsig-0.59.0}/docsig/__main__.py +0 -0
- {docsig-0.58.0 → docsig-0.59.0}/docsig/_config.py +0 -0
- {docsig-0.58.0 → docsig-0.59.0}/docsig/_decorators.py +0 -0
- {docsig-0.58.0 → docsig-0.59.0}/docsig/_directives.py +0 -0
- {docsig-0.58.0 → docsig-0.59.0}/docsig/_hooks.py +0 -0
- {docsig-0.58.0 → docsig-0.59.0}/docsig/_main.py +0 -0
- {docsig-0.58.0 → docsig-0.59.0}/docsig/_report.py +0 -0
- {docsig-0.58.0 → docsig-0.59.0}/docsig/_stub.py +0 -0
- {docsig-0.58.0 → docsig-0.59.0}/docsig/_utils.py +0 -0
- {docsig-0.58.0 → docsig-0.59.0}/docsig/messages.py +0 -0
- {docsig-0.58.0 → docsig-0.59.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.59.0
|
|
4
4
|
Summary: Check signature params for proper documentation
|
|
5
5
|
Home-page: https://pypi.org/project/docsig/
|
|
6
6
|
License: MIT
|
|
@@ -169,7 +169,7 @@ ensure your installation has registered `docsig`
|
|
|
169
169
|
.. code-block:: console
|
|
170
170
|
|
|
171
171
|
$ flake8 --version
|
|
172
|
-
7.1.0 (docsig: 0.
|
|
172
|
+
7.1.0 (docsig: 0.59.0, mccabe: 0.7.0, pycodestyle: 2.12.0, pyflakes: 3.2.0) CPython 3.8.13 on Darwin
|
|
173
173
|
|
|
174
174
|
And now use `flake8` to lint your files
|
|
175
175
|
|
|
@@ -258,7 +258,7 @@ Standalone
|
|
|
258
258
|
|
|
259
259
|
repos:
|
|
260
260
|
- repo: https://github.com/jshwi/docsig
|
|
261
|
-
rev: v0.
|
|
261
|
+
rev: v0.59.0
|
|
262
262
|
hooks:
|
|
263
263
|
- id: docsig
|
|
264
264
|
args:
|
|
@@ -277,7 +277,7 @@ or integrated with ``flake8``
|
|
|
277
277
|
hooks:
|
|
278
278
|
- id: flake8
|
|
279
279
|
additional_dependencies:
|
|
280
|
-
- docsig==0.
|
|
280
|
+
- docsig==0.59.0
|
|
281
281
|
args:
|
|
282
282
|
- "--sig-check-class"
|
|
283
283
|
- "--sig-check-dunders"
|
|
@@ -143,7 +143,7 @@ ensure your installation has registered `docsig`
|
|
|
143
143
|
.. code-block:: console
|
|
144
144
|
|
|
145
145
|
$ flake8 --version
|
|
146
|
-
7.1.0 (docsig: 0.
|
|
146
|
+
7.1.0 (docsig: 0.59.0, mccabe: 0.7.0, pycodestyle: 2.12.0, pyflakes: 3.2.0) CPython 3.8.13 on Darwin
|
|
147
147
|
|
|
148
148
|
And now use `flake8` to lint your files
|
|
149
149
|
|
|
@@ -232,7 +232,7 @@ Standalone
|
|
|
232
232
|
|
|
233
233
|
repos:
|
|
234
234
|
- repo: https://github.com/jshwi/docsig
|
|
235
|
-
rev: v0.
|
|
235
|
+
rev: v0.59.0
|
|
236
236
|
hooks:
|
|
237
237
|
- id: docsig
|
|
238
238
|
args:
|
|
@@ -251,7 +251,7 @@ or integrated with ``flake8``
|
|
|
251
251
|
hooks:
|
|
252
252
|
- id: flake8
|
|
253
253
|
additional_dependencies:
|
|
254
|
-
- docsig==0.
|
|
254
|
+
- docsig==0.59.0
|
|
255
255
|
args:
|
|
256
256
|
- "--sig-check-class"
|
|
257
257
|
- "--sig-check-dunders"
|
|
@@ -8,13 +8,20 @@ from __future__ import annotations as _
|
|
|
8
8
|
import sys as _sys
|
|
9
9
|
from pathlib import Path as _Path
|
|
10
10
|
|
|
11
|
+
import astroid as _ast
|
|
12
|
+
|
|
11
13
|
from . import _decorators
|
|
14
|
+
from ._directives import Directives as _Directives
|
|
15
|
+
from ._files import FILE_INFO as _FILE_INFO
|
|
16
|
+
from ._files import Paths as _Paths
|
|
17
|
+
from ._module import Ast as _Ast
|
|
12
18
|
from ._module import Function as _Function
|
|
13
|
-
from ._module import Modules as _Modules
|
|
14
19
|
from ._module import Parent as _Parent
|
|
15
20
|
from ._report import Failure as _Failure
|
|
16
21
|
from ._report import Failures as _Failures
|
|
22
|
+
from ._utils import pretty_print_error as _pretty_print_error
|
|
17
23
|
from ._utils import print_checks as _print_checks
|
|
24
|
+
from ._utils import vprint as _vprint
|
|
18
25
|
from .messages import TEMPLATE as _TEMPLATE
|
|
19
26
|
from .messages import Messages as _Messages
|
|
20
27
|
|
|
@@ -158,7 +165,8 @@ def _report(
|
|
|
158
165
|
failures: _Failures, path: str | None = None, no_ansi: bool = False
|
|
159
166
|
) -> None:
|
|
160
167
|
for failure in failures:
|
|
161
|
-
|
|
168
|
+
module = f"{path}:" if path is not None else ""
|
|
169
|
+
header = f"{module}{failure.lineno} in {failure.name}"
|
|
162
170
|
if not no_ansi and _sys.stdout.isatty():
|
|
163
171
|
header = f"\033[35m{header}\033[0m"
|
|
164
172
|
|
|
@@ -176,6 +184,97 @@ def _report(
|
|
|
176
184
|
print(f" hint: {item.hint}")
|
|
177
185
|
|
|
178
186
|
|
|
187
|
+
def runner( # pylint: disable=too-many-locals,too-many-arguments
|
|
188
|
+
file: str | _Path,
|
|
189
|
+
disable: _Messages | None = None,
|
|
190
|
+
check_class: bool = False,
|
|
191
|
+
check_class_constructor: bool = False,
|
|
192
|
+
check_dunders: bool = False,
|
|
193
|
+
check_nested: bool = False,
|
|
194
|
+
check_overridden: bool = False,
|
|
195
|
+
check_protected: bool = False,
|
|
196
|
+
check_property_returns: bool = False,
|
|
197
|
+
ignore_no_params: bool = False,
|
|
198
|
+
ignore_args: bool = False,
|
|
199
|
+
ignore_kwargs: bool = False,
|
|
200
|
+
ignore_typechecker: bool = False,
|
|
201
|
+
check_protected_class_methods: bool = False,
|
|
202
|
+
no_ansi: bool = False,
|
|
203
|
+
verbose: bool = False,
|
|
204
|
+
target: _Messages | None = None,
|
|
205
|
+
) -> tuple[_Failures, int]:
|
|
206
|
+
"""Per path runner.
|
|
207
|
+
|
|
208
|
+
:param file: Path to check.
|
|
209
|
+
:param disable: Messages to disable.
|
|
210
|
+
:param check_class: Check class docstrings.
|
|
211
|
+
:param check_class_constructor: Check ``__init__`` methods. Note that this
|
|
212
|
+
is mutually incompatible with check_class.
|
|
213
|
+
:param check_dunders: Check dunder methods
|
|
214
|
+
:param check_nested: Check nested functions and classes.
|
|
215
|
+
:param check_overridden: Check overridden methods
|
|
216
|
+
:param check_protected: Check protected functions and classes.
|
|
217
|
+
:param check_property_returns: Run return checks on properties.
|
|
218
|
+
:param ignore_no_params: Ignore docstrings where parameters are not
|
|
219
|
+
documented
|
|
220
|
+
:param ignore_args: Ignore args prefixed with an asterisk.
|
|
221
|
+
:param ignore_kwargs: Ignore kwargs prefixed with two asterisks.
|
|
222
|
+
:param ignore_typechecker: Ignore checking return values.
|
|
223
|
+
:param check_protected_class_methods: Check public methods belonging
|
|
224
|
+
to protected classes.
|
|
225
|
+
:param no_ansi: Disable ANSI output.
|
|
226
|
+
:param verbose: increase output verbosity.
|
|
227
|
+
:param target: List of errors to target.
|
|
228
|
+
:return: Exit status for whether test failed or not.
|
|
229
|
+
"""
|
|
230
|
+
failures = _Failures()
|
|
231
|
+
path = _Path(file)
|
|
232
|
+
string = path.read_text(encoding="utf-8")
|
|
233
|
+
ast = _Ast.parse(string)
|
|
234
|
+
if ast.success:
|
|
235
|
+
_vprint(
|
|
236
|
+
_FILE_INFO.format(path=path, msg="Parsing Python code successful"),
|
|
237
|
+
verbose,
|
|
238
|
+
)
|
|
239
|
+
module = _Parent(
|
|
240
|
+
ast.module,
|
|
241
|
+
_Directives(string, disable or _Messages()),
|
|
242
|
+
path,
|
|
243
|
+
ignore_args,
|
|
244
|
+
ignore_kwargs,
|
|
245
|
+
check_class_constructor,
|
|
246
|
+
)
|
|
247
|
+
failures = _get_failures(
|
|
248
|
+
module,
|
|
249
|
+
check_class,
|
|
250
|
+
check_class_constructor,
|
|
251
|
+
check_dunders,
|
|
252
|
+
check_nested,
|
|
253
|
+
check_overridden,
|
|
254
|
+
check_protected,
|
|
255
|
+
check_property_returns,
|
|
256
|
+
ignore_no_params,
|
|
257
|
+
ignore_typechecker,
|
|
258
|
+
check_protected_class_methods,
|
|
259
|
+
no_ansi,
|
|
260
|
+
target or _Messages(),
|
|
261
|
+
)
|
|
262
|
+
return failures, 0
|
|
263
|
+
|
|
264
|
+
if path.name.endswith(".py"):
|
|
265
|
+
# pass by silently for files that do not end with .py, may
|
|
266
|
+
# result in a 123 syntax error exit status in the future
|
|
267
|
+
print(path, file=_sys.stderr)
|
|
268
|
+
_pretty_print_error(type(ast.err), str(ast.err), no_ansi=no_ansi)
|
|
269
|
+
return failures, 1
|
|
270
|
+
|
|
271
|
+
_vprint(
|
|
272
|
+
_FILE_INFO.format(path=path, msg=str(ast.err)),
|
|
273
|
+
verbose,
|
|
274
|
+
)
|
|
275
|
+
return failures, 0
|
|
276
|
+
|
|
277
|
+
|
|
179
278
|
@_decorators.parse_msgs
|
|
180
279
|
@_decorators.handle_deprecations
|
|
181
280
|
@_decorators.validate_args
|
|
@@ -239,7 +338,6 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
239
338
|
checks.
|
|
240
339
|
:return: Exit status for whether test failed or not.
|
|
241
340
|
"""
|
|
242
|
-
retcode = 0
|
|
243
341
|
if list_checks:
|
|
244
342
|
return int(bool(_print_checks())) # type: ignore
|
|
245
343
|
|
|
@@ -247,19 +345,56 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
247
345
|
if exclude is not None:
|
|
248
346
|
excludes.append(exclude)
|
|
249
347
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
348
|
+
if string is None:
|
|
349
|
+
retcode = 0
|
|
350
|
+
paths = _Paths(
|
|
351
|
+
*tuple(_Path(i) for i in path),
|
|
352
|
+
excludes=excludes,
|
|
353
|
+
include_ignored=include_ignored,
|
|
354
|
+
verbose=verbose,
|
|
355
|
+
)
|
|
356
|
+
for file in paths:
|
|
357
|
+
failures, parse_retcode = runner(
|
|
358
|
+
file,
|
|
359
|
+
disable,
|
|
360
|
+
check_class,
|
|
361
|
+
check_class_constructor,
|
|
362
|
+
check_dunders,
|
|
363
|
+
check_nested,
|
|
364
|
+
check_overridden,
|
|
365
|
+
check_protected,
|
|
366
|
+
check_property_returns,
|
|
367
|
+
ignore_no_params,
|
|
368
|
+
ignore_args,
|
|
369
|
+
ignore_kwargs,
|
|
370
|
+
ignore_typechecker,
|
|
371
|
+
check_protected_class_methods,
|
|
372
|
+
no_ansi,
|
|
373
|
+
verbose,
|
|
374
|
+
target,
|
|
375
|
+
)
|
|
376
|
+
retcode = retcode or parse_retcode
|
|
377
|
+
if failures:
|
|
378
|
+
_report(failures, str(file), no_ansi)
|
|
379
|
+
retcode = 1
|
|
380
|
+
|
|
381
|
+
return retcode
|
|
382
|
+
|
|
383
|
+
ast = _Ast.parse(string)
|
|
384
|
+
if ast.success:
|
|
385
|
+
_vprint(
|
|
386
|
+
_FILE_INFO.format(
|
|
387
|
+
path="stdin", msg="Parsing Python code successful"
|
|
388
|
+
),
|
|
389
|
+
verbose,
|
|
390
|
+
)
|
|
391
|
+
module = _Parent(
|
|
392
|
+
_ast.parse(string),
|
|
393
|
+
_Directives(string, messages=disable or _Messages()),
|
|
394
|
+
ignore_args=ignore_args,
|
|
395
|
+
ignore_kwargs=ignore_kwargs,
|
|
396
|
+
check_class_constructor=check_class_constructor,
|
|
397
|
+
)
|
|
263
398
|
failures = _get_failures(
|
|
264
399
|
module,
|
|
265
400
|
check_class,
|
|
@@ -276,7 +411,9 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
|
|
|
276
411
|
target or _Messages(),
|
|
277
412
|
)
|
|
278
413
|
if failures:
|
|
279
|
-
_report(failures,
|
|
280
|
-
|
|
414
|
+
_report(failures, no_ansi=no_ansi)
|
|
415
|
+
return 1
|
|
416
|
+
|
|
417
|
+
_vprint(_FILE_INFO.format(path="stdin", msg=str(ast.err)), verbose)
|
|
281
418
|
|
|
282
|
-
return
|
|
419
|
+
return 0
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""
|
|
2
|
+
docsig._files
|
|
3
|
+
=============
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations as _
|
|
7
|
+
|
|
8
|
+
import os as _os
|
|
9
|
+
import re as _re
|
|
10
|
+
import typing as _t
|
|
11
|
+
from pathlib import Path as _Path
|
|
12
|
+
|
|
13
|
+
from pathspec import PathSpec as _PathSpec
|
|
14
|
+
from pathspec.patterns import GitWildMatchPattern as _GitWildMatchPattern
|
|
15
|
+
|
|
16
|
+
from ._utils import vprint as _vprint
|
|
17
|
+
|
|
18
|
+
FILE_INFO = "{path}: {msg}"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class _Gitignore(_PathSpec):
|
|
22
|
+
def _get_repo_relative_to(self, path: _Path) -> _Path | None:
|
|
23
|
+
if (path / ".git" / "HEAD").is_file():
|
|
24
|
+
return path
|
|
25
|
+
|
|
26
|
+
if str(path) == _os.path.abspath(_os.sep):
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
return self._get_repo_relative_to(path.parent)
|
|
30
|
+
|
|
31
|
+
def __init__(self) -> None:
|
|
32
|
+
patterns = []
|
|
33
|
+
repo = self._get_repo_relative_to(_Path.cwd())
|
|
34
|
+
# only consider gitignore patterns valid if inside a git repo
|
|
35
|
+
# there might be stray gitignore files lying about
|
|
36
|
+
if repo is not None:
|
|
37
|
+
# add patterns from all gitignore files
|
|
38
|
+
# adjust patterns to account for their relative paths
|
|
39
|
+
for file in repo.rglob(".gitignore"):
|
|
40
|
+
for pattern in file.read_text(encoding="utf-8").splitlines():
|
|
41
|
+
if pattern.startswith("#"):
|
|
42
|
+
continue
|
|
43
|
+
|
|
44
|
+
# if pattern starts with "/" then os.path.join will
|
|
45
|
+
# consider it in the filesystem root, and it will
|
|
46
|
+
# only ever return /pattern
|
|
47
|
+
if pattern.startswith("/"):
|
|
48
|
+
pattern = pattern[1:]
|
|
49
|
+
|
|
50
|
+
# use os.path.dirname, so it joins without a leading
|
|
51
|
+
# "./", like it does with pathlib parent
|
|
52
|
+
# use os.path.join so trailing slash is preserved
|
|
53
|
+
# replace sep with "/" as, even on windows,
|
|
54
|
+
# gitignore patterns only ever use "/"
|
|
55
|
+
patterns.append(
|
|
56
|
+
_os.path.join(
|
|
57
|
+
_os.path.dirname(file.relative_to(repo)),
|
|
58
|
+
pattern.strip(),
|
|
59
|
+
).replace(_os.sep, "/")
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
super().__init__(map(_GitWildMatchPattern, patterns))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class Paths(_t.List[_Path]): # pylint: disable=too-many-instance-attributes
|
|
66
|
+
"""Collect a list of valid paths.
|
|
67
|
+
|
|
68
|
+
:param paths: Path(s) to parse ``Module``(s) from.
|
|
69
|
+
:param excludes: List pf regular expression of files and dirs to
|
|
70
|
+
exclude from checks.
|
|
71
|
+
:param include_ignored: Check files even if they match a gitignore
|
|
72
|
+
pattern.
|
|
73
|
+
:param verbose: increase output verbosity.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
def __init__( # pylint: disable=too-many-arguments
|
|
77
|
+
self,
|
|
78
|
+
*paths: _Path,
|
|
79
|
+
excludes: list[str],
|
|
80
|
+
include_ignored: bool = False,
|
|
81
|
+
verbose: bool = False,
|
|
82
|
+
) -> None:
|
|
83
|
+
super().__init__()
|
|
84
|
+
self._excludes = excludes
|
|
85
|
+
self._include_ignored = include_ignored
|
|
86
|
+
self._verbose = verbose
|
|
87
|
+
self._gitignore = _Gitignore()
|
|
88
|
+
for path in paths:
|
|
89
|
+
self._populate(path)
|
|
90
|
+
|
|
91
|
+
self.sort()
|
|
92
|
+
|
|
93
|
+
def _populate(self, root: _Path) -> None:
|
|
94
|
+
if not root.exists():
|
|
95
|
+
raise FileNotFoundError(root)
|
|
96
|
+
|
|
97
|
+
if str(root) != "." and any(
|
|
98
|
+
_re.match(i, root.name) for i in self._excludes
|
|
99
|
+
):
|
|
100
|
+
_vprint(
|
|
101
|
+
FILE_INFO.format(path=root, msg="in exclude list, skipping"),
|
|
102
|
+
self._verbose,
|
|
103
|
+
)
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
if not self._include_ignored and self._gitignore.match_file(root):
|
|
107
|
+
_vprint(
|
|
108
|
+
FILE_INFO.format(path=root, msg="in gitignore, skipping"),
|
|
109
|
+
self._verbose,
|
|
110
|
+
)
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
if root.is_file():
|
|
114
|
+
self.append(root)
|
|
115
|
+
|
|
116
|
+
if root.is_dir():
|
|
117
|
+
for path in root.iterdir():
|
|
118
|
+
self._populate(path)
|
|
@@ -5,71 +5,19 @@ docsig._module
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations as _
|
|
7
7
|
|
|
8
|
-
import os as _os
|
|
9
8
|
import re as _re
|
|
10
|
-
import sys as _sys
|
|
11
9
|
import typing as _t
|
|
12
10
|
from pathlib import Path as _Path
|
|
13
11
|
|
|
14
12
|
import astroid as _ast
|
|
15
|
-
from pathspec import PathSpec as _PathSpec
|
|
16
|
-
from pathspec.patterns import GitWildMatchPattern as _GitWildMatchPattern
|
|
17
13
|
|
|
18
14
|
from ._directives import Comment as _Comment
|
|
19
15
|
from ._directives import Directives as _Directives
|
|
20
16
|
from ._stub import Docstring as _Docstring
|
|
21
17
|
from ._stub import RetType as _RetType
|
|
22
18
|
from ._stub import Signature as _Signature
|
|
23
|
-
from ._utils import pretty_print_error as _pretty_print_error
|
|
24
|
-
from ._utils import vprint as _vprint
|
|
25
19
|
from .messages import Messages as _Messages
|
|
26
20
|
|
|
27
|
-
_FILE_INFO = "{path}: {msg}"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class _Gitignore(_PathSpec):
|
|
31
|
-
def _get_repo_relative_to(self, path: _Path) -> _Path | None:
|
|
32
|
-
if (path / ".git" / "HEAD").is_file():
|
|
33
|
-
return path
|
|
34
|
-
|
|
35
|
-
if str(path) == _os.path.abspath(_os.sep):
|
|
36
|
-
return None
|
|
37
|
-
|
|
38
|
-
return self._get_repo_relative_to(path.parent)
|
|
39
|
-
|
|
40
|
-
def __init__(self) -> None:
|
|
41
|
-
patterns = []
|
|
42
|
-
repo = self._get_repo_relative_to(_Path.cwd())
|
|
43
|
-
# only consider gitignore patterns valid if inside a git repo
|
|
44
|
-
# there might be stray gitignore files lying about
|
|
45
|
-
if repo is not None:
|
|
46
|
-
# add patterns from all gitignore files
|
|
47
|
-
# adjust patterns to account for their relative paths
|
|
48
|
-
for file in repo.rglob(".gitignore"):
|
|
49
|
-
for pattern in file.read_text(encoding="utf-8").splitlines():
|
|
50
|
-
if pattern.startswith("#"):
|
|
51
|
-
continue
|
|
52
|
-
|
|
53
|
-
# if pattern starts with "/" then os.path.join will
|
|
54
|
-
# consider it in the filesystem root, and it will
|
|
55
|
-
# only ever return /pattern
|
|
56
|
-
if pattern.startswith("/"):
|
|
57
|
-
pattern = pattern[1:]
|
|
58
|
-
|
|
59
|
-
# use os.path.dirname, so it joins without a leading
|
|
60
|
-
# "./", like it does with pathlib parent
|
|
61
|
-
# use os.path.join so trailing slash is preserved
|
|
62
|
-
# replace sep with "/" as, even on windows,
|
|
63
|
-
# gitignore patterns only ever use "/"
|
|
64
|
-
patterns.append(
|
|
65
|
-
_os.path.join(
|
|
66
|
-
_os.path.dirname(file.relative_to(repo)),
|
|
67
|
-
pattern.strip(),
|
|
68
|
-
).replace(_os.sep, "/")
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
super().__init__(map(_GitWildMatchPattern, patterns))
|
|
72
|
-
|
|
73
21
|
|
|
74
22
|
class Parent(_t.List["Parent"]):
|
|
75
23
|
"""Represents an object that contains functions or methods.
|
|
@@ -98,7 +46,6 @@ class Parent(_t.List["Parent"]):
|
|
|
98
46
|
) -> None:
|
|
99
47
|
super().__init__()
|
|
100
48
|
self._name = node.name
|
|
101
|
-
self._path = f"{path}:" if path is not None else ""
|
|
102
49
|
self._overloads: dict[str, Function] = {}
|
|
103
50
|
self._imports = imports or {}
|
|
104
51
|
self._parse_ast(
|
|
@@ -185,11 +132,6 @@ class Parent(_t.List["Parent"]):
|
|
|
185
132
|
check_class_constructor,
|
|
186
133
|
)
|
|
187
134
|
|
|
188
|
-
@property
|
|
189
|
-
def path(self) -> str:
|
|
190
|
-
"""Representation of path to parent."""
|
|
191
|
-
return self._path
|
|
192
|
-
|
|
193
135
|
@property
|
|
194
136
|
def isprotected(self) -> bool:
|
|
195
137
|
"""Boolean value for whether class is protected."""
|
|
@@ -368,148 +310,21 @@ class Function(Parent):
|
|
|
368
310
|
self._signature.overload(rettype)
|
|
369
311
|
|
|
370
312
|
|
|
371
|
-
class
|
|
372
|
-
"""
|
|
313
|
+
class Ast(_t.NamedTuple):
|
|
314
|
+
"""Object resulting from parsing an AST."""
|
|
373
315
|
|
|
374
|
-
|
|
375
|
-
|
|
316
|
+
module: _ast.Module = _ast.Module("")
|
|
317
|
+
err: Exception = Exception()
|
|
318
|
+
success: bool = False
|
|
376
319
|
|
|
377
|
-
|
|
320
|
+
@classmethod
|
|
321
|
+
def parse(cls, string: str) -> Ast:
|
|
322
|
+
"""Parse an AST.
|
|
378
323
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
exclude from checks.
|
|
383
|
-
:param string: String to parse if provided.
|
|
384
|
-
:param include_ignored: Check files even if they match a gitignore
|
|
385
|
-
pattern.
|
|
386
|
-
:param ignore_args: Ignore args prefixed with an asterisk.
|
|
387
|
-
:param ignore_kwargs: Ignore kwargs prefixed with two asterisks.
|
|
388
|
-
:param check_class_constructor: Check the class constructor's
|
|
389
|
-
docstring. Otherwise, expect the constructor's documentation to
|
|
390
|
-
be on the class level docstring.
|
|
391
|
-
:param no_ansi: Disable ANSI output.
|
|
392
|
-
:param verbose: increase output verbosity.
|
|
393
|
-
"""
|
|
394
|
-
|
|
395
|
-
# handle errors here before appending a module
|
|
396
|
-
def _add_module( # pylint: disable=too-many-arguments
|
|
397
|
-
self,
|
|
398
|
-
messages: _Messages,
|
|
399
|
-
string: str | None = None,
|
|
400
|
-
root: _Path | None = None,
|
|
401
|
-
ignore_args: bool = False,
|
|
402
|
-
ignore_kwargs: bool = False,
|
|
403
|
-
check_class_constructor: bool = False,
|
|
404
|
-
) -> None:
|
|
324
|
+
:param string: String to parse.
|
|
325
|
+
:return: Constructed AST object, whether successful or not.
|
|
326
|
+
"""
|
|
405
327
|
try:
|
|
406
|
-
|
|
407
|
-
string = root.read_text(encoding="utf-8")
|
|
408
|
-
|
|
409
|
-
# empty string won't happen but keeps the
|
|
410
|
-
# typechecker happy
|
|
411
|
-
string = string or ""
|
|
412
|
-
self.append(
|
|
413
|
-
Parent(
|
|
414
|
-
_ast.parse(string),
|
|
415
|
-
_Directives(string, messages),
|
|
416
|
-
root,
|
|
417
|
-
ignore_args,
|
|
418
|
-
ignore_kwargs,
|
|
419
|
-
check_class_constructor,
|
|
420
|
-
)
|
|
421
|
-
)
|
|
422
|
-
_vprint(
|
|
423
|
-
_FILE_INFO.format(
|
|
424
|
-
path=root or "stdin", msg="Parsing Python code successful"
|
|
425
|
-
),
|
|
426
|
-
self._verbose,
|
|
427
|
-
)
|
|
328
|
+
return cls(module=_ast.parse(string), success=True)
|
|
428
329
|
except (_ast.AstroidSyntaxError, UnicodeDecodeError) as err:
|
|
429
|
-
|
|
430
|
-
if root is not None and root.name.endswith(".py"):
|
|
431
|
-
# pass by silently for files that do not end with .py,
|
|
432
|
-
# may result in a 123 syntax error exit status in the
|
|
433
|
-
# future
|
|
434
|
-
print(root, file=_sys.stderr)
|
|
435
|
-
_pretty_print_error(type(err), msg, no_ansi=self._no_ansi)
|
|
436
|
-
self._retcode = 1
|
|
437
|
-
|
|
438
|
-
_vprint(
|
|
439
|
-
_FILE_INFO.format(path=root or "stdin", msg=msg),
|
|
440
|
-
self._verbose,
|
|
441
|
-
)
|
|
442
|
-
|
|
443
|
-
def __init__( # pylint: disable=too-many-arguments
|
|
444
|
-
self,
|
|
445
|
-
*paths: _Path,
|
|
446
|
-
messages: _Messages,
|
|
447
|
-
excludes: list[str],
|
|
448
|
-
string: str | None = None,
|
|
449
|
-
include_ignored: bool = False,
|
|
450
|
-
ignore_args: bool = False,
|
|
451
|
-
ignore_kwargs: bool = False,
|
|
452
|
-
check_class_constructor: bool = False,
|
|
453
|
-
no_ansi: bool = False,
|
|
454
|
-
verbose: bool = False,
|
|
455
|
-
) -> None:
|
|
456
|
-
super().__init__()
|
|
457
|
-
self._messages = messages
|
|
458
|
-
self._excludes = excludes
|
|
459
|
-
self._include_ignored = include_ignored
|
|
460
|
-
self._ignore_args = ignore_args
|
|
461
|
-
self._ignore_kwargs = ignore_kwargs
|
|
462
|
-
self._check_class_constructor = check_class_constructor
|
|
463
|
-
self._no_ansi = no_ansi
|
|
464
|
-
self._verbose = verbose
|
|
465
|
-
self._retcode = 0
|
|
466
|
-
self._gitignore = _Gitignore()
|
|
467
|
-
if string is not None:
|
|
468
|
-
self._add_module(
|
|
469
|
-
messages,
|
|
470
|
-
string=string,
|
|
471
|
-
ignore_args=ignore_args,
|
|
472
|
-
ignore_kwargs=ignore_kwargs,
|
|
473
|
-
check_class_constructor=check_class_constructor,
|
|
474
|
-
)
|
|
475
|
-
else:
|
|
476
|
-
for path in paths:
|
|
477
|
-
self._populate(path)
|
|
478
|
-
|
|
479
|
-
def _populate(self, root: _Path) -> None:
|
|
480
|
-
if not root.exists():
|
|
481
|
-
raise FileNotFoundError(root)
|
|
482
|
-
|
|
483
|
-
if str(root) != "." and any(
|
|
484
|
-
_re.match(i, root.name) for i in self._excludes
|
|
485
|
-
):
|
|
486
|
-
_vprint(
|
|
487
|
-
_FILE_INFO.format(path=root, msg="in exclude list, skipping"),
|
|
488
|
-
self._verbose,
|
|
489
|
-
)
|
|
490
|
-
return
|
|
491
|
-
|
|
492
|
-
if not self._include_ignored and self._gitignore.match_file(root):
|
|
493
|
-
_vprint(
|
|
494
|
-
_FILE_INFO.format(path=root, msg="in gitignore, skipping"),
|
|
495
|
-
self._verbose,
|
|
496
|
-
)
|
|
497
|
-
return
|
|
498
|
-
|
|
499
|
-
if root.is_file():
|
|
500
|
-
self._add_module(
|
|
501
|
-
self._messages,
|
|
502
|
-
root=root,
|
|
503
|
-
ignore_args=self._ignore_args,
|
|
504
|
-
ignore_kwargs=self._ignore_kwargs,
|
|
505
|
-
check_class_constructor=self._check_class_constructor,
|
|
506
|
-
)
|
|
507
|
-
|
|
508
|
-
if root.is_dir():
|
|
509
|
-
for path in root.iterdir():
|
|
510
|
-
self._populate(path)
|
|
511
|
-
|
|
512
|
-
@property
|
|
513
|
-
def retcode(self) -> int:
|
|
514
|
-
"""Return code to propagate to main."""
|
|
515
|
-
return self._retcode
|
|
330
|
+
return cls(err=err)
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"""Flake8 implementation of docsig."""
|
|
2
2
|
|
|
3
3
|
import ast
|
|
4
|
-
import contextlib
|
|
5
|
-
import io
|
|
6
|
-
import re
|
|
7
4
|
import sys
|
|
8
5
|
import typing as t
|
|
9
6
|
from argparse import Namespace
|
|
10
7
|
|
|
11
|
-
import
|
|
8
|
+
from ._config import Parser
|
|
9
|
+
from ._core import runner
|
|
10
|
+
from ._version import __version__
|
|
11
|
+
from .messages import FLAKE8
|
|
12
12
|
|
|
13
13
|
Flake8Error = t.Tuple[int, int, str, t.Type]
|
|
14
14
|
|
|
@@ -22,8 +22,8 @@ class Docsig:
|
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
24
|
off_by_default = False
|
|
25
|
-
name =
|
|
26
|
-
version =
|
|
25
|
+
name = __package__
|
|
26
|
+
version = __version__
|
|
27
27
|
options_dict: t.Dict[str, bool] = {}
|
|
28
28
|
|
|
29
29
|
def __init__(self, tree: ast.Module, filename: str) -> None:
|
|
@@ -141,25 +141,42 @@ class Docsig:
|
|
|
141
141
|
|
|
142
142
|
:return: Flake8 error, if there is one.
|
|
143
143
|
"""
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
144
|
+
sys.argv = [
|
|
145
|
+
__package__,
|
|
146
|
+
self.filename,
|
|
147
|
+
*[
|
|
148
|
+
f"--{k.replace('_', '-')}"
|
|
149
|
+
for k, v in self.options_dict.items()
|
|
150
|
+
if v
|
|
151
|
+
],
|
|
152
|
+
]
|
|
153
|
+
p = Parser()
|
|
154
|
+
results = runner(
|
|
155
|
+
self.filename,
|
|
156
|
+
check_class=p.args.check_class,
|
|
157
|
+
check_class_constructor=p.args.check_class_constructor,
|
|
158
|
+
check_dunders=p.args.check_dunders,
|
|
159
|
+
check_protected_class_methods=(
|
|
160
|
+
p.args.check_protected_class_methods
|
|
161
|
+
),
|
|
162
|
+
check_nested=p.args.check_nested,
|
|
163
|
+
check_overridden=p.args.check_overridden,
|
|
164
|
+
check_protected=p.args.check_protected,
|
|
165
|
+
check_property_returns=p.args.check_property_returns,
|
|
166
|
+
ignore_no_params=p.args.ignore_no_params,
|
|
167
|
+
ignore_args=p.args.ignore_args,
|
|
168
|
+
ignore_kwargs=p.args.ignore_kwargs,
|
|
169
|
+
ignore_typechecker=p.args.ignore_typechecker,
|
|
170
|
+
no_ansi=p.args.no_ansi,
|
|
171
|
+
)[0]
|
|
158
172
|
for result in results:
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
173
|
+
for info in result:
|
|
174
|
+
line = "{msg} '{name}'".format(
|
|
175
|
+
msg=FLAKE8.format(
|
|
176
|
+
ref=info.ref,
|
|
177
|
+
description=info.description,
|
|
178
|
+
symbolic=info.symbolic,
|
|
179
|
+
),
|
|
180
|
+
name=info.name,
|
|
181
|
+
)
|
|
182
|
+
yield info.lineno, 0, line, self.__class__
|
|
@@ -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.59.0"
|
|
15
15
|
message = "bump: version {current_version} → {new_version}"
|
|
16
16
|
sign_tags = true
|
|
17
17
|
tag = true
|
|
@@ -132,7 +132,7 @@ maintainers = [
|
|
|
132
132
|
name = "docsig"
|
|
133
133
|
readme = "README.rst"
|
|
134
134
|
repository = "https://github.com/jshwi/docsig"
|
|
135
|
-
version = "0.
|
|
135
|
+
version = "0.59.0"
|
|
136
136
|
|
|
137
137
|
[tool.poetry.dependencies]
|
|
138
138
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|