docsig 0.49.2__tar.gz → 0.51.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.49.2
3
+ Version: 0.51.0
4
4
  Summary: Check signature params for proper documentation
5
5
  Home-page: https://pypi.org/project/docsig/
6
6
  License: MIT
@@ -22,6 +22,7 @@ Requires-Dist: Sphinx (>=7.0.0,<8.0.0)
22
22
  Requires-Dist: arcon (>=0.4.0)
23
23
  Requires-Dist: astroid (>=3.0.1,<4.0.0)
24
24
  Requires-Dist: click (>=8.1.7,<9.0.0)
25
+ Requires-Dist: pathspec (>=0.12.1,<0.13.0)
25
26
  Project-URL: Documentation, https://docsig.readthedocs.io/en/latest
26
27
  Project-URL: Repository, https://github.com/jshwi/docsig
27
28
  Description-Content-Type: text/x-rst
@@ -106,8 +107,9 @@ Commandline
106
107
  .. code-block:: console
107
108
 
108
109
  usage: docsig [-h] [-V] [-l] [-c | -C] [-D] [-m] [-N] [-o] [-p] [-P] [-i] [-a]
109
- [-k] [-n] [-S] [-v] [-s STR] [-d LIST] [-t LIST] [-e EXCLUDE]
110
- [path [path ...]]
110
+ [-k] [-I] [-n] [-S] [-v] [-s STR] [-d LIST] [-t LIST]
111
+ [-e PATTERN]
112
+ [path [path ...]]
111
113
 
112
114
  Check signature params for proper documentation
113
115
 
@@ -132,6 +134,8 @@ Commandline
132
134
  documented
133
135
  -a, --ignore-args ignore args prefixed with an asterisk
134
136
  -k, --ignore-kwargs ignore kwargs prefixed with two asterisks
137
+ -I, --include-ignored check files even if they match a gitignore
138
+ pattern
135
139
  -n, --no-ansi disable ansi output
136
140
  -S, --summary print a summarised report
137
141
  -v, --verbose increase output verbosity
@@ -223,7 +227,7 @@ It can be added to your .pre-commit-config.yaml as follows:
223
227
 
224
228
  repos:
225
229
  - repo: https://github.com/jshwi/docsig
226
- rev: v0.49.2
230
+ rev: v0.51.0
227
231
  hooks:
228
232
  - id: docsig
229
233
  args:
@@ -78,8 +78,9 @@ Commandline
78
78
  .. code-block:: console
79
79
 
80
80
  usage: docsig [-h] [-V] [-l] [-c | -C] [-D] [-m] [-N] [-o] [-p] [-P] [-i] [-a]
81
- [-k] [-n] [-S] [-v] [-s STR] [-d LIST] [-t LIST] [-e EXCLUDE]
82
- [path [path ...]]
81
+ [-k] [-I] [-n] [-S] [-v] [-s STR] [-d LIST] [-t LIST]
82
+ [-e PATTERN]
83
+ [path [path ...]]
83
84
 
84
85
  Check signature params for proper documentation
85
86
 
@@ -104,6 +105,8 @@ Commandline
104
105
  documented
105
106
  -a, --ignore-args ignore args prefixed with an asterisk
106
107
  -k, --ignore-kwargs ignore kwargs prefixed with two asterisks
108
+ -I, --include-ignored check files even if they match a gitignore
109
+ pattern
107
110
  -n, --no-ansi disable ansi output
108
111
  -S, --summary print a summarised report
109
112
  -v, --verbose increase output verbosity
@@ -195,7 +198,7 @@ It can be added to your .pre-commit-config.yaml as follows:
195
198
 
196
199
  repos:
197
200
  - repo: https://github.com/jshwi/docsig
198
- rev: v0.49.2
201
+ rev: v0.51.0
199
202
  hooks:
200
203
  - id: docsig
201
204
  args:
@@ -109,6 +109,12 @@ class Parser(_ArgumentParser):
109
109
  action="store_true",
110
110
  help="ignore kwargs prefixed with two asterisks",
111
111
  )
112
+ self.add_argument(
113
+ "-I",
114
+ "--include-ignored",
115
+ action="store_true",
116
+ help="check files even if they match a gitignore pattern",
117
+ )
112
118
  self.add_argument(
113
119
  "-n",
114
120
  "--no-ansi",
@@ -131,6 +131,7 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
131
131
  check_overridden: bool = False,
132
132
  check_protected: bool = False,
133
133
  check_property_returns: bool = False,
134
+ include_ignored: bool = False,
134
135
  ignore_no_params: bool = False,
135
136
  ignore_args: bool = False,
136
137
  ignore_kwargs: bool = False,
@@ -163,6 +164,8 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
163
164
  :param check_overridden: Check overridden methods
164
165
  :param check_protected: Check protected functions and classes.
165
166
  :param check_property_returns: Run return checks on properties.
167
+ :param include_ignored: Check files even if they match a gitignore
168
+ pattern.
166
169
  :param ignore_no_params: Ignore docstrings where parameters are not
167
170
  documented
168
171
  :param ignore_args: Ignore args prefixed with an asterisk.
@@ -188,9 +191,11 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
188
191
  disable=disable or [],
189
192
  string=string,
190
193
  excludes=excludes,
194
+ include_ignored=include_ignored,
191
195
  ignore_args=ignore_args,
192
196
  ignore_kwargs=ignore_kwargs,
193
197
  check_class_constructor=check_class_constructor,
198
+ no_ansi=no_ansi,
194
199
  verbose=verbose,
195
200
  )
196
201
  display = _Display(no_ansi)
@@ -225,4 +230,4 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
225
230
  else:
226
231
  display.report()
227
232
 
228
- return int(bool(display))
233
+ return max(int(bool(display)), modules.retcode)
@@ -0,0 +1,20 @@
1
+ """
2
+ docsig._hooks
3
+ =============
4
+ """
5
+
6
+ import sys as _sys
7
+ from os import environ as _e
8
+
9
+ from ._utils import pretty_print_error
10
+
11
+
12
+ def excepthook(no_ansi: bool = False) -> None:
13
+ """Print user friendly commandline error if debug not enabled.
14
+
15
+ :param no_ansi: Disable ANSI output.
16
+ """
17
+ if _e.get("DOCSIG_DEBUG", None) != "1":
18
+ _sys.excepthook = lambda x, y, _: pretty_print_error(
19
+ x, str(y), no_ansi
20
+ )
@@ -9,7 +9,7 @@ from __future__ import annotations as _
9
9
 
10
10
  from ._config import Parser as _Parser
11
11
  from ._core import docsig as _docsig
12
- from ._hooks import pretty_print_error as _pretty_print_error
12
+ from ._hooks import excepthook as _excepthook
13
13
 
14
14
 
15
15
  def main() -> str | int:
@@ -20,7 +20,7 @@ def main() -> str | int:
20
20
  :return: Exit status for whether test failed or not.
21
21
  """
22
22
  p = _Parser()
23
- _pretty_print_error(p.args.no_ansi)
23
+ _excepthook(p.args.no_ansi)
24
24
  return _docsig(
25
25
  *p.args.path,
26
26
  string=p.args.string,
@@ -33,6 +33,7 @@ def main() -> str | int:
33
33
  check_overridden=p.args.check_overridden,
34
34
  check_protected=p.args.check_protected,
35
35
  check_property_returns=p.args.check_property_returns,
36
+ include_ignored=p.args.include_ignored,
36
37
  ignore_no_params=p.args.ignore_no_params,
37
38
  ignore_args=p.args.ignore_args,
38
39
  ignore_kwargs=p.args.ignore_kwargs,
@@ -5,23 +5,73 @@ docsig._module
5
5
 
6
6
  from __future__ import annotations as _
7
7
 
8
+ import os as _os
8
9
  import re as _re
10
+ import sys as _sys
9
11
  import typing as _t
10
12
  from pathlib import Path as _Path
11
13
 
12
14
  import astroid as _ast
15
+ import click as _click
13
16
  from astroid import AstroidSyntaxError as _AstroidSyntaxError
17
+ from pathspec import PathSpec as _PathSpec
18
+ from pathspec.patterns import GitWildMatchPattern as _GitWildMatchPattern
14
19
 
15
20
  from ._directives import Directive as _Directive
16
21
  from ._directives import Directives as _Directives
17
22
  from ._message import Message as _Message
18
23
  from ._stub import Docstring as _Docstring
19
24
  from ._stub import Signature as _Signature
25
+ from ._utils import pretty_print_error
20
26
  from ._utils import vprint as _vprint
21
27
 
22
28
  _FILE_INFO = "{path}: {msg}"
23
29
 
24
30
 
31
+ class _Gitignore(_PathSpec):
32
+ def _get_repo_relative_to(self, path: _Path) -> _Path | None:
33
+ if (path / ".git" / "HEAD").is_file():
34
+ return path
35
+
36
+ if str(path) == _os.path.abspath(_os.sep):
37
+ return None
38
+
39
+ return self._get_repo_relative_to(path.parent)
40
+
41
+ def __init__(self) -> None:
42
+ patterns = []
43
+ repo = self._get_repo_relative_to(_Path.cwd())
44
+ # only consider gitignore patterns valid if inside a git repo
45
+ # there might be stray gitignore files lying about
46
+ if repo is not None:
47
+ # add patterns from all gitignore files
48
+ # adjust patterns to account for their relative paths
49
+ for file in repo.rglob(".gitignore"):
50
+ for pattern in file.read_text(encoding="utf-8").splitlines():
51
+ if pattern.startswith("#"):
52
+ continue
53
+
54
+ # if pattern starts with "/" then os.path.join will
55
+ # consider it in the filesystem root, and it will
56
+ # only ever return /pattern
57
+ if pattern.startswith("/"):
58
+ pattern = pattern[1:]
59
+
60
+ # use os.path.dirname, so it joins without a leading
61
+ # "./", like it does with pathlib parent
62
+ # use os.path.join so trailing slash is preserved
63
+ # replace sep with "/" as, even on windows,
64
+ # gitignore patterns only ever use "/"
65
+ patterns.append(
66
+ _os.path.join(
67
+ _os.path.dirname(file.relative_to(repo)),
68
+ pattern.strip(),
69
+ ).replace(_os.sep, "/")
70
+ )
71
+
72
+ super().__init__(map(_GitWildMatchPattern, patterns))
73
+
74
+
25
75
  class Parent(_t.List["Parent"]):
26
76
  """Represents an object that contains functions or methods.
27
77
 
@@ -304,7 +354,7 @@ class Function(Parent):
304
354
  return self._directives
305
355
 
306
356
 
307
- class Modules(_t.List[Parent]):
357
+ class Modules(_t.List[Parent]): # pylint: disable=too-many-instance-attributes
308
358
  """Sequence of ``Module`` objects parsed from Python modules or str.
309
359
 
310
360
  Recursively collect Python files from within all dirs that exist
@@ -317,11 +367,14 @@ class Modules(_t.List[Parent]):
317
367
  :param excludes: List pf regular expression of files and dirs to
318
368
  exclude from checks.
319
369
  :param string: String to parse if provided.
370
+ :param include_ignored: Check files even if they match a gitignore
371
+ pattern.
320
372
  :param ignore_args: Ignore args prefixed with an asterisk.
321
373
  :param ignore_kwargs: Ignore kwargs prefixed with two asterisks.
322
374
  :param check_class_constructor: Check the class constructor's
323
375
  docstring. Otherwise, expect the constructor's documentation to
324
376
  be on the class level docstring.
377
+ :param no_ansi: Disable ANSI output.
325
378
  :param verbose: increase output verbosity.
326
379
  """
327
380
 
@@ -352,22 +405,24 @@ class Modules(_t.List[Parent]):
352
405
  check_class_constructor,
353
406
  )
354
407
  )
408
+ _vprint(
409
+ _FILE_INFO.format(
410
+ path=root or "stdin", msg="Parsing Python code successful"
411
+ ),
412
+ self._verbose,
413
+ )
355
414
  except (_AstroidSyntaxError, UnicodeDecodeError) as err:
415
+ msg = str(err).replace("\n", " ")
356
416
  if root is not None and root.name.endswith(".py"):
357
- # keep raising errors for .py files as was done prior to
358
- # this change
359
417
  # pass by silently for files that do not end with .py,
360
- # which were not checked at all prior (these may result
361
- # in a 123 syntax error exit status in the future)
362
- # with this there should be no breaking change, and
363
- # files that are supposed to be python, files evident by
364
- # their suffix, will continue to fail
365
- raise err
418
+ # may result in a 123 syntax error exit status in the
419
+ # future
420
+ _click.echo(root, file=_sys.stderr)
421
+ pretty_print_error(type(err), msg, no_ansi=self._no_ansi)
422
+ self._retcode = 1
366
423
 
367
424
  _vprint(
368
- _FILE_INFO.format(
369
- path=root or "stdin", msg=str(err).replace("\n", " ")
370
- ),
425
+ _FILE_INFO.format(path=root or "stdin", msg=msg),
371
426
  self._verbose,
372
427
  )
373
428
 
@@ -377,18 +432,24 @@ class Modules(_t.List[Parent]):
377
432
  disable: list[_Message],
378
433
  excludes: list[str],
379
434
  string: str | None = None,
435
+ include_ignored: bool = False,
380
436
  ignore_args: bool = False,
381
437
  ignore_kwargs: bool = False,
382
438
  check_class_constructor: bool = False,
439
+ no_ansi: bool = False,
383
440
  verbose: bool = False,
384
441
  ) -> None:
385
442
  super().__init__()
386
443
  self._disable = disable
387
444
  self._excludes = excludes
445
+ self._include_ignored = include_ignored
388
446
  self._ignore_args = ignore_args
389
447
  self._ignore_kwargs = ignore_kwargs
390
448
  self.check_class_constructor = check_class_constructor
449
+ self._no_ansi = no_ansi
391
450
  self._verbose = verbose
451
+ self._retcode = 0
452
+ self._gitignore = _Gitignore()
392
453
  if string is not None:
393
454
  self._add_module(
394
455
  disable,
@@ -414,6 +475,13 @@ class Modules(_t.List[Parent]):
414
475
  )
415
476
  return
416
477
 
478
+ if not self._include_ignored and self._gitignore.match_file(root):
479
+ _vprint(
480
+ _FILE_INFO.format(path=root, msg="in gitignore, skipping"),
481
+ self._verbose,
482
+ )
483
+ return
484
+
417
485
  if root.is_file():
418
486
  self._add_module(
419
487
  self._disable,
@@ -426,3 +494,8 @@ class Modules(_t.List[Parent]):
426
494
  if root.is_dir():
427
495
  for path in root.iterdir():
428
496
  self._populate(path)
497
+
498
+ @property
499
+ def retcode(self) -> int:
500
+ """Return code to propagate to main."""
501
+ return self._retcode
@@ -5,6 +5,8 @@ docsig._utils
5
5
 
6
6
  from __future__ import annotations as _
7
7
 
8
+ import sys as _sys
9
+ import typing as _t
8
10
  from difflib import SequenceMatcher as _SequenceMatcher
9
11
 
10
12
  import click as _click
@@ -31,3 +33,19 @@ def vprint(msg: str, verbose: bool = False) -> None:
31
33
  """
32
34
  if verbose:
33
35
  _click.echo(msg)
36
+
37
+
38
+ def pretty_print_error(
39
+ exception_type: _t.Type[BaseException], msg: str, no_ansi: bool
40
+ ) -> None:
41
+ """Print user-friendly exception.
42
+
43
+ :param exception_type: Type of the exception.
44
+ :param msg: The exception message.
45
+ :param no_ansi: Whether to in ANSI escape codes.
46
+ """
47
+ _click.echo(
48
+ f"{_click.style(exception_type.__name__, fg='red', bold=True)}: {msg}",
49
+ file=_sys.stderr,
50
+ color=not no_ansi and _sys.stderr.isatty(),
51
+ )
@@ -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.49.2"
11
+ __version__ = "0.51.0"
@@ -16,16 +16,6 @@ exclude = '''
16
16
  '''
17
17
  line-length = 79
18
18
 
19
- [tool.constcheck]
20
- ignore_strings = [
21
- "disable",
22
- "red",
23
- "store",
24
- "store_true",
25
- "targets",
26
- "utf-8"
27
- ]
28
-
29
19
  [tool.coverage.report]
30
20
  exclude_lines = [
31
21
  "@_t.overload"
@@ -73,7 +63,7 @@ maintainers = [
73
63
  name = "docsig"
74
64
  readme = "README.rst"
75
65
  repository = "https://github.com/jshwi/docsig"
76
- version = "0.49.2"
66
+ version = "0.51.0"
77
67
 
78
68
  [tool.poetry.dependencies]
79
69
  Pygments = "^2.13.0"
@@ -81,6 +71,7 @@ Sphinx = "^7.0.0"
81
71
  arcon = ">=0.4.0"
82
72
  astroid = "^3.0.1"
83
73
  click = "^8.1.7"
74
+ pathspec = "^0.12.1"
84
75
  python = "^3.8"
85
76
 
86
77
  [tool.poetry.dev-dependencies]
@@ -104,7 +95,6 @@ docsig = "docsig.__main__:main"
104
95
  audit = [
105
96
  "about-tests",
106
97
  "commit-policy",
107
- "const",
108
98
  "copyright-year",
109
99
  "docs",
110
100
  "files",
@@ -1,22 +0,0 @@
1
- """
2
- docsig._hooks
3
- =============
4
- """
5
-
6
- import sys as _sys
7
- from os import environ as _e
8
-
9
- import click as _click
10
-
11
-
12
- def pretty_print_error(no_ansi: bool = False) -> None:
13
- """Print user friendly commandline error if debug not enabled.
14
-
15
- :param no_ansi: Disable ANSI output.
16
- """
17
- if _e.get("DOCSIG_DEBUG", None) != "1":
18
- _sys.excepthook = lambda x, y, _: _click.echo(
19
- f"{_click.style(x.__name__, fg='red', bold=True)}: {y}",
20
- file=_sys.stderr,
21
- color=not no_ansi and _sys.stderr.isatty(),
22
- )
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