docsig 0.52.0__tar.gz → 0.53.1__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.52.0
3
+ Version: 0.53.1
4
4
  Summary: Check signature params for proper documentation
5
5
  Home-page: https://pypi.org/project/docsig/
6
6
  License: MIT
@@ -17,7 +17,6 @@ Classifier: Programming Language :: Python :: 3.9
17
17
  Classifier: Programming Language :: Python :: 3.10
18
18
  Classifier: Programming Language :: Python :: 3.11
19
19
  Classifier: Programming Language :: Python :: 3.12
20
- Requires-Dist: Pygments (>=2.13.0,<3.0.0)
21
20
  Requires-Dist: Sphinx (>=7.0.0,<8.0.0)
22
21
  Requires-Dist: arcon (>=0.4.0)
23
22
  Requires-Dist: astroid (>=3.0.1,<4.0.0)
@@ -108,10 +107,9 @@ Commandline
108
107
 
109
108
  .. code-block:: console
110
109
 
111
- usage: docsig [-h] [-V] [-l] [-c | -C] [-D] [-m] [-N] [-o] [-p] [-P] [-i] [-a]
112
- [-k] [-I] [-n] [-S] [-v] [-s STR] [-d LIST] [-t LIST]
113
- [-e PATTERN]
114
- [path [path ...]]
110
+ usage: docsig [-h] [-V] [-l] [-c | -C] [-D] [-m] [-N] [-o] [-p] [-P] [-i] [-a] [-k] [-T]
111
+ [-I] [-n] [-v] [-s STR] [-d LIST] [-t LIST] [-e PATTERN]
112
+ [path [path ...]]
115
113
 
116
114
  Check signature params for proper documentation
117
115
 
@@ -123,11 +121,10 @@ Commandline
123
121
  -V, --version show program's version number and exit
124
122
  -l, --list-checks display a list of all checks and their messages
125
123
  -c, --check-class check class docstrings
126
- -C, --check-class-constructor check __init__ methods. Note: mutually
127
- incompatible with -c
124
+ -C, --check-class-constructor check __init__ methods. Note: mutually incompatible
125
+ with -c
128
126
  -D, --check-dunders check dunder methods
129
- -m, --check-protected-class-methods check public methods belonging to protected
130
- classes
127
+ -m, --check-protected-class-methods check public methods belonging to protected classes
131
128
  -N, --check-nested check nested functions and classes
132
129
  -o, --check-overridden check overridden methods
133
130
  -p, --check-protected check protected functions and classes
@@ -136,28 +133,24 @@ Commandline
136
133
  documented
137
134
  -a, --ignore-args ignore args prefixed with an asterisk
138
135
  -k, --ignore-kwargs ignore kwargs prefixed with two asterisks
139
- -I, --include-ignored check files even if they match a gitignore
140
- pattern
136
+ -T, --ignore-typechecker ignore checking return values
137
+ -I, --include-ignored check files even if they match a gitignore pattern
141
138
  -n, --no-ansi disable ansi output
142
- -S, --summary print a summarised report
143
139
  -v, --verbose increase output verbosity
144
140
  -s STR, --string STR string to parse instead of files
145
141
  -d LIST, --disable LIST comma separated list of rules to disable
146
142
  -t LIST, --target LIST comma separated list of rules to target
147
- -e PATTERN, --exclude PATTERN regular expression of files or dirs to exclude
148
- from checks
143
+ -e PATTERN, --exclude PATTERN regular expression of files or dirs to exclude from
144
+ checks
149
145
 
150
146
  Options can also be configured with the pyproject.toml file
151
147
 
152
- If you find the output is too verbose then the report can be configured to display a summary
153
-
154
148
  .. code-block:: toml
155
149
 
156
150
  [tool.docsig]
157
151
  check-dunders = false
158
152
  check-overridden = false
159
153
  check-protected = false
