docsig 0.90.0__tar.gz → 0.90.2__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.
Files changed (27) hide show
  1. {docsig-0.90.0 → docsig-0.90.2}/PKG-INFO +4 -4
  2. {docsig-0.90.0 → docsig-0.90.2}/README.rst +3 -3
  3. {docsig-0.90.0 → docsig-0.90.2}/docsig/_check.py +3 -3
  4. {docsig-0.90.0 → docsig-0.90.2}/docsig/_directives.py +8 -0
  5. {docsig-0.90.0 → docsig-0.90.2}/docsig/_module.py +16 -0
  6. {docsig-0.90.0 → docsig-0.90.2}/docsig/_report.py +11 -9
  7. {docsig-0.90.0 → docsig-0.90.2}/docsig/_stub.py +47 -16
  8. {docsig-0.90.0 → docsig-0.90.2}/docsig/_version.py +1 -1
  9. {docsig-0.90.0 → docsig-0.90.2}/pyproject.toml +2 -2
  10. {docsig-0.90.0 → docsig-0.90.2}/LICENSE +0 -0
  11. {docsig-0.90.0 → docsig-0.90.2}/LICENSES/SPHINX_LICENSE +0 -0
  12. {docsig-0.90.0 → docsig-0.90.2}/docsig/__init__.py +0 -0
  13. {docsig-0.90.0 → docsig-0.90.2}/docsig/__main__.py +0 -0
  14. {docsig-0.90.0 → docsig-0.90.2}/docsig/_config.py +0 -0
  15. {docsig-0.90.0 → docsig-0.90.2}/docsig/_core.py +0 -0
  16. {docsig-0.90.0 → docsig-0.90.2}/docsig/_decorators.py +0 -0
  17. {docsig-0.90.0 → docsig-0.90.2}/docsig/_files.py +0 -0
  18. {docsig-0.90.0 → docsig-0.90.2}/docsig/_hooks.py +0 -0
  19. {docsig-0.90.0 → docsig-0.90.2}/docsig/_main.py +0 -0
  20. {docsig-0.90.0 → docsig-0.90.2}/docsig/_parsers.py +0 -0
  21. {docsig-0.90.0 → docsig-0.90.2}/docsig/_utils.py +0 -0
  22. {docsig-0.90.0 → docsig-0.90.2}/docsig/_vendor/sphinx/ext/napoleon.py +0 -0
  23. {docsig-0.90.0 → docsig-0.90.2}/docsig/messages.py +0 -0
  24. {docsig-0.90.0 → docsig-0.90.2}/docsig/plugin/__init__.py +0 -0
  25. {docsig-0.90.0 → docsig-0.90.2}/docsig/plugin/_flake8.py +0 -0
  26. {docsig-0.90.0 → docsig-0.90.2}/docsig/plugin/_validate_pyproject.py +0 -0
  27. {docsig-0.90.0 → docsig-0.90.2}/docsig/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: docsig
3
- Version: 0.90.0
3
+ Version: 0.90.2
4
4
  Summary: Check signature params for proper documentation
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -206,7 +206,7 @@ ensure your installation has registered `docsig`
206
206
  .. code-block:: console
207
207
 
208
208
  $ flake8 --version
209
- 7.3.0 (docsig: 0.90.0, mccabe: 0.7.0, pycodestyle: 2.14.0, pyflakes: 3.4.0) CPython 3.10.19 on Darwin
209
+ 7.3.0 (docsig: 0.90.2, mccabe: 0.7.0, pycodestyle: 2.14.0, pyflakes: 3.4.0) CPython 3.10.19 on Darwin
210
210
 
211
211
  And now use `flake8` to lint your files
212
212
 
@@ -292,7 +292,7 @@ Standalone
292
292
 
293
293
  repos:
294
294
  - repo: https://github.com/jshwi/docsig
295
- rev: v0.90.0
295
+ rev: v0.90.2
296
296
  hooks:
297
297
  - id: docsig
298
298
  args:
@@ -311,7 +311,7 @@ or integrated with ``flake8``
311
311
  hooks:
312
312
  - id: flake8
313
313
  additional_dependencies:
314
- - docsig==0.90.0
314
+ - docsig==0.90.2
315
315
  args:
316
316
  - "--sig-check-class"
317
317
  - "--sig-check-dunders"
@@ -178,7 +178,7 @@ ensure your installation has registered `docsig`
178
178
  .. code-block:: console
179
179
 
180
180
  $ flake8 --version
181
- 7.3.0 (docsig: 0.90.0, mccabe: 0.7.0, pycodestyle: 2.14.0, pyflakes: 3.4.0) CPython 3.10.19 on Darwin
181
+ 7.3.0 (docsig: 0.90.2, mccabe: 0.7.0, pycodestyle: 2.14.0, pyflakes: 3.4.0) CPython 3.10.19 on Darwin
182
182
 
183
183
  And now use `flake8` to lint your files
184
184
 
@@ -264,7 +264,7 @@ Standalone
264
264
 
265
265
  repos:
266
266
  - repo: https://github.com/jshwi/docsig
267
- rev: v0.90.0
267
+ rev: v0.90.2
268
268
  hooks:
269
269
  - id: docsig
270
270
  args:
@@ -283,7 +283,7 @@ or integrated with ``flake8``
283
283
  hooks:
284
284
  - id: flake8
285
285
  additional_dependencies:
286
- - docsig==0.90.0
286
+ - docsig==0.90.2
287
287
  args:
288
288
  - "--sig-check-class"
289
289
  - "--sig-check-dunders"
@@ -47,9 +47,9 @@ def _run_check(
47
47
  parent,
48
48
  config,
49
49
  ):
50
- failure = _check_function(child, config)
51
- if failure:
52
- failures.append(failure)
50
+ result = _check_function(child, config)
51
+ if result:
52
+ failures.append(result)
53
53
 
54
54
  # recurse for either class methods or, if enabled, nested functions
55
55
  if not isinstance(child, _Function) or config.check.nested:
@@ -174,6 +174,14 @@ class Directives(dict[int, tuple[Comments, _Messages]]):
174
174
  comments = scoped_comments
175
175
  messages = scoped_messages
176
176
  else:
177
+ # keep disable on this line even if an earlier
178
+ # token already recorded an empty entry (e.g.
179
+ # a string arg before an inline comment)
180
+ directives[lineno] = (
181
+ Comments(scoped_comments),
182
+ _Messages(scoped_messages),
183
+ )
184
+
177
185
  # defer scoped state for the next line without
178
186
  # changing module-level messages
179
187
  pending_inline = (
@@ -109,6 +109,22 @@ class Parent: # pylint: disable=too-many-instance-attributes
109
109
  subnode.lineno or 0,
110
110
  (_Comments(), _Messages()),
111
111
  )
112
+
113
+ # astroid sets lineno to the first decorator
114
+ # inline disable on the def line is at fromlineno
115
+ fromlineno = getattr(subnode, "fromlineno", 0)
116
+ if (
117
+ isinstance(subnode, _ast.nodes.FunctionDef)
118
+ and fromlineno
119
+ and fromlineno != (subnode.lineno or 0)
120
+ ):
121
+ more_comments, more_disabled = self._directives.get(
122
+ fromlineno,
123
+ (_Comments(), _Messages()),
124
+ )
125
+ comments.extend(more_comments)
126
+ disabled.extend(more_disabled)
127
+
112
128
  comments.extend(parent_comments)
113
129
  disabled.extend(parent_disabled)
114
130
  if isinstance(
@@ -13,6 +13,7 @@ import json as _json
13
13
  import os as _os
14
14
  import sys as _sys
15
15
  import typing as _t
16
+ from dataclasses import dataclass as _dataclass
16
17
  from warnings import warn as _warn
17
18
 
18
19
  import astroid as _ast
@@ -71,7 +72,8 @@ class Failures(list["Failure"]):
71
72
  """Sequence of Failure instances (one per function checked)."""
72
73
 
73
74
 
74
- class Failed(_t.NamedTuple):
75
+ @_dataclass(frozen=True, order=True)
76
+ class Diagnostic: # pylint: disable=too-few-public-methods
75
77
  """Single reported issue."""
76
78
 
77
79
  name: str
@@ -83,7 +85,7 @@ class Failed(_t.NamedTuple):
83
85
  new: bool = False
84
86
 
85
87
 
86
- class Failure(list[Failed]):
88
+ class Failure(list[Diagnostic]):
87
89
  """Collect docstring and signature failures for one function.
88
90
 
89
91
  Runs configured checks and appends Failed entries for each
@@ -137,7 +139,7 @@ class Failure(list[Failed]):
137
139
  **kwargs: _t.Any,
138
140
  ) -> None:
139
141
  self._retcode.add(int(not value.new))
140
- failed = Failed(
142
+ failed = Diagnostic(
141
143
  self._name,
142
144
  value.ref,
143
145
  value.description.format(**kwargs),
@@ -416,15 +418,15 @@ def report(
416
418
  retcodes = RetCode()
417
419
  output = []
418
420
  obj = []
419
- for failure in failures:
420
- retcodes.add(failure.retcode)
421
+ for result in failures:
422
+ retcodes.add(result.retcode)
421
423
  path_prefix = f"{file}:" if file is not None else ""
422
- header = f"{path_prefix}{failure.lineno} in {failure.name}"
424
+ header = f"{path_prefix}{result.lineno} in {result.name}"
423
425
  if not config.no_ansi and _sys.stdout.isatty():
424
426
  header = f"\033[35m{header}\033[0m"
425
427
 
426
428
  output.append(header)
427
- for item in failure:
429
+ for item in result:
428
430
  extra = None
429
431
  if item.hint:
430
432
  extra = f"hint: {item.hint}"
@@ -444,9 +446,9 @@ def report(
444
446
 
445
447
  obj.append(
446
448
  {
447
- "line": None if failure.retcode == 2 else item.lineno,
449
+ "line": None if result.retcode == 2 else item.lineno,
448
450
  "message": msg,
449
- "exit": failure.retcode,
451
+ "exit": result.retcode,
450
452
  },
451
453
  )
452
454
 
@@ -7,8 +7,8 @@ Stub types for parsed docstrings and signatures.
7
7
 
8
8
  from __future__ import annotations as _
9
9
 
10
+ import inspect as _inspect
10
11
  import re as _re
11
- import textwrap as _textwrap
12
12
  import typing as _t
13
13
  from collections import Counter as _Counter
14
14
  from enum import Enum as _Enum
@@ -325,21 +325,44 @@ class Docstring(_Stub):
325
325
 
326
326
  return False
327
327
 
328
+ @staticmethod
329
+ def _docstring_style(string: str) -> str:
330
+ # prefer existing rst fields over napoleon section headers
331
+ if _re.search(r"^:\w+", string, _re.MULTILINE):
332
+ return "rst"
333
+
334
+ if _re.search(
335
+ r"^(Parameters|Other Parameters|Returns|Yields|Raises|"
336
+ r"See Also|Notes|Examples|Attributes|Methods)\n"
337
+ r"\s*-{3,}\s*$",
338
+ string,
339
+ _re.MULTILINE,
340
+ ):
341
+ return "numpy"
342
+
343
+ if _re.search(
344
+ r"^(Args|Arguments|Parameters|Returns|Yields|Raises|"
345
+ r"Attributes|Example|Examples):\s*$",
346
+ string,
347
+ _re.MULTILINE,
348
+ ):
349
+ return "google"
350
+
351
+ return "rst"
352
+
328
353
  @staticmethod
329
354
  def _normalize_docstring(string: str) -> str:
330
- # convert Google and numpy style docstrings to parse docstrings
331
- # as restructured text
332
- return str(
333
- _s.NumpyDocstring( # type: ignore
334
- str(
335
- _s.GoogleDocstring( # type: ignore
336
- _textwrap.dedent(
337
- "\n".join(string.splitlines()[1:]),
338
- ).replace("*", ""),
339
- ),
340
- ),
341
- ),
342
- )
355
+ # convert Google or numpy style to rst when detected
356
+ # leave rst (including field lists) unchanged
357
+ string = _inspect.cleandoc(string)
358
+ style = Docstring._docstring_style(string)
359
+ if style == "google":
360
+ return str(_s.GoogleDocstring(string)) # type: ignore
361
+
362
+ if style == "numpy":
363
+ return str(_s.NumpyDocstring(string)) # type: ignore
364
+
365
+ return string
343
366
 
344
367
  def __init__(
345
368
  self,
@@ -371,16 +394,24 @@ class Docstring(_Stub):
371
394
  # the suggestion is broken
372
395
  # noinspection RegExpSingleCharAlternation
373
396
  for match in _re.findall(
374
- r":([\w\s]+(?:\s\|\s[\w\s]+|\w+))([^\w\s])((?:.|\n)*?)(?=\n:|$)",
397
+ r":((?:\\?\*){0,2}[\w]+"
398
+ r"(?:\s+(?:\\?\*){0,2}[\w]+|"
399
+ r"\s\|\s(?:\\?\*){0,2}[\w]+)*)"
400
+ r"([^\w\s\\*])"
401
+ r"((?:.|\n)*?)(?=\n:|$)",
375
402
  string,
376
403
  ):
377
404
  if match:
378
405
  kinds = match[0].split()
379
406
  if kinds:
407
+ name = None
408
+ if len(kinds) > 1:
409
+ # drop * / ** / napoleon-escaped stars on the name
410
+ name = kinds[-1].lstrip("\\*")
380
411
  docstring.args.append(
381
412
  Param(
382
413
  DocType.from_str(kinds[0]),
383
- UNNAMED if len(kinds) == 1 else kinds[-1],
414
+ UNNAMED if name is None else name,
384
415
  match[2] or None,
385
416
  int(indent_anomaly),
386
417
  match[1],
@@ -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.90.0"
11
+ __version__ = "0.90.2"
@@ -29,7 +29,7 @@ maintainers = [
29
29
  name = "docsig"
30
30
  readme = "README.rst"
31
31
  requires-python = ">=3.10"
32
- version = "0.90.0"
32
+ version = "0.90.2"
33
33
 
34
34
  [project.entry-points."flake8.extension"]
35
35
  SIG = "docsig.plugin:Flake8"
@@ -47,7 +47,7 @@ line-length = 79
47
47
  allow_dirty = true
48
48
  commit = true
49
49
  commit_args = "-sS"
50
- current_version = "0.90.0"
50
+ current_version = "0.90.2"
51
51
  message = "bump: version {current_version} → {new_version}"
52
52
  sign_tags = true
53
53
  tag = true
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes