docsig 0.53.2__tar.gz → 0.54.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {docsig-0.53.2 → docsig-0.54.0}/PKG-INFO +2 -2
- {docsig-0.53.2 → docsig-0.54.0}/README.rst +1 -1
- {docsig-0.53.2 → docsig-0.54.0}/docsig/_report.py +50 -33
- {docsig-0.53.2 → docsig-0.54.0}/docsig/_stub.py +32 -3
- {docsig-0.53.2 → docsig-0.54.0}/docsig/_version.py +1 -1
- {docsig-0.53.2 → docsig-0.54.0}/docsig/messages.py +6 -21
- {docsig-0.53.2 → docsig-0.54.0}/pyproject.toml +55 -40
- {docsig-0.53.2 → docsig-0.54.0}/LICENSE +0 -0
- {docsig-0.53.2 → docsig-0.54.0}/docsig/__init__.py +0 -0
- {docsig-0.53.2 → docsig-0.54.0}/docsig/__main__.py +0 -0
- {docsig-0.53.2 → docsig-0.54.0}/docsig/_config.py +0 -0
- {docsig-0.53.2 → docsig-0.54.0}/docsig/_core.py +0 -0
- {docsig-0.53.2 → docsig-0.54.0}/docsig/_decorators.py +0 -0
- {docsig-0.53.2 → docsig-0.54.0}/docsig/_directives.py +0 -0
- {docsig-0.53.2 → docsig-0.54.0}/docsig/_hooks.py +0 -0
- {docsig-0.53.2 → docsig-0.54.0}/docsig/_main.py +0 -0
- {docsig-0.53.2 → docsig-0.54.0}/docsig/_module.py +0 -0
- {docsig-0.53.2 → docsig-0.54.0}/docsig/_utils.py +0 -0
- {docsig-0.53.2 → docsig-0.54.0}/docsig/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: docsig
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.54.0
|
|
4
4
|
Summary: Check signature params for proper documentation
|
|
5
5
|
Home-page: https://pypi.org/project/docsig/
|
|
6
6
|
License: MIT
|
|
@@ -222,7 +222,7 @@ It can be added to your .pre-commit-config.yaml as follows:
|
|
|
222
222
|
|
|
223
223
|
repos:
|
|
224
224
|
- repo: https://github.com/jshwi/docsig
|
|
225
|
-
rev: v0.
|
|
225
|
+
rev: v0.54.0
|
|
226
226
|
hooks:
|
|
227
227
|
- id: docsig
|
|
228
228
|
args:
|
|
@@ -12,6 +12,7 @@ import click as _click
|
|
|
12
12
|
|
|
13
13
|
from ._module import Function as _Function
|
|
14
14
|
from ._stub import UNNAMED as _UNNAMED
|
|
15
|
+
from ._stub import VALID_DESCRIPTION as _VALID_DESCRIPTION
|
|
15
16
|
from ._stub import Param as _Param
|
|
16
17
|
from ._stub import RetType as _RetType
|
|
17
18
|
from ._utils import almost_equal as _almost_equal
|
|
@@ -81,13 +82,41 @@ class Failure(_t.List[str]):
|
|
|
81
82
|
for index in range(len(func)):
|
|
82
83
|
arg = func.signature.args.get(index)
|
|
83
84
|
doc = func.docstring.args.get(index)
|
|
85
|
+
self._no_description(doc)
|
|
84
86
|
self._description_syntax(doc)
|
|
85
87
|
self._indent_syntax(doc)
|
|
86
|
-
|
|
87
|
-
if
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
|
|
89
|
+
if doc.name == _UNNAMED:
|
|
90
|
+
# if the parameter does not have a name, but exists,
|
|
91
|
+
# then it must be incorrectly documented
|
|
92
|
+
# prior implementation relied on the docstring
|
|
93
|
+
# parameter equalling the signature parameter
|
|
94
|
+
self._add(_E[107])
|
|
95
|
+
|
|
96
|
+
elif arg != doc:
|
|
97
|
+
if any(
|
|
98
|
+
arg.name == i.name for i in self._func.docstring.args
|
|
99
|
+
) or any(
|
|
100
|
+
doc.name == i.name for i in self._func.signature.args
|
|
101
|
+
):
|
|
102
|
+
# parameters out of order
|
|
103
|
+
self._add(_E[101])
|
|
104
|
+
|
|
105
|
+
elif (
|
|
106
|
+
arg.name is not None
|
|
107
|
+
and doc.name is not None
|
|
108
|
+
and not self._errors
|
|
109
|
+
and _almost_equal(
|
|
110
|
+
arg.name, doc.name, _MIN_MATCH, _MAX_MATCH
|
|
111
|
+
)
|
|
112
|
+
):
|
|
113
|
+
# spelling error found in documented parameter
|
|
114
|
+
self._add(_E[112])
|
|
115
|
+
|
|
116
|
+
elif arg.name is not None and doc.name is not None:
|
|
117
|
+
# documented parameter not equal to its
|
|
118
|
+
# respective argument
|
|
119
|
+
self._add(_E[110])
|
|
91
120
|
|
|
92
121
|
self.sort()
|
|
93
122
|
|
|
@@ -103,12 +132,6 @@ class Failure(_t.List[str]):
|
|
|
103
132
|
if value not in self._disable and message not in self:
|
|
104
133
|
super().append(message)
|
|
105
134
|
|
|
106
|
-
def _order(self, sig: _Param, doc: _Param) -> None:
|
|
107
|
-
if any(sig.name == i.name for i in self._func.docstring.args) or any(
|
|
108
|
-
doc.name == i.name for i in self._func.signature.args
|
|
109
|
-
):
|
|
110
|
-
self._add(_E[101])
|
|
111
|
-
|
|
112
135
|
def _exists(self) -> None:
|
|
113
136
|
# pop the parameters that do not exist so that they are excluded
|
|
114
137
|
# from further analysis, that way there are no additional, and
|
|
@@ -132,6 +155,18 @@ class Failure(_t.List[str]):
|
|
|
132
155
|
|
|
133
156
|
def _missing(self) -> None:
|
|
134
157
|
if len(self._func.signature.args) > len(self._func.docstring.args):
|
|
158
|
+
# append the parameters that are missing so that they are
|
|
159
|
+
# included in further analysis, that way there are no
|
|
160
|
+
# additional, and redundant, errors
|
|
161
|
+
# this will ensure that both signature and docstring are
|
|
162
|
+
# equal in length, with all parameters that are not
|
|
163
|
+
# documented accounted for
|
|
164
|
+
for count, arg in enumerate(self._func.signature.args, 1):
|
|
165
|
+
if count > len(self._func.docstring.args):
|
|
166
|
+
self._func.docstring.args.append(
|
|
167
|
+
_Param(arg.kind, arg.name, _VALID_DESCRIPTION, 0)
|
|
168
|
+
)
|
|
169
|
+
# params-missing
|
|
135
170
|
self._add(_E[103])
|
|
136
171
|
|
|
137
172
|
def _duplicates(self) -> None:
|
|
@@ -185,32 +220,10 @@ class Failure(_t.List[str]):
|
|
|
185
220
|
|
|
186
221
|
self._add(_E[105], hint=hint)
|
|
187
222
|
|
|
188
|
-
def _incorrect(self, doc: _Param) -> None:
|
|
189
|
-
# if the parameter does not have a name, but exists, then it
|
|
190
|
-
# must be incorrectly documented
|
|
191
|
-
# prior implementation relied on the docstring parameter
|
|
192
|
-
# equalling the signature parameter
|
|
193
|
-
if doc.name == _UNNAMED:
|
|
194
|
-
self._add(_E[107])
|
|
195
|
-
|
|
196
|
-
# final catch-all
|
|
197
|
-
def _not_equal(self, sig: _Param, doc: _Param) -> None:
|
|
198
|
-
if sig.name is not None and doc.name is not None and not self._errors:
|
|
199
|
-
self._add(_E[110])
|
|
200
|
-
|
|
201
223
|
def _class_return(self) -> None:
|
|
202
224
|
if self._func.docstring.returns and self._func.isinit:
|
|
203
225
|
self._add(_E[111], hint=True)
|
|
204
226
|
|
|
205
|
-
def _misspelled(self, sig: _Param, doc: _Param) -> None:
|
|
206
|
-
if (
|
|
207
|
-
sig.name is not None
|
|
208
|
-
and doc.name is not None
|
|
209
|
-
and not self._errors
|
|
210
|
-
and _almost_equal(sig.name, doc.name, _MIN_MATCH, _MAX_MATCH)
|
|
211
|
-
):
|
|
212
|
-
self._add(_E[112])
|
|
213
|
-
|
|
214
227
|
def _description_syntax(self, doc: _Param) -> None:
|
|
215
228
|
if doc.description is not None and not doc.description.startswith(" "):
|
|
216
229
|
self._add(_E[115])
|
|
@@ -219,6 +232,10 @@ class Failure(_t.List[str]):
|
|
|
219
232
|
if doc.indent > 0:
|
|
220
233
|
self._add(_E[116])
|
|
221
234
|
|
|
235
|
+
def _no_description(self, doc: _Param) -> None:
|
|
236
|
+
if doc.description is None:
|
|
237
|
+
self._add(_E[117])
|
|
238
|
+
|
|
222
239
|
def _invalid_directive(self) -> None:
|
|
223
240
|
for comment in self._func.comments:
|
|
224
241
|
if not comment.isvalid:
|
|
@@ -17,6 +17,9 @@ import sphinx.ext.napoleon as _s
|
|
|
17
17
|
# no function will accidentally have this name
|
|
18
18
|
UNNAMED = -1000
|
|
19
19
|
|
|
20
|
+
# an example of valid parameter description
|
|
21
|
+
VALID_DESCRIPTION = " A valid description."
|
|
22
|
+
|
|
20
23
|
|
|
21
24
|
# noinspection PyTypeChecker
|
|
22
25
|
class _GoogleDocstring(str):
|
|
@@ -134,7 +137,9 @@ class Param(_t.NamedTuple):
|
|
|
134
137
|
class _Matches(_t.List[Param]):
|
|
135
138
|
_pattern = _re.compile(":(.*?):")
|
|
136
139
|
|
|
137
|
-
def __init__(
|
|
140
|
+
def __init__(
|
|
141
|
+
self, string: str, indent_anomaly: bool, missing_descriptions: bool
|
|
142
|
+
) -> None:
|
|
138
143
|
super().__init__()
|
|
139
144
|
for line in string.splitlines():
|
|
140
145
|
strip_line = line.lstrip()
|
|
@@ -152,7 +157,9 @@ class _Matches(_t.List[Param]):
|
|
|
152
157
|
name = UNNAMED
|
|
153
158
|
|
|
154
159
|
if len(match) > 1:
|
|
155
|
-
|
|
160
|
+
second = match[1]
|
|
161
|
+
if second != "" or not missing_descriptions:
|
|
162
|
+
description = second
|
|
156
163
|
|
|
157
164
|
super().append(
|
|
158
165
|
Param(
|
|
@@ -326,6 +333,24 @@ class Docstring(_Stub):
|
|
|
326
333
|
# look for spaces in odd intervals
|
|
327
334
|
return bool(any(i % 2 != 0 for i in leading_spaces))
|
|
328
335
|
|
|
336
|
+
@staticmethod
|
|
337
|
+
def _missing_descriptions(string: str) -> bool:
|
|
338
|
+
# find out if parameter is missing a description
|
|
339
|
+
new = ""
|
|
340
|
+
for line in string.strip().splitlines()[2:]:
|
|
341
|
+
line = line.lstrip()
|
|
342
|
+
if not line.startswith(":"):
|
|
343
|
+
# it is not a parameter, it is a next line description
|
|
344
|
+
# append the next entry to the same line
|
|
345
|
+
new = f"{new[:-1]} "
|
|
346
|
+
new += f"{line}\n"
|
|
347
|
+
if not new:
|
|
348
|
+
return True
|
|
349
|
+
|
|
350
|
+
# if it ends with a colon, it's a parameter without a
|
|
351
|
+
# description
|
|
352
|
+
return any(i.endswith(":") for i in new.splitlines())
|
|
353
|
+
|
|
329
354
|
def __init__(
|
|
330
355
|
self,
|
|
331
356
|
node: _ast.Const | None = None,
|
|
@@ -336,7 +361,11 @@ class Docstring(_Stub):
|
|
|
336
361
|
self._string = None
|
|
337
362
|
if node is not None:
|
|
338
363
|
self._string = _RawDocstring(node.value)
|
|
339
|
-
for i in _Matches(
|
|
364
|
+
for i in _Matches(
|
|
365
|
+
self._string,
|
|
366
|
+
self._indent_anomaly(node.value),
|
|
367
|
+
self._missing_descriptions(node.value),
|
|
368
|
+
):
|
|
340
369
|
self._args.append(i)
|
|
341
370
|
|
|
342
371
|
self._returns = self._string is not None and bool(
|
|
@@ -1,26 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
2
|
docsig.messages
|
|
3
3
|
===============
|
|
4
|
-
| E101: parameters out of order
|
|
5
|
-
| E102: includes parameters that do not exist
|
|
6
|
-
| E103: parameters missing
|
|
7
|
-
| E104: return statement documented for None
|
|
8
|
-
| E105: return missing from docstring
|
|
9
|
-
| E106: duplicate parameters found
|
|
10
|
-
| E107: parameter appears to be incorrectly documented
|
|
11
|
-
| E108: return statement documented for property
|
|
12
|
-
| E109: cannot determine whether a return statement should exist or not
|
|
13
|
-
| E110: documented parameter not equal to its respective argument
|
|
14
|
-
| E111: return statement documented for class
|
|
15
|
-
| E112: spelling error found in documented parameter
|
|
16
|
-
| E113: function is missing a docstring
|
|
17
|
-
| E114: class is missing a docstring
|
|
18
|
-
| E115: syntax error in description
|
|
19
|
-
| E116: param not indented correctly
|
|
20
|
-
| E201: unknown module comment directive '{directive}'
|
|
21
|
-
| E202: unknown inline comment directive '{directive}'
|
|
22
|
-
| E203: unknown module comment option for {directive} '{option}'
|
|
23
|
-
| E204: unknown inline comment option for {directive} '{option}'
|
|
24
4
|
"""
|
|
25
5
|
|
|
26
6
|
from __future__ import annotations
|
|
@@ -157,7 +137,7 @@ E = MessageMap(
|
|
|
157
137
|
),
|
|
158
138
|
109: Message(
|
|
159
139
|
"E109",
|
|
160
|
-
"cannot determine whether a return statement should exist
|
|
140
|
+
"cannot determine whether a return statement should exist",
|
|
161
141
|
"confirm-return-needed",
|
|
162
142
|
"annotate type to indicate whether return documentation needed",
|
|
163
143
|
),
|
|
@@ -197,6 +177,11 @@ E = MessageMap(
|
|
|
197
177
|
"param not indented correctly",
|
|
198
178
|
"incorrect-indent",
|
|
199
179
|
),
|
|
180
|
+
117: Message(
|
|
181
|
+
"E117",
|
|
182
|
+
"description missing from parameter",
|
|
183
|
+
"description-missing",
|
|
184
|
+
),
|
|
200
185
|
# E2xx: Config
|
|
201
186
|
201: Message(
|
|
202
187
|
"E201",
|
|
@@ -5,15 +5,6 @@ requires = [
|
|
|
5
5
|
]
|
|
6
6
|
|
|
7
7
|
[tool.black]
|
|
8
|
-
exclude = '''
|
|
9
|
-
/(
|
|
10
|
-
| \.git
|
|
11
|
-
| \.mypy_cache
|
|
12
|
-
| _build
|
|
13
|
-
| build
|
|
14
|
-
| dist
|
|
15
|
-
)/
|
|
16
|
-
'''
|
|
17
8
|
line-length = 79
|
|
18
9
|
|
|
19
10
|
[tool.codespell]
|
|
@@ -32,10 +23,21 @@ omit = [
|
|
|
32
23
|
"whitelist.py"
|
|
33
24
|
]
|
|
34
25
|
|
|
26
|
+
[tool.deptry.per_rule_ignores]
|
|
27
|
+
DEP004 = ["yaml"]
|
|
28
|
+
|
|
29
|
+
[tool.docformatter]
|
|
30
|
+
in-place = true
|
|
31
|
+
wrap-summaries = 72
|
|
32
|
+
|
|
35
33
|
[tool.docsig]
|
|
36
34
|
check-class = true
|
|
37
35
|
check-protected-class-methods = true
|
|
38
36
|
|
|
37
|
+
[tool.flynt]
|
|
38
|
+
line-length = 79
|
|
39
|
+
transform-concats = true
|
|
40
|
+
|
|
39
41
|
[tool.isort]
|
|
40
42
|
ensure_newline_before_comments = true
|
|
41
43
|
force_grid_wrap = 0
|
|
@@ -45,6 +47,14 @@ multi_line_output = 3
|
|
|
45
47
|
profile = "black"
|
|
46
48
|
use_parentheses = true
|
|
47
49
|
|
|
50
|
+
[tool.mypy]
|
|
51
|
+
exclude = [
|
|
52
|
+
"whitelist\\.py"
|
|
53
|
+
]
|
|
54
|
+
ignore_missing_imports = true
|
|
55
|
+
install_types = true
|
|
56
|
+
non_interactive = true
|
|
57
|
+
|
|
48
58
|
[tool.poetry]
|
|
49
59
|
authors = [
|
|
50
60
|
"jshwi <stephen@jshwisolutions.com>"
|
|
@@ -66,7 +76,7 @@ maintainers = [
|
|
|
66
76
|
name = "docsig"
|
|
67
77
|
readme = "README.rst"
|
|
68
78
|
repository = "https://github.com/jshwi/docsig"
|
|
69
|
-
version = "0.
|
|
79
|
+
version = "0.54.0"
|
|
70
80
|
|
|
71
81
|
[tool.poetry.dependencies]
|
|
72
82
|
Sphinx = "^7.0.0"
|
|
@@ -76,57 +86,62 @@ click = "^8.1.7"
|
|
|
76
86
|
pathspec = "^0.12.1"
|
|
77
87
|
python = "^3.8"
|
|
78
88
|
|
|
79
|
-
[tool.poetry.dev
|
|
89
|
+
[tool.poetry.group.dev.dependencies]
|
|
90
|
+
black = "^24.4.2"
|
|
80
91
|
bump2version = "^1.0.1"
|
|
81
92
|
deptry = "^0.16.1"
|
|
93
|
+
docformatter = "^1.7.5"
|
|
94
|
+
flynt = "^1.0.1"
|
|
95
|
+
isort = "^5.13.2"
|
|
96
|
+
mypy = "^1.10.0"
|
|
97
|
+
pre-commit = "^3.3.3"
|
|
98
|
+
pylint = "^3.1.0"
|
|
99
|
+
tox = "^4.15.0"
|
|
100
|
+
vulture = "^2.11"
|
|
101
|
+
|
|
102
|
+
[tool.poetry.group.docs.dependencies]
|
|
82
103
|
furo = "^2024.4.27"
|
|
83
|
-
ipython = "^8.12.0"
|
|
84
104
|
myst-parser = "^3.0.1"
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
105
|
+
pytest = "^8.2.0"
|
|
106
|
+
pyyaml = "^6.0.1"
|
|
107
|
+
sphinx-copybutton = "^0.5.2"
|
|
108
|
+
sphinx-markdown-builder = ">=0.5.5,<0.7.0"
|
|
109
|
+
templatest = "^0.10.1"
|
|
110
|
+
|
|
111
|
+
[tool.poetry.group.tests.dependencies]
|
|
112
|
+
pytest = "^8.2.0"
|
|
113
|
+
pytest-benchmark = "^4.0.0"
|
|
114
|
+
pytest-cov = "^5.0.0"
|
|
115
|
+
pytest-randomly = "^3.15.0"
|
|
88
116
|
pytest-sugar = "^1.0.0"
|
|
89
117
|
pytest-xdist = "^3.6.1"
|
|
90
|
-
restview = "^3.0.0"
|
|
91
|
-
sphinx-copybutton = "^0.5.2"
|
|
92
|
-
sphinx-toolbox = "^3.5.0"
|
|
93
118
|
templatest = "^0.10.1"
|
|
94
|
-
tox = "^4.15.0"
|
|
95
119
|
|
|
96
120
|
[tool.poetry.scripts]
|
|
97
121
|
docsig = "docsig.__main__:main"
|
|
98
122
|
|
|
99
|
-
[tool.
|
|
100
|
-
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"format",
|
|
104
|
-
"format-docs",
|
|
105
|
-
"format-str",
|
|
106
|
-
"imports",
|
|
107
|
-
"lint",
|
|
108
|
-
"test",
|
|
109
|
-
"typecheck",
|
|
110
|
-
"unused",
|
|
111
|
-
"whitelist"
|
|
123
|
+
[tool.pylint.options]
|
|
124
|
+
disable = [
|
|
125
|
+
"consider-using-f-string",
|
|
126
|
+
"fixme"
|
|
112
127
|
]
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
)$
|
|
119
|
-
'''
|
|
128
|
+
ignore-patterns = [
|
|
129
|
+
'conf\.py',
|
|
130
|
+
'whitelist\.py'
|
|
131
|
+
]
|
|
132
|
+
min-similarity-lines = 9
|
|
120
133
|
|
|
121
134
|
[tool.pytest.ini_options]
|
|
122
135
|
addopts = [
|
|
123
136
|
"--color=yes",
|
|
124
137
|
"--cov-report=term-missing",
|
|
125
138
|
"--durations=5",
|
|
126
|
-
"-n=auto",
|
|
127
139
|
"-vv"
|
|
128
140
|
]
|
|
129
141
|
filterwarnings = "ignore::DeprecationWarning"
|
|
142
|
+
markers = [
|
|
143
|
+
"benchmark: Marks tests as benchmarks"
|
|
144
|
+
]
|
|
130
145
|
norecursedirs = [
|
|
131
146
|
".git",
|
|
132
147
|
".idea",
|
|
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
|