160
- summary = true
161
154
  disable = [
162
155
  "E101",
163
156
  "E102",
@@ -187,7 +180,7 @@ API
187
180
  ... :param param3: About param3.
188
181
  ... '''
189
182
  ... """
190
- >>> docsig(string=string, summary=True, no_ansi=True)
183
+ >>> docsig(string=string, no_ansi=True)
191
184
  0
192
185
 
193
186
  .. code-block:: python
@@ -201,7 +194,7 @@ API
201
194
  ... :param param3: About param3.
202
195
  ... '''
203
196
  ... """
204
- >>> docsig(string=string, summary=True, no_ansi=True)
197
+ >>> docsig(string=string, no_ansi=True)
205
198
  2 in function
206
199
  E102: includes parameters that do not exist (params-do-not-exist)
207
200
  1
@@ -229,7 +222,7 @@ It can be added to your .pre-commit-config.yaml as follows:
229
222
 
230
223
  repos:
231
224
  - repo: https://github.com/jshwi/docsig
232
- rev: v0.52.0
225
+ rev: v0.53.1
233
226
  hooks:
234
227
  - id: docsig
235
228
  args:
@@ -237,5 +230,4 @@ It can be added to your .pre-commit-config.yaml as follows:
237
230
  - "--check-dunders"
238
231
  - "--check-overridden"
239
232
  - "--check-protected"
240
- - "--summary"
241
233
 
@@ -79,10 +79,9 @@ Commandline
79
79
 
80
80
  .. code-block:: console
81
81
 
82
- usage: docsig [-h] [-V] [-l] [-c | -C] [-D] [-m] [-N] [-o] [-p] [-P] [-i] [-a]
83
- [-k] [-I] [-n] [-S] [-v] [-s STR] [-d LIST] [-t LIST]
84
- [-e PATTERN]
85
- [path [path ...]]
82
+ usage: docsig [-h] [-V] [-l] [-c | -C] [-D] [-m] [-N] [-o] [-p] [-P] [-i] [-a] [-k] [-T]
83
+ [-I] [-n] [-v] [-s STR] [-d LIST] [-t LIST] [-e PATTERN]
84
+ [path [path ...]]
86
85
 
87
86
  Check signature params for proper documentation
88
87
 
@@ -94,11 +93,10 @@ Commandline
94
93
  -V, --version show program's version number and exit
95
94
  -l, --list-checks display a list of all checks and their messages
96
95
  -c, --check-class check class docstrings
97
- -C, --check-class-constructor check __init__ methods. Note: mutually
98
- incompatible with -c
96
+ -C, --check-class-constructor check __init__ methods. Note: mutually incompatible
97
+ with -c
99
98
  -D, --check-dunders check dunder methods
100
- -m, --check-protected-class-methods check public methods belonging to protected
101
- classes
99
+ -m, --check-protected-class-methods check public methods belonging to protected classes
102
100
  -N, --check-nested check nested functions and classes
103
101
  -o, --check-overridden check overridden methods
104
102
  -p, --check-protected check protected functions and classes
@@ -107,28 +105,24 @@ Commandline
107
105
  documented
108
106
  -a, --ignore-args ignore args prefixed with an asterisk
109
107
  -k, --ignore-kwargs ignore kwargs prefixed with two asterisks
110
- -I, --include-ignored check files even if they match a gitignore
111
- pattern
108
+ -T, --ignore-typechecker ignore checking return values
109
+ -I, --include-ignored check files even if they match a gitignore pattern
112
110
  -n, --no-ansi disable ansi output
113
- -S, --summary print a summarised report
114
111
  -v, --verbose increase output verbosity
115
112
  -s STR, --string STR string to parse instead of files
116
113
  -d LIST, --disable LIST comma separated list of rules to disable
117
114
  -t LIST, --target LIST comma separated list of rules to target
118
- -e PATTERN, --exclude PATTERN regular expression of files or dirs to exclude
119
- from checks
115
+ -e PATTERN, --exclude PATTERN regular expression of files or dirs to exclude from
116
+ checks
120
117
 
121
118
  Options can also be configured with the pyproject.toml file
122
119
 
123
- If you find the output is too verbose then the report can be configured to display a summary
124
-
125
120
  .. code-block:: toml
126
121
 
127
122
  [tool.docsig]
128
123
  check-dunders = false
129
124
  check-overridden = false
130
125
  check-protected = false
131
- summary = true
132
126
  disable = [
133
127
  "E101",
134
128
  "E102",
@@ -158,7 +152,7 @@ API
158
152
  ... :param param3: About param3.
159
153
  ... '''
160
154
  ... """
161
- >>> docsig(string=string, summary=True, no_ansi=True)
155
+ >>> docsig(string=string, no_ansi=True)
162
156
  0
163
157
 
164
158
  .. code-block:: python
@@ -172,7 +166,7 @@ API
172
166
  ... :param param3: About param3.
173
167
  ... '''
174
168
  ... """
175
- >>> docsig(string=string, summary=True, no_ansi=True)
169
+ >>> docsig(string=string, no_ansi=True)
176
170
  2 in function
177
171
  E102: includes parameters that do not exist (params-do-not-exist)
178
172
  1
@@ -200,7 +194,7 @@ It can be added to your .pre-commit-config.yaml as follows:
200
194
 
201
195
  repos:
202
196
  - repo: https://github.com/jshwi/docsig
203
- rev: v0.52.0
197
+ rev: v0.53.1
204
198
  hooks:
205
199
  - id: docsig
206
200
  args:
@@ -208,4 +202,3 @@ It can be added to your .pre-commit-config.yaml as follows:
208
202
  - "--check-dunders"
209
203
  - "--check-overridden"
210
204
  - "--check-protected"
211
- - "--summary"
@@ -3,10 +3,10 @@ docsig._config
3
3
  ==============
4
4
  """
5
5
 
6
+ from argparse import SUPPRESS as _SUPPRESS
6
7
  from argparse import HelpFormatter as _HelpFormatter
7
8
  from pathlib import Path as _Path
8
9
 
9
- import click as _click
10
10
  from arcon import ArgumentParser as _ArgumentParser
11
11
 
12
12
  from ._version import __version__
@@ -18,7 +18,6 @@ class Parser(_ArgumentParser):
18
18
  def __init__(self) -> None:
19
19
  super().__init__(
20
20
  version=__version__,
21
- prog=_click.style(__package__, fg="cyan"),
22
21
  formatter_class=lambda prog: _HelpFormatter(
23
22
  prog, max_help_position=45
24
23
  ),
@@ -109,6 +108,12 @@ class Parser(_ArgumentParser):
109
108
  action="store_true",
110
109
  help="ignore kwargs prefixed with two asterisks",
111
110
  )
111
+ self.add_argument(
112
+ "-T",
113
+ "--ignore-typechecker",
114
+ action="store_true",
115
+ help="ignore checking return values",
116
+ )
112
117
  self.add_argument(
113
118
  "-I",
114
119
  "--include-ignored",
@@ -121,12 +126,6 @@ class Parser(_ArgumentParser):
121
126
  action="store_true",
122
127
  help="disable ansi output",
123
128
  )
124
- self.add_argument(
125
- "-S",
126
- "--summary",
127
- action="store_true",
128
- help="print a summarised report",
129
- )
130
129
  self.add_argument(
131
130
  "-v",
132
131
  "--verbose",
@@ -158,3 +157,11 @@ class Parser(_ArgumentParser):
158
157
  metavar="PATTERN",
159
158
  help="regular expression of files or dirs to exclude from checks",
160
159
  )
160
+
161
+ # deprecated
162
+ self.add_argument(
163
+ "-S",
164
+ "--summary",
165
+ action="store_true",
166
+ help=_SUPPRESS,
167
+ )
@@ -8,38 +8,36 @@ from __future__ import annotations as _
8
8
  from pathlib import Path as _Path
9
9
 
10
10
  from . import _decorators
11
- from ._display import Display as _Display
12
- from ._display import Failure as _Failure
13
- from ._display import Failures as _Failures
14
- from ._display import FuncStr as _FuncStr
15
11
  from ._module import Function as _Function
16
12
  from ._module import Modules as _Modules
17
13
  from ._module import Parent as _Parent
14
+ from ._report import Failure as _Failure
15
+ from ._report import Failures as _Failures
18
16
  from ._report import Report as _Report
19
17
  from ._utils import print_checks as _print_checks
20
18
  from .messages import Messages as _Messages
21
19
 
22
20
  _DEFAULT_EXCLUDES = """\
23
21
  (?x)^(
24
- |\\.?venv
25
- |\\.git
26
- |\\.hg
27
- |\\.idea
28
- |\\.mypy_cache
29
- |\\.nox
30
- |\\.pytest_cache
31
- |\\.svn
32
- |\\.tox
33
- |\\.vscode
34
- |_?build
35
- |__pycache__
36
- |dist
37
- |node_modules
22
+ |\\.?venv
23
+ |\\.git
24
+ |\\.hg
25
+ |\\.idea
26
+ |\\.mypy_cache
27
+ |\\.nox
28
+ |\\.pytest_cache
29
+ |\\.svn
30
+ |\\.tox
31
+ |\\.vscode
32
+ |_?build
33
+ |__pycache__
34
+ |dist
35
+ |node_modules
38
36
  )$
39
37
  """
40
38
 
41
39
 
42
- def _run_check( # pylint: disable=too-many-arguments
40
+ def _run_check( # pylint: disable=too-many-arguments,too-many-locals
43
41
  child: _Parent,
44
42
  parent: _Parent,
45
43
  check_class: bool,
@@ -50,6 +48,7 @@ def _run_check( # pylint: disable=too-many-arguments
50
48
  check_protected: bool,
51
49
  check_property_returns: bool,
52
50
  ignore_no_params: bool,
51
+ ignore_typechecker: bool,
53
52
  no_ansi: bool,
54
53
  targets: _Messages,
55
54
  failures: _Failures,
@@ -67,11 +66,15 @@ def _run_check( # pylint: disable=too-many-arguments
67
66
  and not (child.isdunder and not check_dunders)
68
67
  and not (child.docstring.bare and ignore_no_params)
69
68
  ):
70
- report = _Report(
71
- child, targets, child.messages, check_property_returns
69
+ failure = _Failure(
70
+ child,
71
+ targets,
72
+ child.messages,
73
+ check_property_returns,
74
+ ignore_typechecker,
72
75
  )
73
- if report:
74
- failures.append(_Failure(child, _FuncStr(child), report))
76
+ if failure:
77
+ failures.append(failure)
75
78
 
76
79
  if check_nested:
77
80
  for func in child:
@@ -86,6 +89,7 @@ def _run_check( # pylint: disable=too-many-arguments
86
89
  check_protected,
87
90
  check_property_returns,
88
91
  ignore_no_params,
92
+ ignore_typechecker,
89
93
  no_ansi,
90
94
  targets,
91
95
  failures,
@@ -104,6 +108,7 @@ def _run_check( # pylint: disable=too-many-arguments
104
108
  check_protected,
105
109
  check_property_returns,
106
110
  ignore_no_params,
111
+ ignore_typechecker,
107
112
  no_ansi,
108
113
  targets,
109
114
  failures,
@@ -111,6 +116,7 @@ def _run_check( # pylint: disable=too-many-arguments
111
116
 
112
117
 
113
118
  @_decorators.parse_msgs
119
+ @_decorators.handle_deprecations
114
120
  @_decorators.validate_args
115
121
  def docsig( # pylint: disable=too-many-locals,too-many-arguments
116
122
  *path: str | _Path,
@@ -128,8 +134,8 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
128
134
  ignore_no_params: bool = False,
129
135
  ignore_args: bool = False,
130
136
  ignore_kwargs: bool = False,
137
+ ignore_typechecker: bool = False,
131
138
  no_ansi: bool = False,
132
- summary: bool = False,
133
139
  verbose: bool = False,
134
140
  targets: _Messages | None = None,
135
141
  disable: _Messages | None = None,
@@ -163,8 +169,8 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
163
169
  documented
164
170
  :param ignore_args: Ignore args prefixed with an asterisk.
165
171
  :param ignore_kwargs: Ignore kwargs prefixed with two asterisks.
172
+ :param ignore_typechecker: Ignore checking return values.
166
173
  :param no_ansi: Disable ANSI output.
167
- :param summary: Print a summarised report.
168
174
  :param verbose: increase output verbosity.
169
175
  :param targets: List of errors to target.
170
176
  :param disable: List of errors to disable.
@@ -191,7 +197,7 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
191
197
  no_ansi=no_ansi,
192
198
  verbose=verbose,
193
199
  )
194
- display = _Display(no_ansi)
200
+ report = _Report()
195
201
  for module in modules:
196
202
  for top_level in module:
197
203
  if (
@@ -211,16 +217,13 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
211
217
  check_protected,
212
218
  check_property_returns,
213
219
  ignore_no_params,
220
+ ignore_typechecker,
214
221
  no_ansi,
215
222
  targets or _Messages(),
216
223
  failures,
217
224
  )
218
225
  if failures:
219
- display[top_level.path].append(failures)
220
-
221
- if summary:
222
- display.summarise()
223
- else:
224
- display.report()
226
+ report[top_level.path].append(failures)
225
227
 
226
- return max(int(bool(display)), modules.retcode)
228
+ report.print(no_ansi)
229
+ return max(int(bool(report)), modules.retcode)
@@ -9,6 +9,7 @@ import functools as _functools
9
9
  import sys as _sys
10
10
  import typing as _t
11
11
  from pathlib import Path as _Path
12
+ from warnings import warn as _warn
12
13
 
13
14
  from .messages import E as _E
14
15
 
@@ -34,6 +35,28 @@ def parse_msgs(func: _WrappedFuncType) -> _WrappedFuncType:
34
35
  return _wrapper
35
36
 
36
37
 
38
+ def handle_deprecations(func: _WrappedFuncType) -> _WrappedFuncType:
39
+ """Allow, but warn, of deprecated arguments.
40
+
41
+ :param func: Function to wrap.
42
+ :return: Wrapped function.
43
+ """
44
+
45
+ @_functools.wraps(func)
46
+ def _wrapper(*args: str | _Path, **kwargs: _t.Any) -> str | int:
47
+ if kwargs.pop("summary", None):
48
+ _warn(
49
+ "summary is deprecated and will be removed in a future"
50
+ " version",
51
+ category=DeprecationWarning,
52
+ stacklevel=4,
53
+ )
54
+
55
+ return func(*args, **kwargs)
56
+
57
+ return _wrapper
58
+
59
+
37
60
  def validate_args(func: _FuncType) -> _WrappedFuncType:
38
61
  """Confirm args passed to function are valid.
39
62
 
@@ -37,8 +37,9 @@ def main() -> str | int:
37
37
  ignore_no_params=p.args.ignore_no_params,
38
38
  ignore_args=p.args.ignore_args,
39
39
  ignore_kwargs=p.args.ignore_kwargs,
40
+ ignore_typechecker=p.args.ignore_typechecker,
40
41
  no_ansi=p.args.no_ansi,
41
- summary=p.args.summary,
42
+ summary=p.args.summary, # deprecated
42
43
  verbose=p.args.verbose,
43
44
  targets=p.args.target,
44
45
  disable=p.args.disable,
@@ -20,6 +20,7 @@ from pathspec.patterns import GitWildMatchPattern as _GitWildMatchPattern
20
20
  from ._directives import Comment as _Comment
21
21
  from ._directives import Directives as _Directives
22
22
  from ._stub import Docstring as _Docstring
23
+ from ._stub import RetType as _RetType
23
24
  from ._stub import Signature as _Signature
24
25
  from ._utils import pretty_print_error as _pretty_print_error
25
26
  from ._utils import vprint as _vprint
@@ -151,7 +152,12 @@ class Parent(_t.List["Parent"]):
151
152
  self._imports,
152
153
  )
153
154
  if func.isoverloaded:
154
- self._overloads[func.name] = func
155
+ if (
156
+ func.name not in self._overloads
157
+ or self._overloads[func.name].signature.rettype
158
+ == _RetType.NONE
159
+ ):
160
+ self._overloads[func.name] = func
155
161
  else:
156
162
  if func.name in self._overloads:
157
163
  func.overload(
@@ -356,7 +362,7 @@ class Function(Parent):
356
362
  """Comments, if any, belonging to this function."""
357
363
  return self._comments
358
364
 
359
- def overload(self, rettype: str | None) -> None:
365
+ def overload(self, rettype: _RetType) -> None:
360
366
  """Overload function with new signature return type.
361
367
 
362
368
  :param rettype: Return type of overloaded signature.
@@ -5,11 +5,14 @@ docsig._report
5
5
 
6
6
  from __future__ import annotations as _
7
7
 
8
+ import sys as _sys
8
9
  import typing as _t
9
10
 
11
+ import click as _click
12
+
10
13
  from ._module import Function as _Function
11
- from ._stub import RETURN as _RETURN
12
14
  from ._stub import Param as _Param
15
+ from ._stub import RetType as _RetType
13
16
  from ._utils import almost_equal as _almost_equal
14
17
  from .messages import TEMPLATE as _TEMPLATE
15
18
  from .messages import E as _E
@@ -20,21 +23,23 @@ _MIN_MATCH = 0.8
20
23
  _MAX_MATCH = 1.0
21
24
 
22
25
 
23
- class Report(_t.List[str]):
26
+ class Failure(_t.List[str]):
24
27
  """Compile and produce report.
25
28
 
26
29
  :param func: Function object.
27
30
  :param targets: List of errors to target.
28
31
  :param disable: List of errors to disable.
29
32
  :param check_property_returns: Run return checks on properties.
33
+ :param ignore_typechecker: Ignore checking return values.
30
34
  """
31
35
 
32
- def __init__(
36
+ def __init__( # pylint: disable=too-many-arguments
33
37
  self,
34
38
  func: _Function,
35
39
  targets: _Messages,
36
40
  disable: _Messages,
37
41
  check_property_returns: bool,
42
+ ignore_typechecker: bool,
38
43
  ) -> None:
39
44
  super().__init__()
40
45
  self._disable = list(disable)
@@ -47,8 +52,14 @@ class Report(_t.List[str]):
47
52
 
48
53
  self._errors = _Messages()
49
54
  self._func = func
50
- self._no_prop_return = func.isproperty and not check_property_returns
51
- self._no_returns = func.isinit or self._no_prop_return
55
+ self._no_prop_return = (
56
+ func.isproperty
57
+ and not check_property_returns
58
+ and not ignore_typechecker
59
+ )
60
+ self._no_returns = (
61
+ func.isinit or self._no_prop_return or ignore_typechecker
62
+ )
52
63
  self._invalid_directive()
53
64
  self._invalid_directive_options()
54
65
  self._missing_class_docstring()
@@ -116,7 +127,7 @@ class Report(_t.List[str]):
116
127
  def _extra_return(self) -> None:
117
128
  if (
118
129
  self._func.docstring.returns
119
- and self._func.signature.rettype == "None"
130
+ and self._func.signature.rettype == _RetType.NONE
120
131
  and not self._no_returns
121
132
  ):
122
133
  self._add(_E[104])
@@ -126,8 +137,11 @@ class Report(_t.List[str]):
126
137
  self._add(_E[108], hint=True)
127
138
 
128
139
  def _return_not_typed(self) -> None:
129
- if self._func.signature.rettype is None and not self._no_returns:
130
- self._add(_E[109])
140
+ if (
141
+ self._func.signature.rettype == _RetType.UNTYPED
142
+ and not self._no_returns
143
+ ):
144
+ self._add(_E[109], hint=True)
131
145
 
132
146
  def _missing_return(self) -> None:
133
147
  hint = False
@@ -137,7 +151,7 @@ class Report(_t.List[str]):
137
151
  and not self._no_returns
138
152
  ):
139
153
  docstring = self._func.docstring.string
140
- if docstring is not None and _RETURN in docstring:
154
+ if docstring is not None and "return" in docstring:
141
155
  hint = True
142
156
 
143
157
  self._add(_E[105], hint=hint)
@@ -188,11 +202,43 @@ class Report(_t.List[str]):
188
202
  option=rule.description,
189
203
  )
190
204
 
191
- def get_report(self, prefix: str = "") -> str:
192
- """Get report compiled as a string.
205
+ @property
206
+ def func(self) -> _Function:
207
+ """Function this failure belongs to."""
208
+ return self._func
209
+
210
+
211
+ class Failures(_t.List[Failure]):
212
+ """Sequence of failed functions."""
193
213
 
194
- :param prefix: Prefix report.
195
- :return: Current report.
214
+
215
+ class Report(_t.Dict[str, _t.List[Failures]]):
216
+ """Collect and display report."""
217
+
218
+ def __getitem__(self, key: str) -> list[Failures]:
219
+ if key not in super().__iter__():
220
+ super().__setitem__(key, [])
221
+
222
+ return super().__getitem__(key)
223
+
224
+ def print(self, no_ansi: bool) -> None:
225
+ """Display report summary if any checks have failed.
226
+
227
+ :param no_ansi: Disable ANSI output.
196
228
  """
197
- report = f"\n{prefix}".join(self)
198
- return f"{report}\n"
229
+ for key, value in self.items():
230
+ for failures in value:
231
+ for failure in failures:
232
+ header = f"{key}{failure.func.lineno}"
233
+ function = failure.func.name
234
+ if failure.func.parent.name:
235
+ function = f"{failure.func.parent.name}.{function}"
236
+
237
+ header += f" in {function}"
238
+ _click.echo(
239
+ "{}\n {}".format(
240
+ _click.style(header, fg="magenta"),
241
+ "\n ".join(failure),
242
+ ),
243
+ color=not no_ansi and _sys.stdout.isatty(),
244
+ )
@@ -9,16 +9,11 @@ import re as _re
9
9
  import textwrap as _textwrap
10
10
  import typing as _t
11
11
  from collections import Counter as _Counter
12
+ from enum import Enum as _Enum
12
13
 
13
14
  import astroid as _ast
14
15
  import sphinx.ext.napoleon as _s
15
16
 
16
- PARAM = "param"
17
- KEYWORD = "keyword"
18
- KEY = "key"
19
- RETURN = "return"
20
- ARG = "arg"
21
-
22
17
 
23
18
  # noinspection PyTypeChecker
24
19
  class _GoogleDocstring(str):
@@ -51,10 +46,66 @@ class _RawDocstring(str):
51
46
  )
52
47
 
53
48
 
49
+ class RetType(_Enum):
50
+ """Defines the possible types of a return annotation."""
51
+
52
+ NONE = 1
53
+ SOME = 2
54
+ UNTYPED = 3
55
+
56
+ @classmethod
57
+ def from_ast(cls, returns: _ast.NodeNG | None) -> RetType:
58
+ """Construct a return type object from an AST node.
59
+
60
+ :param returns: Ast node or None.
61
+ :return: Constructed return type.
62
+ """
63
+ if isinstance(returns, _ast.Const) and returns.value is None:
64
+ return cls.NONE
65
+
66
+ if isinstance(
67
+ returns,
68
+ (
69
+ _ast.Const,
70
+ _ast.Name,
71
+ _ast.Attribute,
72
+ _ast.Subscript,
73
+ _ast.BinOp,
74
+ ),
75
+ ):
76
+ return cls.SOME
77
+
78
+ return cls.UNTYPED
79
+
80
+
81
+ class DocType(_Enum):
82
+ """Defines the possible types of a docstring."""
83
+
84
+ PARAM = 1
85
+ ARG = 2
86
+ KWARG = 3
87
+ UNKNOWN = 4
88
+
89
+ @classmethod
90
+ def from_str(cls, docstring: str) -> DocType:
91
+ """Construct a doc type object from a docstring.
92
+
93
+ :param docstring: Docstring string.
94
+ :return: Constructed doc type.
95
+ """
96
+ try:
97
+ return cls[docstring.upper()]
98
+ except KeyError:
99
+ if docstring in ("key", "keyword"):
100
+ return cls.KWARG
101
+
102
+ return cls.UNKNOWN
103
+
104
+
54
105
  class Param(_t.NamedTuple):
55
106
  """A tuple of param types and their names."""
56
107
 
57
- kind: str = PARAM
108
+ kind: DocType = DocType.PARAM
58
109
  name: str | None = None
59
110
  description: str | None = None
60
111
  indent: int = 0
@@ -64,7 +115,7 @@ class Param(_t.NamedTuple):
64
115
  return False
65
116
 
66
117
  args = self, other
67
- return all(i.kind == KEY for i in args) or (
118
+ return all(i.kind == DocType.KWARG for i in args) or (
68
119
  self.name == other.name and all(i.name is not None for i in args)
69
120
  )
70
121
 
@@ -79,7 +130,6 @@ class Param(_t.NamedTuple):
79
130
 
80
131
  class _Matches(_t.List[Param]):
81
132
  _pattern = _re.compile(":(.*?):")
82
- _normalize = {KEYWORD: KEY}
83
133
 
84
134
  def __init__(self, string: str) -> None:
85
135
  super().__init__()
@@ -90,9 +140,7 @@ class _Matches(_t.List[Param]):
90
140
  name = description = None
91
141
  kinds = match[0].split()
92
142
  if kinds:
93
- kind = kinds[0]
94
- for substring, replace in self._normalize.items():
95
- kind = kind.replace(substring, replace)
143
+ kind = DocType.from_str(kinds[0])
96
144
 
97
145
  if len(kinds) > 1:
98
146
  name = kinds[1]
@@ -121,12 +169,12 @@ class _Params(_t.List[Param]):
121
169
  # pylint: disable=too-many-boolean-expressions
122
170
  def append(self, value: Param) -> None:
123
171
  if not value.isprotected and (
124
- value.kind == PARAM
125
- or (value.kind == ARG and not self._ignore_args)
172
+ value.kind == DocType.PARAM
173
+ or (value.kind == DocType.ARG and not self._ignore_args)
126
174
  or (
127
- value.kind == KEY
175
+ value.kind == DocType.KWARG
128
176
  and not self._ignore_kwargs
129
- and not any(i.kind == KEY for i in self)
177
+ and not any(i.kind == DocType.KWARG for i in self)
130
178
  )
131
179
  ):
132
180
  super().append(value)
@@ -203,45 +251,19 @@ class Signature(_Stub):
203
251
  for a in [
204
252
  *arguments.posonlyargs,
205
253
  *arguments.args,
206
- Param(ARG, name=arguments.vararg),
254
+ Param(DocType.ARG, name=arguments.vararg),
207
255
  *arguments.kwonlyargs,
208
- Param(KEY, name=arguments.kwarg),
256
+ Param(DocType.KWARG, name=arguments.kwarg),
209
257
  ]
210
258
  if a is not None and a.name
211
259
  ]:
212
260
  self.args.append(i)
213
261
 
214
- self._rettype = (
215
- returns if isinstance(returns, str) else self._get_rettype(returns)
216
- )
217
- self._returns = str(self._rettype) != "None"
218
-
219
- def _get_rettype(self, returns: _ast.NodeNG | None) -> str | None:
220
- if isinstance(returns, _ast.Name):
221
- return returns.name
222
-
223
- if isinstance(returns, _ast.Attribute):
224
- return returns.attrname
225
-
226
- if isinstance(returns, _ast.Const):
227
- return str(returns.value)
228
-
229
- if isinstance(returns, _ast.Subscript):
230
- return "{}[{}]".format(
231
- self._get_rettype(returns.value),
232
- self._get_rettype(returns.slice),
233
- )
234
-
235
- if isinstance(returns, _ast.BinOp):
236
- return "{} | {}".format(
237
- self._get_rettype(returns.left),
238
- self._get_rettype(returns.right),
239
- )
240
-
241
- return None
262
+ self._rettype = RetType.from_ast(returns)
263
+ self._returns = self._rettype == RetType.SOME
242
264
 
243
265
  @property
244
- def rettype(self) -> str | None:
266
+ def rettype(self) -> RetType:
245
267
  """Function's return value.
246
268
 
247
269
  If a function is typed to return None, return str(None). If no
@@ -249,15 +271,13 @@ class Signature(_Stub):
249
271
  """
250
272
  return self._rettype
251
273
 
252
- def overload(self, rettype: str | None) -> None:
274
+ def overload(self, rettype: RetType) -> None:
253
275
  """Overload signature with a ret type.
254
276
 
255
277
  :param rettype: Return type of overloaded signature.
256
278
  """
257
- self._rettype = (
258
- rettype if isinstance(rettype, str) else self._get_rettype(rettype)
259
- )
260
- self._returns = str(rettype) != "None"
279
+ self._rettype = rettype
280
+ self._returns = rettype != RetType.NONE
261
281
 
262
282
 
263
283
  class Docstring(_Stub):
@@ -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.52.0"
11
+ __version__ = "0.53.1"
@@ -159,6 +159,7 @@ E = MessageMap(
159
159
  "E109",
160
160
  "cannot determine whether a return statement should exist or not",
161
161
  "confirm-return-needed",
162
+ "annotate type to indicate whether return documentation needed",
162
163
  ),
163
164
  110: Message(
164
165
  "E110",
@@ -16,6 +16,9 @@ exclude = '''
16
16
  '''
17
17
  line-length = 79
18
18
 
19
+ [tool.codespell]
20
+ ignore-words-list = "astroid"
21
+
19
22
  [tool.coverage.report]
20
23
  exclude_lines = [
21
24
  "@_t.overload"
@@ -31,7 +34,6 @@ omit = [
31
34
  [tool.docsig]
32
35
  check-class = true
33
36
  check-protected-class-methods = true
34
- summary = true
35
37
 
36
38
  [tool.isort]
37
39
  ensure_newline_before_comments = true
@@ -63,10 +65,9 @@ maintainers = [
63
65
  name = "docsig"
64
66
  readme = "README.rst"
65
67
  repository = "https://github.com/jshwi/docsig"
66
- version = "0.52.0"
68
+ version = "0.53.1"
67
69
 
68
70
  [tool.poetry.dependencies]
69
- Pygments = "^2.13.0"
70
71
  Sphinx = "^7.0.0"
71
72
  arcon = ">=0.4.0"
72
73
  astroid = "^3.0.1"
@@ -82,11 +83,12 @@ pre-commit = "^3.3.3"
82
83
  pyaud = "^7.5.0"
83
84
  pytest-randomly = "^3.13.0"
84
85
  pytest-sugar = "^1.0.0"
85
- pytest-xdist = "^3.5.0"
86
+ pytest-xdist = "^3.6.1"
86
87
  restview = "^3.0.0"
87
88
  sphinx-immaterial = "^0.11.9"
88
89
  sphinx-toolbox = "^3.5.0"
89
90
  templatest = "^0.10.1"
91
+ tox = "^4.15.0"
90
92
 
91
93
  [tool.poetry.scripts]
92
94
  docsig = "docsig.__main__:main"
@@ -110,6 +112,7 @@ exclude = '''
110
112
  (?x)^(
111
113
  | docs\/conf\.py
112
114
  | whitelist\.py
115
+ | scripts\/update_readme\.py
113
116
  )$
114
117
  '''
115
118
 
@@ -1,232 +0,0 @@
1
- """
2
- docsig._display
3
- ===============
4
- """
5
-
6
- from __future__ import annotations as _
7
-
8
- import sys as _sys
9
- import typing as _t
10
- from collections import UserString as _UserString
11
-
12
- import click as _click
13
- from pygments import highlight as _highlight
14
- from pygments.formatters.terminal256 import (
15
- Terminal256Formatter as _Terminal256Formatter,
16
- )
17
-
18
- # noinspection PyUnresolvedReferences
19
- from pygments.lexers.python import PythonLexer as _PythonLexer
20
-
21
- from ._module import Function as _Function
22
- from ._report import Report as _Report
23
- from ._stub import ARG as _ARG
24
- from ._stub import KEY as _KEY
25
- from ._stub import Param as _Param
26
-
27
- TAB = " "
28
-
29
-
30
- def syntax(obj: _t.Any) -> str:
31
- """Get code representation with syntax highlighting.
32
-
33
- :param obj: Any object, represented as ``__str__``.
34
- :return: Colored string or string as was supplied.
35
- """
36
- return _highlight(
37
- obj, _PythonLexer(), _Terminal256Formatter(style="monokai")
38
- ).strip()
39
-
40
-
41
- class FuncStr(_UserString):
42
- """String representation for function.
43
-
44
- :param func: Represents a function with signature and docstring
45
- parameters.
46
- """
47
-
48
- CHECK = "\u2713"
49
- CROSS = "\u2716"
50
- TRIPLE_QUOTES = '"""'
51
-
52
- def __init__(self, func: _Function) -> None:
53
- super().__init__(func.name)
54
- self._parent_name = func.parent.name
55
- self._isinit = func.isinit
56
- self.data = ""
57
- self._is_string = func.docstring.string is not None
58
- if self._isinit:
59
- self.data += TAB
60
-
61
- self.data += syntax(f"def {func.name}(")
62
- if self._is_string:
63
- self._docstring = syntax(f"{TAB}{self.TRIPLE_QUOTES}")
64
- else:
65
- self._docstring = f"{TAB}{_click.style('...', fg='red')}\n"
66
-
67
- self._mark = _click.style(self.CHECK, fg="green")
68
- for index in range(len(func)):
69
- arg = func.signature.args.get(index)
70
- doc = func.docstring.args.get(index)
71
- self.add_param(arg, doc, arg != doc)
72
- if index + 1 != len(func):
73
- self.add_comma()
74
-
75
- self.set_mark()
76
- if func.docstring.returns and func.signature.returns:
77
- self.add_return()
78
- elif (
79
- func.docstring.returns
80
- and not func.signature.returns
81
- or func.signature.returns
82
- and not func.docstring.returns
83
- ):
84
- self.add_return(failed=True)
85
-
86
- self.close_sig(func.signature.rettype)
87
- self.close_docstring()
88
- self.render()
89
-
90
- def _cat_docstring(self, string: str) -> None:
91
- if self._is_string:
92
- self._docstring += string
93
-
94
- def set_mark(self, failed: bool = False) -> None:
95
- """Set mark to a cross or a check.
96
-
97
- :param failed: Boolean to test that check failed.
98
- """
99
- self._mark = (
100
- _click.style(self.CROSS, fg="red")
101
- if failed
102
- else _click.style(self.CHECK, fg="green")
103
- )
104
-
105
- def add_param(
106
- self, sig: _Param, doc: _Param, failed: bool = False
107
- ) -> None:
108
- """Add parameters to docstring.
109
-
110
- :param sig: Signature argument.
111
- :param doc: Docstring argument.
112
- :param failed: Boolean to test that check failed.
113
- """
114
- self.set_mark(failed)
115
- sig_name = sig.name
116
- if sig.kind == _KEY:
117
- sig_name = f"**{sig_name}"
118
-
119
- if sig.kind == _ARG:
120
- sig_name = f"*{sig_name}"
121
-
122
- self.data += f"{self._mark}{sig_name}"
123
- doc_name = doc.name
124
- if doc.kind == _KEY:
125
- doc_name = "(**)"
126
-
127
- self._cat_docstring(f"\n{TAB}:{doc.kind} {doc_name}: {self._mark}")
128
-
129
- def add_return(self, failed: bool = False) -> None:
130
- """Add return statement to docstring.
131
-
132
- :param failed: Boolean to test that check failed.
133
- """
134
- self.set_mark(failed)
135
- self._cat_docstring(f"\n{TAB}:return: {self._mark}")
136
-
137
- def close_sig(self, arg: str | None) -> None:
138
- """Close function signature.
139
-
140
- :param arg: Signature argument.
141
- """
142
- if arg is not None:
143
- self.data += f"{syntax(') -> ')}{self._mark}{arg}{syntax(':')}"
144
- else:
145
- self.data += "{}{}{}".format(
146
- syntax(")"),
147
- _click.style("?", fg="red"),
148
- syntax(":"),
149
- )
150
-
151
- def add_comma(self) -> None:
152
- """Add comma between parenthesis."""
153
- self.data += syntax(", ")
154
-
155
- def close_docstring(self) -> None:
156
- """Close docstring."""
157
- self._cat_docstring(f"\n{TAB}{syntax(self.TRIPLE_QUOTES)}\n")
158
-
159
- def render(self) -> None:
160
- """Render final string by adding docstring to function."""
161
- if self._isinit:
162
- self.data = (
163
- syntax(f"class {self._parent_name}:")
164
- + f"\n{self._docstring}"
165
- + f"\n{self.data}\n"
166
- )
167
- else:
168
- self.data += f"\n{self._docstring}"
169
-
170
-
171
- class Failure(_t.NamedTuple):
172
- """Failed function data."""
173
-
174
- func: _Function
175
- func_str: FuncStr
176
- report: _Report
177
-
178
-
179
- class Failures(_t.List[Failure]):
180
- """Sequence of failed functions."""
181
-
182
-
183
- class Display(_t.Dict[str, _t.List[Failures]]):
184
- """Collect and display report.
185
-
186
- :param no_ansi: Disable ANSI output.
187
- """
188
-
189
- def __init__(self, no_ansi: bool = False) -> None:
190
- super().__init__()
191
- self._ansi = not no_ansi and _sys.stdout.isatty()
192
-
193
- def __getitem__(self, key: str) -> list[Failures]:
194
- if key not in super().__iter__():
195
- super().__setitem__(key, [])
196
-
197
- return super().__getitem__(key)
198
-
199
- def report(self) -> None:
200
- """Display report if any checks have failed."""
201
- for key, value in self.items():
202
- for failures in value:
203
- for failure in failures:
204
- header = f"{key}{failure.func.lineno}"
205
- if failure.func.parent.name:
206
- header += f" in {failure.func.parent.name}"
207
-
208
- _click.echo(
209
- _click.style(header, fg="magenta"), color=self._ansi
210
- )
211
- _click.echo(len(header) * "-", color=self._ansi)
212
- _click.echo(failure.func_str, color=self._ansi)
213
- _click.echo(failure.report.get_report(), color=self._ansi)
214
-
215
- def summarise(self) -> None:
216
- """Display report summary if any checks have failed."""
217
- for key, value in self.items():
218
- for failures in value:
219
- for failure in failures:
220
- header = f"{key}{failure.func.lineno}"
221
- function = failure.func.name
222
- if failure.func.parent.name:
223
- function = f"{failure.func.parent.name}.{function}"
224
-
225
- header += f" in {function}"
226
- _click.echo(
227
- "{}\n {}".format(
228
- _click.style(header, fg="magenta"),
229
- failure.report.get_report(" ").strip(),
230
- ),
231
- color=self._ansi,
232
- )
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes