docsig 0.80.0__tar.gz → 0.80.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.4
2
2
  Name: docsig
3
- Version: 0.80.0
3
+ Version: 0.80.1
4
4
  Summary: Check signature params for proper documentation
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -116,7 +116,7 @@ Commandline
116
116
  [--check-property-returns] [--check-protected]
117
117
  [--check-protected-class-methods] [--ignore-args] [--ignore-kwargs]
118
118
  [--ignore-no-params] [-d LIST] [-t LIST] [-e PATTERN] [-E PATH [PATH ...]]
119
- [-i] [-s STR]
119
+ [-I] [-s STR]
120
120
  [path [path ...]]
121
121
 
122
122
  Check signature params for proper documentation
@@ -152,7 +152,7 @@ Commandline
152
152
  regular expression of files or dirs to exclude from checks
153
153
  -E PATH [PATH ...], --excludes PATH [PATH ...]
154
154
  path glob patterns to exclude from checks
155
- -i, --include-ignored
155
+ -I, --include-ignored
156
156
  check files even if they match a gitignore pattern
157
157
  -s STR, --string STR string to parse instead of files
158
158
 
@@ -184,7 +184,7 @@ ensure your installation has registered `docsig`
184
184
  .. code-block:: console
185
185
 
186
186
  $ flake8 --version
187
- 7.3.0 (docsig: 0.80.0, mccabe: 0.7.0, pycodestyle: 2.14.0, pyflakes: 3.4.0) CPython 3.10.19 on Darwin
187
+ 7.3.0 (docsig: 0.80.1, mccabe: 0.7.0, pycodestyle: 2.14.0, pyflakes: 3.4.0) CPython 3.10.19 on Darwin
188
188
 
189
189
  And now use `flake8` to lint your files
190
190
 
@@ -270,7 +270,7 @@ Standalone
270
270
 
271
271
  repos:
272
272
  - repo: https://github.com/jshwi/docsig
273
- rev: v0.80.0
273
+ rev: v0.80.1
274
274
  hooks:
275
275
  - id: docsig
276
276
  args:
@@ -289,7 +289,7 @@ or integrated with ``flake8``
289
289
  hooks:
290
290
  - id: flake8
291
291
  additional_dependencies:
292
- - docsig==0.80.0
292
+ - docsig==0.80.1
293
293
  args:
294
294
  - "--sig-check-class"
295
295
  - "--sig-check-dunders"
@@ -87,7 +87,7 @@ Commandline
87
87
  [--check-property-returns] [--check-protected]
88
88
  [--check-protected-class-methods] [--ignore-args] [--ignore-kwargs]
89
89
  [--ignore-no-params] [-d LIST] [-t LIST] [-e PATTERN] [-E PATH [PATH ...]]
90
- [-i] [-s STR]
90
+ [-I] [-s STR]
91
91
  [path [path ...]]
92
92
 
93
93
  Check signature params for proper documentation
@@ -123,7 +123,7 @@ Commandline
123
123
  regular expression of files or dirs to exclude from checks
124
124
  -E PATH [PATH ...], --excludes PATH [PATH ...]
125
125
  path glob patterns to exclude from checks
126
- -i, --include-ignored
126
+ -I, --include-ignored
127
127
  check files even if they match a gitignore pattern
128
128
  -s STR, --string STR string to parse instead of files
129
129
 
@@ -155,7 +155,7 @@ ensure your installation has registered `docsig`
155
155
  .. code-block:: console
156
156
 
157
157
  $ flake8 --version
158
- 7.3.0 (docsig: 0.80.0, mccabe: 0.7.0, pycodestyle: 2.14.0, pyflakes: 3.4.0) CPython 3.10.19 on Darwin
158
+ 7.3.0 (docsig: 0.80.1, mccabe: 0.7.0, pycodestyle: 2.14.0, pyflakes: 3.4.0) CPython 3.10.19 on Darwin
159
159
 
160
160
  And now use `flake8` to lint your files
161
161
 
@@ -241,7 +241,7 @@ Standalone
241
241
 
242
242
  repos:
243
243
  - repo: https://github.com/jshwi/docsig
244
- rev: v0.80.0
244
+ rev: v0.80.1
245
245
  hooks:
246
246
  - id: docsig
247
247
  args:
@@ -260,7 +260,7 @@ or integrated with ``flake8``
260
260
  hooks:
261
261
  - id: flake8
262
262
  additional_dependencies:
263
- - docsig==0.80.0
263
+ - docsig==0.80.1
264
264
  args:
265
265
  - "--sig-check-class"
266
266
  - "--sig-check-dunders"
@@ -16,7 +16,7 @@ from pathlib import Path as _Path
16
16
  import tomli as _tomli
17
17
 
18
18
  from ._version import __version__
19
- from .messages import Messages as _Messages
19
+ from .messages import Messages
20
20
 
21
21
  PYPROJECT_TOML = "pyproject.toml"
22
22
 
@@ -133,45 +133,101 @@ def parse_args(args: _t.Sequence[str] | None = None) -> _argparse.Namespace:
133
133
  help="increase output verbosity",
134
134
  )
135
135
  group = parser.add_mutually_exclusive_group(required=False)
136
+ group.add_argument(
137
+ "-c",
138
+ action="store_true",
139
+ help=_argparse.SUPPRESS,
140
+ dest="check_class",
141
+ )
136
142
  group.add_argument(
137
143
  "--check-class",
138
144
  action="store_true",
139
145
  help="check class docstrings",
146
+ dest="check_class",
147
+ )
148
+ group.add_argument(
149
+ "-C",
150
+ action="store_true",
151
+ help=_argparse.SUPPRESS,
152
+ dest="check_class_constructor",
140
153
  )
141
154
  group.add_argument(
142
155
  "--check-class-constructor",
143
156
  action="store_true",
144
157
  help="check __init__ methods",
158
+ dest="check_class_constructor",
159
+ )
160
+ parser.add_argument(
161
+ "-D",
162
+ action="store_true",
163
+ help=_argparse.SUPPRESS,
164
+ dest="check_dunders",
145
165
  )
146
166
  parser.add_argument(
147
167
  "--check-dunders",
148
168
  action="store_true",
149
169
  help="check dunder methods",
170
+ dest="check_dunders",
171
+ )
172
+ parser.add_argument(
173
+ "-N",
174
+ action="store_true",
175
+ help=_argparse.SUPPRESS,
176
+ dest="check_nested",
150
177
  )
151
178
  parser.add_argument(
152
179
  "--check-nested",
153
180
  action="store_true",
154
181
  help="check nested functions and classes",
182
+ dest="check_nested",
183
+ )
184
+ parser.add_argument(
185
+ "-o",
186
+ action="store_true",
187
+ help=_argparse.SUPPRESS,
188
+ dest="check_overridden",
155
189
  )
156
190
  parser.add_argument(
157
191
  "--check-overridden",
158
192
  action="store_true",
159
193
  help="check overridden methods",
194
+ dest="check_overridden",
195
+ )
196
+ parser.add_argument(
197
+ "-P",
198
+ action="store_true",
199
+ help=_argparse.SUPPRESS,
200
+ dest="check_property_returns",
160
201
  )
161
202
  parser.add_argument(
162
203
  "--check-property-returns",
163
204
  action="store_true",
164
205
  help="check property return values",
206
+ dest="check_property_returns",
207
+ )
208
+ parser.add_argument(
209
+ "-p",
210
+ action="store_true",
211
+ help=_argparse.SUPPRESS,
212
+ dest="check_protected",
165
213
  )
166
214
  parser.add_argument(
167
215
  "--check-protected",
168
216
  action="store_true",
169
217
  help="check protected functions and classes",
218
+ dest="check_protected",
219
+ )
220
+ parser.add_argument(
221
+ "-m",
222
+ action="store_true",
223
+ help=_argparse.SUPPRESS,
224
+ dest="check_protected_class_methods",
170
225
  )
171
226
  parser.add_argument(
172
227
  "--check-protected-class-methods",
173
228
  action="store_true",
174
229
  help="check public methods belonging to protected classes",
230
+ dest="check_protected_class_methods",
175
231
  )
176
232
  parser.add_argument(
177
233
  "--ignore-args",
@@ -183,10 +239,22 @@ def parse_args(args: _t.Sequence[str] | None = None) -> _argparse.Namespace:
183
239
  action="store_true",
184
240
  help="ignore kwargs prefixed with two asterisks",
185
241
  )
242
+ parser.add_argument(
243
+ "-i",
244
+ action="store_true",
245
+ help=_argparse.SUPPRESS,
246
+ dest="ignore_no_params",
247
+ )
186
248
  parser.add_argument(
187
249
  "--ignore-no-params",
188
250
  action="store_true",
189
251
  help="ignore docstrings where parameters are not documented",
252
+ dest="ignore_no_params",
253
+ )
254
+ parser.add_argument(
255
+ "--ignore-typechecker",
256
+ action="store_true",
257
+ help=_argparse.SUPPRESS,
190
258
  )
191
259
  parser.add_argument(
192
260
  "-d",
@@ -220,7 +288,7 @@ def parse_args(args: _t.Sequence[str] | None = None) -> _argparse.Namespace:
220
288
  help="path glob patterns to exclude from checks",
221
289
  )
222
290
  parser.add_argument(
223
- "-i",
291
+ "-I",
224
292
  "--include-ignored",
225
293
  action="store_true",
226
294
  help="check files even if they match a gitignore pattern",
@@ -287,11 +355,11 @@ class Config:
287
355
  takes a single config object instead of many parameters.
288
356
  """
289
357
 
290
- check: Check
291
- ignore: Ignore
292
- target: _Messages = _field(default_factory=list)
293
- disable: _Messages = _field(default_factory=list)
294
- exclude: str | None = None
358
+ check: Check = _field(default_factory=Check)
359
+ ignore: Ignore = _field(default_factory=Ignore)
360
+ target: Messages = _field(default_factory=Messages)
361
+ disable: Messages = _field(default_factory=Messages)
362
+ exclude: list[str] = _field(default_factory=list)
295
363
  excludes: list[str] | None = None
296
364
  list_checks: bool = False
297
365
  include_ignored: bool = False
@@ -9,6 +9,7 @@ import logging as _logging
9
9
  import os as _os
10
10
  import sys as _sys
11
11
  import typing as _t
12
+ import warnings as _warnings
12
13
  from pathlib import Path as _Path
13
14
 
14
15
  import astroid as _ast
@@ -27,8 +28,28 @@ from ._report import Failure as _Failure
27
28
  from ._report import Failures as _Failures
28
29
  from ._utils import print_checks as _print_checks
29
30
  from .messages import TEMPLATE as _TEMPLATE
31
+ from .messages import E as _E
30
32
  from .messages import Messages as _Messages
31
33
 
34
+ _DEFAULT_EXCLUDES = """\
35
+ (?x)^(
36
+ |\\.?venv[\\\\/].*
37
+ |\\.git[\\\\/].*
38
+ |\\.hg[\\\\/].*
39
+ |\\.idea[\\\\/].*
40
+ |\\.mypy_cache[\\\\/].*
41
+ |\\.nox[\\\\/].*
42
+ |\\.pytest_cache[\\\\/].*
43
+ |\\.svn[\\\\/].*
44
+ |\\.tox[\\\\/].*
45
+ |\\.vscode[\\\\/].*
46
+ |_?build[\\\\/].*
47
+ |.*[\\\\/]__pycache__[\\\\/].*
48
+ |dist[\\\\/].*
49
+ |node_modules[\\\\/].*
50
+ )$
51
+ """
52
+
32
53
 
33
54
  def setup_logger(verbose: bool) -> None:
34
55
  """Setup docsig logger.
@@ -96,17 +117,10 @@ def _run_check(
96
117
  _run_check(func, child, config, failures)
97
118
 
98
119
 
99
- def _derive_module_name(file_path: str | _Path) -> str:
100
- converted = _os.path.splitext(str(file_path))[0]
101
- converted = converted.replace(_os.sep, ".")
102
- converted = converted.replace("-", "_")
103
- return converted
104
-
105
-
106
120
  def _from_file(path: _Path, config: _Config) -> _Parent:
107
121
  try:
108
122
  code = path.read_text(encoding="utf-8")
109
- parent = _parse_from_string(
123
+ parent = _from_str(
110
124
  context={
111
125
  "code": code,
112
126
  "module_name": _derive_module_name(path),
@@ -126,7 +140,7 @@ def _from_file(path: _Path, config: _Config) -> _Parent:
126
140
  return parent
127
141
 
128
142
 
129
- def _parse_from_string(
143
+ def _from_str(
130
144
  context: dict[str, _t.Any],
131
145
  config: _Config,
132
146
  path: _Path | None = None,
@@ -150,8 +164,8 @@ def _parse_from_string(
150
164
  return parent
151
165
 
152
166
 
153
- def _run_checks(module: _Parent, config: _Config) -> _Failures:
154
- failures: _Failures = []
167
+ def _get_failures(module: _Parent, config: _Config) -> _Failures:
168
+ failures = _Failures()
155
169
  for top_level in module.children:
156
170
  if (
157
171
  not top_level.isprotected
@@ -192,6 +206,56 @@ def _report(
192
206
  return max(retcodes)
193
207
 
194
208
 
209
+ def _run_docsig(
210
+ *path: str | _Path,
211
+ string: str | None = None,
212
+ config: _Config,
213
+ ) -> int:
214
+ setup_logger(config.verbose)
215
+ if config.list_checks:
216
+ return int(bool(_print_checks())) # type: ignore
217
+
218
+ if string is None:
219
+ retcodes = [0]
220
+ paths = _Paths(
221
+ *path,
222
+ patterns=config.exclude,
223
+ excludes=config.excludes,
224
+ include_ignored=config.include_ignored,
225
+ )
226
+ for path_ in paths:
227
+ failures = runner(path_, config)
228
+ retcodes.append(_report(failures, config, str(path_)))
229
+
230
+ return max(retcodes)
231
+
232
+ module = _from_str({"code": string}, config)
233
+ failures = _get_failures(module, config)
234
+ return _report(failures, config)
235
+
236
+
237
+ def handle_deprecations(
238
+ ignore_typechecker: bool,
239
+ disable: list,
240
+ messages: list,
241
+ stacklevel: int,
242
+ ) -> None:
243
+ """Warn for deprecated arguments.
244
+
245
+ :param ignore_typechecker: Whether using or not.
246
+ :param disable: List to add messages to.
247
+ :param messages: Messages.
248
+ :param stacklevel: Warning stacklevel.
249
+ """
250
+ if ignore_typechecker:
251
+ _warnings.warn(
252
+ "ignore-typechecker is deprecated, use disable for SIG5xx instead",
253
+ category=FutureWarning,
254
+ stacklevel=stacklevel,
255
+ )
256
+ disable.extend(messages)
257
+
258
+
195
259
  def runner(path: _Path, config: _Config) -> _Failures:
196
260
  """Per path runner.
197
261
 
@@ -200,7 +264,7 @@ def runner(path: _Path, config: _Config) -> _Failures:
200
264
  :return: Exit status for whether the test failed or not.
201
265
  """
202
266
  module = _from_file(path, config)
203
- return _run_checks(module, config)
267
+ return _get_failures(module, config)
204
268
 
205
269
 
206
270
  @_decorators.parse_msgs
@@ -221,6 +285,7 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
221
285
  ignore_no_params: bool = False,
222
286
  ignore_args: bool = False,
223
287
  ignore_kwargs: bool = False,
288
+ ignore_typechecker: bool = False, # deprecated
224
289
  no_ansi: bool = False,
225
290
  verbose: bool = False,
226
291
  target: _Messages | None = None,
@@ -256,6 +321,7 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
256
321
  documented
257
322
  :param ignore_args: Ignore args prefixed with an asterisk.
258
323
  :param ignore_kwargs: Ignore kwargs prefixed with two asterisks.
324
+ :param ignore_typechecker: Ignore checking return values.
259
325
  :param no_ansi: Disable ANSI output.
260
326
  :param verbose: Increase output verbosity.
261
327
  :param target: List of errors to target.
@@ -265,6 +331,24 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
265
331
  :param excludes: Files or dirs to exclude from checks.
266
332
  :return: Exit status for whether a test failed or not.
267
333
  """
334
+ disable = disable or _Messages()
335
+ handle_deprecations(
336
+ ignore_typechecker,
337
+ disable,
338
+ [
339
+ _E[501],
340
+ _E[502],
341
+ _E[503],
342
+ _E[504],
343
+ _E[505],
344
+ _E[506],
345
+ ],
346
+ stacklevel=5,
347
+ )
348
+ exclude_ = [_DEFAULT_EXCLUDES]
349
+ if exclude is not None:
350
+ exclude_.append(exclude)
351
+
268
352
  check = _Check(
269
353
  class_=check_class,
270
354
  class_constructor=check_class_constructor,
@@ -287,30 +371,16 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
287
371
  ignore=ignore,
288
372
  no_ansi=no_ansi,
289
373
  verbose=verbose,
290
- target=target or [],
291
- disable=disable or [],
292
- exclude=exclude,
374
+ target=target or _Messages(),
375
+ disable=disable,
376
+ exclude=exclude_,
293
377
  excludes=excludes,
294
378
  )
295
- setup_logger(config.verbose)
296
- if config.list_checks:
297
- return int(bool(_print_checks())) # type: ignore
379
+ return _run_docsig(*path, string=string, config=config)
298
380
 
299
- if string:
300
- module = _parse_from_string({"code": string}, config)
301
- failures = _run_checks(module, config)
302
- return _report(failures, config)
303
381
 
304
- retcodes = [0]
305
- paths = _Paths(
306
- *path,
307
- patterns=config.exclude,
308
- excludes=config.excludes,
309
- include_ignored=config.include_ignored,
310
- )
311
- for path_ in paths:
312
- failures = runner(path_, config)
313
- retcode = _report(failures, config, str(path_))
314
- retcodes.append(retcode)
315
-
316
- return max(retcodes)
382
+ def _derive_module_name(file_path: str | _Path) -> str:
383
+ converted = _os.path.splitext(str(file_path))[0]
384
+ converted = converted.replace(_os.sep, ".")
385
+ converted = converted.replace("-", "_")
386
+ return converted
@@ -12,8 +12,6 @@ from io import StringIO as _StringIO
12
12
  from .messages import E as _E
13
13
  from .messages import Messages as _Messages
14
14
 
15
- Comments: _t.TypeAlias = _t.List["Comment"]
16
-
17
15
 
18
16
  class Comment(_Messages):
19
17
  """Represents a comment directive.
@@ -74,6 +72,10 @@ class Comment(_Messages):
74
72
  return None
75
73
 
76
74
 
75
+ class Comments(_t.List[Comment]):
76
+ """List of comments."""
77
+
78
+
77
79
  class Directives(_t.Dict[int, _t.Tuple[Comments, _Messages]]):
78
80
  """Data for directives:
79
81
 
@@ -100,7 +102,7 @@ class Directives(_t.Dict[int, _t.Tuple[Comments, _Messages]]):
100
102
  """
101
103
  directives = cls()
102
104
  fin = _StringIO(text)
103
- comments: Comments = []
105
+ comments = Comments()
104
106
  for line in _tokenize.generate_tokens(fin.readline):
105
107
  # do nothing for these line types
106
108
  if line.type in (_tokenize.NAME, _tokenize.OP, _tokenize.DEDENT):
@@ -110,8 +112,8 @@ class Directives(_t.Dict[int, _t.Tuple[Comments, _Messages]]):
110
112
  # scope, but do not update the global comments and messages
111
113
  # unless it is confirmed that the comment is a module level
112
114
  # directive
113
- scoped_comments: Comments = list(comments)
114
- scoped_messages = list(messages)
115
+ scoped_comments = Comments(comments)
116
+ scoped_messages = _Messages(messages)
115
117
  lineno, col = line.start
116
118
  if line.type == _tokenize.COMMENT:
117
119
  comment = Comment.parse(line.string, col)
@@ -120,7 +122,7 @@ class Directives(_t.Dict[int, _t.Tuple[Comments, _Messages]]):
120
122
  if comment.disable:
121
123
  scoped_messages.extend(comment)
122
124
  elif comment.enable:
123
- scoped_messages = list(
125
+ scoped_messages = _Messages(
124
126
  i for i in messages if i not in comment
125
127
  )
126
128
 
@@ -16,24 +16,6 @@ from pathspec.patterns import GitWildMatchPattern as _GitWildMatchPattern
16
16
  from wcmatch.pathlib import Path as _WcPath
17
17
 
18
18
  FILE_INFO = "%s: %s"
19
- _DEFAULT_EXCLUDES = """\
20
- (?x)^(
21
- |\\.?venv[\\\\/].*
22
- |\\.git[\\\\/].*
23
- |\\.hg[\\\\/].*
24
- |\\.idea[\\\\/].*
25
- |\\.mypy_cache[\\\\/].*
26
- |\\.nox[\\\\/].*
27
- |\\.pytest_cache[\\\\/].*
28
- |\\.svn[\\\\/].*
29
- |\\.tox[\\\\/].*
30
- |\\.vscode[\\\\/].*
31
- |_?build[\\\\/].*
32
- |.*[\\\\/]__pycache__[\\\\/].*
33
- |dist[\\\\/].*
34
- |node_modules[\\\\/].*
35
- )$
36
- """
37
19
 
38
20
 
39
21
  class _Gitignore(_PathSpec):
@@ -99,15 +81,13 @@ class Paths(_t.List[_Path]):
99
81
  def __init__(
100
82
  self,
101
83
  *paths: _Path | str,
102
- patterns: str | None = None,
84
+ patterns: list[str],
103
85
  excludes: list[str] | None = None,
104
86
  include_ignored: bool = False,
105
87
  ) -> None:
106
88
  super().__init__()
107
- exclude = [_DEFAULT_EXCLUDES]
108
- if patterns is not None:
109
- exclude.append(patterns)
110
-
89
+ self._patterns = patterns
90
+ self._excludes = excludes or []
111
91
  self._include_ignored = include_ignored
112
92
  self._gitignore = _Gitignore()
113
93
  self._logger = _logging.getLogger(__package__)
@@ -116,8 +96,8 @@ class Paths(_t.List[_Path]):
116
96
 
117
97
  for path in list(self):
118
98
  if str(path) != "." and (
119
- any(_re.match(i, str(path)) for i in exclude)
120
- or any(_glob(path, i) for i in excludes or [])
99
+ any(_re.match(i, str(path)) for i in self._patterns)
100
+ or any(_glob(path, i) for i in self._excludes)
121
101
  ):
122
102
  self._logger.debug(
123
103
  FILE_INFO,
@@ -0,0 +1,62 @@
1
+ """
2
+ docsig._main
3
+ ============
4
+
5
+ Contains package entry point.
6
+ """
7
+
8
+ from __future__ import annotations as _
9
+
10
+ import sys as _sys
11
+ import warnings as _warnings
12
+
13
+ from ._config import parse_args as _parse_args
14
+ from ._core import docsig as _docsig
15
+ from ._hooks import excepthook as _excepthook
16
+
17
+
18
+ def _warn_on_deprecated_short_flags() -> None:
19
+ deprecated_short_flags = {
20
+ "-c": "--check-class",
21
+ "-C": "--check-class-constructor",
22
+ "-D": "--check-dunders",
23
+ "-m": "--check-protected-class-methods",
24
+ "-N": "--check-nested",
25
+ "-o": "--check-overridden",
26
+ "-p": "--check-protected",
27
+ "-P": "--check-property-returns",
28
+ "-i": "--ignore-no-params",
29
+ }
30
+ raw_args = _sys.argv[1:]
31
+ expanded_flags = []
32
+ for arg in raw_args:
33
+ if arg.startswith("--") or not arg.startswith("-") or arg == "-":
34
+ expanded_flags.append(arg)
35
+ elif len(arg) > 2:
36
+ expanded_flags.extend([f"-{ch}" for ch in arg[1:]])
37
+ else:
38
+ expanded_flags.append(arg)
39
+
40
+ used_flags = set(expanded_flags)
41
+ for short, long in deprecated_short_flags.items():
42
+ if short in used_flags:
43
+ _warnings.warn(
44
+ f"short option '{short}' is deprecated, use '{long}' instead",
45
+ category=FutureWarning,
46
+ stacklevel=2,
47
+ )
48
+
49
+
50
+ def main() -> str | int:
51
+ """Main function for package.
52
+
53
+ Collect config and arguments for the commandline.
54
+
55
+ :return: Exit status for whether the test failed or not.
56
+ """
57
+ _warn_on_deprecated_short_flags()
58
+ a = _parse_args()
59
+ _excepthook(a.no_ansi)
60
+ kwargs = vars(a)
61
+ path = kwargs.pop("path")
62
+ return _docsig(*path, **kwargs)
@@ -19,10 +19,6 @@ from ._stub import RetType as _RetType
19
19
  from ._stub import Signature as _Signature
20
20
  from .messages import Messages as _Messages
21
21
 
22
- _Imports: _t.TypeAlias = dict[str, str]
23
- _Overloads: _t.TypeAlias = _t.Dict[str, "Function"]
24
- _Children: _t.TypeAlias = _t.List[_t.Union["Parent", "Function"]]
25
-
26
22
 
27
23
  class Error(_Enum):
28
24
  """Represents an unrecoverable error."""
@@ -31,6 +27,10 @@ class Error(_Enum):
31
27
  UNICODE = 2
32
28
 
33
29
 
30
+ class _Imports(_t.Dict[str, str]):
31
+ """Represents python imports."""
32
+
33
+
34
34
  class Parent: # pylint: disable=too-many-instance-attributes
35
35
  """Represents an object that contains functions or methods.
36
36
 
@@ -66,21 +66,20 @@ class Parent: # pylint: disable=too-many-instance-attributes
66
66
  error: Error | None = None,
67
67
  ) -> None:
68
68
  super().__init__()
69
+ self._name = "module"
69
70
  self._error = error
70
- self._directives = directives or _Directives()
71
71
  self._ignore_args = ignore_args
72
72
  self._ignore_kwargs = ignore_kwargs
73
73
  self._check_class_constructor = check_class_constructor
74
- self._children: _Children = []
75
- self._imports: _Imports = imports or {}
76
- self._overloads: _Overloads = {}
74
+ self._children = _Children()
75
+ self._imports = imports or _Imports()
76
+ self._overloads = _Overloads()
77
77
  if node is None:
78
- self._name = "module"
79
78
  if not isinstance(self, Function) and error is not None:
80
79
  self._children.append(Function(path, error=error))
81
80
  else:
82
81
  self._name = node.name
83
- self._parse_ast(node, path)
82
+ self._parse_ast(node, directives or _Directives(), path)
84
83
 
85
84
  def _parse_ast(
86
85
  self,
@@ -90,20 +89,21 @@ class Parent: # pylint: disable=too-many-instance-attributes
90
89
  | _ast.nodes.FunctionDef
91
90
  | _ast.nodes.NodeNG
92
91
  ),
92
+ directives: _Directives,
93
93
  path: _Path | None = None,
94
94
  ) -> None:
95
95
  # need to keep track of `comments` as, even though they are
96
96
  # resolved in the directive object, they are needed to notify
97
97
  # the user in the case that they are invalid
98
- parent_comments, parent_disabled = self._directives.get(
98
+ parent_comments, parent_disabled = directives.get(
99
99
  node.lineno,
100
- ([], []),
100
+ (_Comments(), _Messages()),
101
101
  )
102
102
  if hasattr(node, "body"):
103
103
  for subnode in node.body:
104
- comments, disabled = self._directives.get(
104
+ comments, disabled = directives.get(
105
105
  subnode.lineno,
106
- ([], []),
106
+ (_Comments(), _Messages()),
107
107
  )
108
108
  comments.extend(parent_comments)
109
109
  disabled.extend(parent_disabled)
@@ -118,7 +118,7 @@ class Parent: # pylint: disable=too-many-instance-attributes
118
118
  func = Function(
119
119
  subnode,
120
120
  comments,
121
- self._directives,
121
+ directives,
122
122
  disabled,
123
123
  path,
124
124
  self._ignore_args,
@@ -144,7 +144,7 @@ class Parent: # pylint: disable=too-many-instance-attributes
144
144
  self._children.append(
145
145
  Parent(
146
146
  subnode,
147
- self._directives,
147
+ directives,
148
148
  path,
149
149
  self._ignore_args,
150
150
  self._ignore_kwargs,
@@ -153,7 +153,7 @@ class Parent: # pylint: disable=too-many-instance-attributes
153
153
  ),
154
154
  )
155
155
  else:
156
- self._parse_ast(subnode, path)
156
+ self._parse_ast(subnode, directives, path)
157
157
 
158
158
  @property
159
159
  def isprotected(self) -> bool:
@@ -211,8 +211,8 @@ class Function(Parent): # pylint: disable=too-many-instance-attributes
211
211
  check_class_constructor,
212
212
  imports,
213
213
  )
214
- self._comments = comments or []
215
- self._messages = messages or []
214
+ self._comments = comments or _Comments()
215
+ self._messages = messages or _Messages()
216
216
  self._parent = None
217
217
  self._decorators = None
218
218
  self._signature = _Signature()
@@ -366,3 +366,11 @@ class Function(Parent): # pylint: disable=too-many-instance-attributes
366
366
  :param rettype: The return type of the overloaded signature.
367
367
  """
368
368
  self._signature.overload(rettype)
369
+
370
+
371
+ class _Overloads(_t.Dict[str, Function]):
372
+ """Represents overloaded methods."""
373
+
374
+
375
+ class _Children(_t.List[_t.Union[Parent, Function]]):
376
+ """Represents children of an object."""
@@ -18,6 +18,7 @@ from ._stub import Param as _Param
18
18
  from ._stub import Params as _Params
19
19
  from ._stub import RetType as _RetType
20
20
  from ._utils import almost_equal as _almost_equal
21
+ from ._utils import has_bad_return as _has_bad_return
21
22
  from ._utils import sentence_tokenizer as _sentence_tokenizer
22
23
  from .messages import E as _E
23
24
  from .messages import Message as _Message
@@ -27,9 +28,6 @@ _MIN_MATCH = 0.8
27
28
  _MAX_MATCH = 1.0
28
29
 
29
30
 
30
- Failures: _t.TypeAlias = _t.List["Failure"]
31
-
32
-
33
31
  class Failed(_t.NamedTuple):
34
32
  """Report info object."""
35
33
 
@@ -58,9 +56,19 @@ class Failure(_t.List[Failed]):
58
56
  super().__init__()
59
57
  self._retcode = 0
60
58
  self._func = func
59
+ self._docstring_len = len(self._func.docstring.args)
61
60
  if target:
62
61
  self._func.messages.extend(i for i in _E.all if i not in target)
63
62
 
63
+ self._name = self._func.name
64
+ if (
65
+ self._func.parent is not None
66
+ and self._func.parent.name
67
+ and not isinstance(self._func.parent, _scoped_nodes.Module)
68
+ ):
69
+ self._name = f"{self._func.parent.name}.{self._func.name}"
70
+
71
+ self._check_property_returns = check_property_returns
64
72
  if self._func.error is not None:
65
73
  self._sig9xx_error()
66
74
  else:
@@ -75,23 +83,18 @@ class Failure(_t.List[Failed]):
75
83
  sig = self._func.signature.args.get(index)
76
84
  self._sig4xx_parameters(doc, sig)
77
85
 
78
- self._sig5xx_returns(check_property_returns)
86
+ self._sig5xx_returns()
79
87
 
80
88
  self.sort()
81
89
 
82
- def _add(
83
- self,
84
- value: _Message,
85
- hint: bool = False,
86
- **kwargs: _t.Any,
87
- ) -> None:
90
+ def _add(self, value: _Message, hint: bool = False, **kwargs) -> None:
88
91
  self._retcode = 1
89
92
  failed = Failed(
90
- self.name,
93
+ self._name,
91
94
  value.ref,
92
95
  value.description.format(**kwargs),
93
96
  value.symbolic,
94
- self.lineno,
97
+ self._func.lineno,
95
98
  value.hint if hint else None,
96
99
  )
97
100
  if value not in self._func.messages and failed not in self:
@@ -216,9 +219,9 @@ class Failure(_t.List[Failed]):
216
219
  # bad-closing-token
217
220
  self._add(_E[304], token=doc.closing_token, hint=True)
218
221
  if doc.description is not None and not all(
219
- i.strip()[0].isupper()
222
+ stripped[0].isupper()
220
223
  for i in _sentence_tokenizer(doc.description)
221
- if i and not any(i.startswith(x) for x in (":", ".", "`"))
224
+ if i and (stripped := i.strip())[0].isalpha()
222
225
  ):
223
226
  # description is not capitalized
224
227
  self._add(_E[305])
@@ -231,7 +234,7 @@ class Failure(_t.List[Failed]):
231
234
  if (
232
235
  sig.name in self._func.docstring.args.names
233
236
  or doc.name in self._func.signature.args.names
234
- ) and len(self._func.docstring.args) > 1:
237
+ ) and self._docstring_len > 1:
235
238
  # params-out-of-order
236
239
  self._add(_E[402])
237
240
  elif (
@@ -246,9 +249,9 @@ class Failure(_t.List[Failed]):
246
249
  # param-not-equal-to-arg
247
250
  self._add(_E[404])
248
251
 
249
- def _sig5xx_returns(self, check_property_returns: bool) -> None:
252
+ def _sig5xx_returns(self) -> None:
250
253
  if not self._func.isinit and not (
251
- self._func.isproperty and not check_property_returns
254
+ self._func.isproperty and not self._check_property_returns
252
255
  ):
253
256
  # no types, cannot know either way
254
257
  if self._func.signature.rettype == _RetType.UNTYPED:
@@ -264,14 +267,9 @@ class Failure(_t.List[Failed]):
264
267
  # return-type is some, so return should be documented
265
268
  elif self._func.signature.returns:
266
269
  # return-missing
267
- lines = str(self._func.docstring.string).splitlines()
268
270
  self._add(
269
271
  _E[503],
270
- hint=(
271
- len(lines) > 1
272
- and "return" in lines[-1]
273
- and ":param" not in lines[-1]
274
- ),
272
+ hint=_has_bad_return(str(self._func.docstring.string)),
275
273
  )
276
274
  elif self._func.docstring.returns:
277
275
  # this method is init, so no return should be documented
@@ -279,7 +277,7 @@ class Failure(_t.List[Failed]):
279
277
  # class-return-documented
280
278
  self._add(_E[504], hint=True)
281
279
  # method is property and not set to document property
282
- elif self._func.isproperty and not check_property_returns:
280
+ elif self._func.isproperty and not self._check_property_returns:
283
281
  # return-documented-for-property
284
282
  self._add(_E[505], hint=True)
285
283
 
@@ -294,14 +292,7 @@ class Failure(_t.List[Failed]):
294
292
  @property
295
293
  def name(self) -> str:
296
294
  """Function name."""
297
- if (
298
- self._func.parent is not None
299
- and self._func.parent.name
300
- and not isinstance(self._func.parent, _scoped_nodes.Module)
301
- ):
302
- return f"{self._func.parent.name}.{self._func.name}"
303
-
304
- return self._func.name
295
+ return self._name
305
296
 
306
297
  @property
307
298
  def lineno(self) -> int:
@@ -312,3 +303,7 @@ class Failure(_t.List[Failed]):
312
303
  def retcode(self) -> int:
313
304
  """How to exit the program."""
314
305
  return self._retcode
306
+
307
+
308
+ class Failures(_t.List[Failure]):
309
+ """Sequence of failed functions."""
@@ -80,7 +80,7 @@ class DocType(_Enum):
80
80
  # todo: consider a parent object that can be used for returns that do
81
81
  # todo: not include the name attribute
82
82
  class Param:
83
- """Represents a docstring or signature param.
83
+ """A tuple of param types and their names.
84
84
 
85
85
  :param kind: The type of the parameter.
86
86
  :param name: The name of the parameter.
@@ -98,11 +98,11 @@ class Param:
98
98
  indent: int = 0,
99
99
  closing_token: str = ":",
100
100
  ) -> None:
101
- self._kind = kind
102
- self._name = name
103
- self._description = description
104
- self._indent = indent
105
- self._closing_token = closing_token
101
+ self.kind = kind
102
+ self.name = name
103
+ self.description = description
104
+ self.indent = indent
105
+ self.closing_token = closing_token
106
106
 
107
107
  def __eq__(self, other: object) -> bool:
108
108
  iseq = False
@@ -123,31 +123,6 @@ class Param:
123
123
  """Boolean value for whether this parameter is protected."""
124
124
  return str(self.name).startswith("_")
125
125
 
126
- @property
127
- def kind(self) -> DocType:
128
- """Type of the param."""
129
- return self._kind
130
-
131
- @property
132
- def name(self) -> str | None:
133
- """Name of the param."""
134
- return self._name
135
-
136
- @property
137
- def description(self) -> str | None:
138
- """Description of param."""
139
- return self._description
140
-
141
- @property
142
- def indent(self) -> int:
143
- """Number of spaces in the indent."""
144
- return self._indent
145
-
146
- @property
147
- def closing_token(self) -> str:
148
- """Token used to terminate the param name definition."""
149
- return self._closing_token
150
-
151
126
 
152
127
  class Params(_t.List[Param]):
153
128
  """A list-like collection of params.
@@ -54,6 +54,25 @@ def print_checks() -> None:
54
54
  print(msg.fstring(_TEMPLATE))
55
55
 
56
56
 
57
+ def has_bad_return(string: str) -> bool:
58
+ """Search for return documented with poor syntax.
59
+
60
+ Put this here in case the function increases in complexity.
61
+
62
+ Do more than search the docstring for the word return as return
63
+ statements come last, so only search the last line params can also
64
+ come last, so make sure it is not a param declaration.
65
+
66
+ :param string: Docstring to check.
67
+ :return: Boolean value indicating if a function has a bad return
68
+ statement.
69
+ """
70
+ lines = string.splitlines()
71
+ return (
72
+ len(lines) > 1 and "return" in lines[-1] and ":param" not in lines[-1]
73
+ )
74
+
75
+
57
76
  def sentence_tokenizer(text: str) -> list[str]:
58
77
  """Split text into sentences.
59
78
 
@@ -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.80.0"
11
+ __version__ = "0.80.1"
@@ -17,9 +17,6 @@ TEMPLATE = "{ref}: {description} ({symbolic})"
17
17
  FLAKE8 = "{ref} {description} ({symbolic})"
18
18
 
19
19
 
20
- Messages: _t.TypeAlias = _t.List["Message"]
21
-
22
-
23
20
  class Message(_t.NamedTuple):
24
21
  """Represents an error message."""
25
22
 
@@ -57,6 +54,10 @@ class Message(_t.NamedTuple):
57
54
  )
58
55
 
59
56
 
57
+ class Messages(_t.List[Message]):
58
+ """List of messages."""
59
+
60
+
60
61
  class MessageMap(_t.Dict[int, Message]):
61
62
  """Messages mapped under an integer version of their codes."""
62
63
 
@@ -78,12 +79,12 @@ class MessageMap(_t.Dict[int, Message]):
78
79
  :param refs: List of codes or symbolic references.
79
80
  :return: List of message types.
80
81
  """
81
- return list(self.from_ref(i) for i in refs)
82
+ return Messages(self.from_ref(i) for i in refs)
82
83
 
83
84
  @property
84
85
  def all(self) -> Messages:
85
86
  """Get all messages that aren't a config error."""
86
- return list(v for k, v in self.items() if len(str(k)) > 1)
87
+ return Messages(v for k, v in self.items() if len(str(k)) > 1)
87
88
 
88
89
 
89
90
  # SIGxxx: Error
@@ -142,7 +143,7 @@ E = MessageMap(
142
143
  "parameters missing",
143
144
  "params-missing",
144
145
  ),
145
- #: SIG3xx Parameters
146
+ #: SIG3xx Description
146
147
  301: Message(
147
148
  "SIG301",
148
149
  "description missing from parameter",
@@ -169,7 +170,7 @@ E = MessageMap(
169
170
  "description does not begin with a capital letter",
170
171
  "description-not-capitalized",
171
172
  ),
172
- #: SIG4xx Description
173
+ #: SIG4xx Parameters
173
174
  401: Message(
174
175
  "SIG401",
175
176
  "param not indented correctly",
@@ -1,25 +1,23 @@
1
1
  """Flake8 implementation of docsig."""
2
2
 
3
- import ast as _ast
4
- import os as _os
5
- import sys as _sys
6
- import typing as _t
7
- from argparse import Namespace as _Namespace
8
- from pathlib import Path as _Path
3
+ import ast
4
+ import os
5
+ import sys
6
+ import typing as t
7
+ from argparse import SUPPRESS, Namespace
8
+ from pathlib import Path
9
9
 
10
10
  from ._config import Check as _Check
11
11
  from ._config import Config as _Config
12
12
  from ._config import Ignore as _Ignore
13
13
  from ._config import get_config as _get_config
14
14
  from ._config import merge_configs as _merge_configs
15
- from ._core import runner as _runner
16
- from ._core import setup_logger as _setup_logger
15
+ from ._core import handle_deprecations, runner, setup_logger
17
16
  from ._version import __version__
18
- from .messages import FLAKE8 as _FLAKE8
19
- from .messages import E as _E
17
+ from .messages import FLAKE8, E
20
18
 
21
- _Flake8Error = _t.Tuple[int, int, str, _t.Type[_t.Any]]
22
- _sys.path.append(_os.path.abspath(_os.getcwd()))
19
+ Flake8Error = t.Tuple[int, int, str, t.Type]
20
+ sys.path.append(os.path.abspath(os.getcwd()))
23
21
 
24
22
 
25
23
  class Docsig:
@@ -32,9 +30,9 @@ class Docsig:
32
30
  off_by_default = False
33
31
  name = __package__
34
32
  version = __version__
35
- a = _Namespace()
33
+ a = Namespace()
36
34
 
37
- def __init__(self, tree: _ast.Module, filename: str) -> None:
35
+ def __init__(self, tree: ast.Module, filename: str) -> None:
38
36
  _tree = tree # noqa
39
37
  self.filename = filename
40
38
 
@@ -43,17 +41,11 @@ class Docsig:
43
41
  # might require that flake8 actually be installed, which is not a
44
42
  # requirement for this package
45
43
  @classmethod
46
- def add_options(cls, parser: _t.Any) -> None:
44
+ def add_options(cls, parser: t.Any) -> None:
47
45
  """Add flake8 commandline and config options.
48
46
 
49
47
  :param parser: Flake8 option manager.
50
48
  """
51
- parser.add_option(
52
- "--sig-verbose",
53
- action="store_true",
54
- parse_from_config=True,
55
- help="increase output verbosity",
56
- )
57
49
  parser.add_option(
58
50
  "--sig-check-class",
59
51
  action="store_true",
@@ -64,10 +56,7 @@ class Docsig:
64
56
  "--sig-check-class-constructor",
65
57
  action="store_true",
66
58
  parse_from_config=True,
67
- help=(
68
- "check __init__ methods (mutually incompatible with"
69
- " --sig-check-class)"
70
- ),
59
+ help="check __init__ methods. Note: mutually incompatible with -c",
71
60
  )
72
61
  parser.add_option(
73
62
  "--sig-check-dunders",
@@ -75,6 +64,12 @@ class Docsig:
75
64
  parse_from_config=True,
76
65
  help="check dunder methods",
77
66
  )
67
+ parser.add_option(
68
+ "--sig-check-protected-class-methods",
69
+ action="store_true",
70
+ parse_from_config=True,
71
+ help="check public methods belonging to protected classes",
72
+ )
78
73
  parser.add_option(
79
74
  "--sig-check-nested",
80
75
  action="store_true",
@@ -88,22 +83,22 @@ class Docsig:
88
83
  help="check overridden methods",
89
84
  )
90
85
  parser.add_option(
91
- "--sig-check-property-returns",
86
+ "--sig-check-protected",
92
87
  action="store_true",
93
88
  parse_from_config=True,
94
- help="check property return values",
89
+ help="check protected functions and classes",
95
90
  )
96
91
  parser.add_option(
97
- "--sig-check-protected",
92
+ "--sig-check-property-returns",
98
93
  action="store_true",
99
94
  parse_from_config=True,
100
- help="check protected functions and classes",
95
+ help="check property return values",
101
96
  )
102
97
  parser.add_option(
103
- "--sig-check-protected-class-methods",
98
+ "--sig-ignore-no-params",
104
99
  action="store_true",
105
100
  parse_from_config=True,
106
- help="check public methods belonging to protected classes",
101
+ help="ignore docstrings where parameters are not documented",
107
102
  )
108
103
  parser.add_option(
109
104
  "--sig-ignore-args",
@@ -118,39 +113,54 @@ class Docsig:
118
113
  help="ignore kwargs prefixed with two asterisks",
119
114
  )
120
115
  parser.add_option(
121
- "--sig-ignore-no-params",
116
+ "--sig-ignore-typechecker",
122
117
  action="store_true",
123
118
  parse_from_config=True,
124
- help="ignore docstrings where parameters are not documented",
119
+ help=SUPPRESS,
120
+ )
121
+ parser.add_option(
122
+ "--sig-verbose",
123
+ action="store_true",
124
+ parse_from_config=True,
125
+ help="increase output verbosity",
125
126
  )
126
127
 
127
128
  @classmethod
128
- def parse_options(cls, a: _Namespace) -> None:
129
+ def parse_options(cls, a: Namespace) -> None:
129
130
  """Parse flake8 options into an instance-accessible dict.
130
131
 
131
132
  :param a: Argparse namespace.
132
133
  """
134
+ if getattr(a, "sig_ignore_typechecker", False):
135
+ a.extend_ignore = list(a.extend_ignore or [])
136
+ handle_deprecations(
137
+ getattr(a, "sig_ignore_typechecker", False),
138
+ a.extend_ignore,
139
+ ["SIG501", "SIG502", "SIG503", "SIG504", "SIG505", "SIG506"],
140
+ stacklevel=8,
141
+ )
142
+
133
143
  cls.a.__dict__ = _merge_configs(
134
144
  {k.replace("sig_", ""): v for k, v in a.__dict__.items()},
135
145
  _get_config(__package__),
136
146
  )
137
147
 
138
- def run(self) -> _t.Generator[_Flake8Error, None, None]:
148
+ def run(self) -> t.Generator[Flake8Error, None, None]:
139
149
  """Run docsig and possibly yield a flake8 error.
140
150
 
141
151
  :return: Flake8 error, if there is one.
142
152
  """
143
153
  if self.a.check_class and self.a.check_class_constructor:
144
154
  line = "{msg}".format(
145
- msg=_FLAKE8.format(
146
- ref=_E[5].ref,
147
- description=_E[5].description,
148
- symbolic=_E[5].symbolic,
155
+ msg=FLAKE8.format(
156
+ ref=E[5].ref,
157
+ description=E[5].description,
158
+ symbolic=E[5].symbolic,
149
159
  ),
150
160
  )
151
161
  yield 0, 0, line, self.__class__
152
162
  else:
153
- _setup_logger(self.a.verbose)
163
+ setup_logger(self.a.verbose)
154
164
  check = _Check(
155
165
  class_=self.a.check_class,
156
166
  class_constructor=self.a.check_class_constructor,
@@ -171,11 +181,11 @@ class Docsig:
171
181
  ignore=ignore,
172
182
  verbose=self.a.verbose,
173
183
  )
174
- results = _runner(_Path(self.filename), config)
184
+ results = runner(Path(self.filename), config)
175
185
  for result in results:
176
186
  for info in result:
177
187
  line = "{msg} '{name}'".format(
178
- msg=_FLAKE8.format(
188
+ msg=FLAKE8.format(
179
189
  ref=info.ref,
180
190
  description=info.description,
181
191
  symbolic=info.symbolic,
@@ -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.80.0"
14
+ current_version = "0.80.1"
15
15
  message = "bump: version {current_version} → {new_version}"
16
16
  sign_tags = true
17
17
  tag = true
@@ -61,6 +61,7 @@ fail_under = 100
61
61
 
62
62
  [tool.coverage.run]
63
63
  omit = [
64
+ "*/sitecustomize.py",
64
65
  "docs/conf.py",
65
66
  "docsig/__main__.py",
66
67
  "whitelist.py"
@@ -121,7 +122,7 @@ maintainers = [
121
122
  name = "docsig"
122
123
  readme = "README.rst"
123
124
  repository = "https://github.com/jshwi/docsig"
124
- version = "0.80.0"
125
+ version = "0.80.1"
125
126
 
126
127
  [tool.poetry.dependencies]
127
128
  Sphinx = ">=7,<9"
@@ -1,26 +0,0 @@
1
- """
2
- docsig._main
3
- ============
4
-
5
- Contains package entry point.
6
- """
7
-
8
- from __future__ import annotations as _
9
-
10
- from ._config import parse_args as _parse_args
11
- from ._core import docsig as _docsig
12
- from ._hooks import excepthook as _excepthook
13
-
14
-
15
- def main() -> str | int:
16
- """Main function for package.
17
-
18
- Collect config and arguments for the commandline.
19
-
20
- :return: Exit status for whether the test failed or not.
21
- """
22
- a = _parse_args()
23
- _excepthook(a.no_ansi)
24
- kwargs = vars(a)
25
- path = kwargs.pop("path")
26
- return _docsig(*path, **kwargs)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes