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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: docsig
3
- Version: 0.58.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.58.0, mccabe: 0.7.0, pycodestyle: 2.12.0, pyflakes: 3.2.0) CPython 3.8.13 on Darwin
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.58.0
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.58.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.58.0, mccabe: 0.7.0, pycodestyle: 2.12.0, pyflakes: 3.2.0) CPython 3.8.13 on Darwin
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.58.0
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.58.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
- header = f"{path}{failure.lineno} in {failure.name}"
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
- modules = _Modules(
251
- *tuple(_Path(i) for i in path),
252
- messages=disable or _Messages(),
253
- string=string,
254
- excludes=excludes,
255
- include_ignored=include_ignored,
256
- ignore_args=ignore_args,
257
- ignore_kwargs=ignore_kwargs,
258
- check_class_constructor=check_class_constructor,
259
- no_ansi=no_ansi,
260
- verbose=verbose,
261
- )
262
- for module in modules:
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, module.path, no_ansi=no_ansi)
280
- retcode = 1
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 max(retcode, modules.retcode)
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 Modules(_t.List[Parent]): # pylint: disable=too-many-instance-attributes
372
- """Sequence of ``Module`` objects parsed from Python modules or str.
313
+ class Ast(_t.NamedTuple):
314
+ """Object resulting from parsing an AST."""
373
315
 
374
- Recursively collect Python files from within all dirs that exist
375
- under paths provided.
316
+ module: _ast.Module = _ast.Module("")
317
+ err: Exception = Exception()
318
+ success: bool = False
376
319
 
377
- If string is provided, ignore paths.
320
+ @classmethod
321
+ def parse(cls, string: str) -> Ast:
322
+ """Parse an AST.
378
323
 
379
- :param paths: Path(s) to parse ``Module``(s) from.
380
- :param messages: List of checks to disable.
381
- :param excludes: List pf regular expression of files and dirs to
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
- if root is not None:
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
- msg = str(err).replace("\n", " ")
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)
@@ -8,4 +8,4 @@ Allows for access to the version internally without cyclic imports
8
8
  caused by accessing it through __init__.
9
9
  """
10
10
 
11
- __version__ = "0.58.0"
11
+ __version__ = "0.59.0"
@@ -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 docsig
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 = docsig.__name__
26
- version = docsig.__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
- buffer = io.StringIO()
145
- with contextlib.redirect_stdout(buffer):
146
- sys.argv = [
147
- __package__,
148
- self.filename,
149
- *[
150
- f"--{k.replace('_', '-')}"
151
- for k, v in self.options_dict.items()
152
- if v
153
- ],
154
- ]
155
- docsig.main()
156
-
157
- results = re.split(r"^(?!\s)", buffer.getvalue(), flags=re.MULTILINE)
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
- if not result:
160
- continue
161
-
162
- header, remainder = result.splitlines()[:2]
163
- lineno, func_name = header.split(":", 1)[1].split(" in ", 1)
164
- line = f"{remainder.lstrip().replace(':', '')} '{func_name}'"
165
- yield int(lineno), 0, line, self.__class__
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.58.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.58.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