docstringify 0.7.0__tar.gz → 1.0.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.
- {docstringify-0.7.0 → docstringify-1.0.0}/.pre-commit-config.yaml +4 -4
- {docstringify-0.7.0 → docstringify-1.0.0}/PKG-INFO +30 -10
- {docstringify-0.7.0 → docstringify-1.0.0}/README.md +28 -8
- {docstringify-0.7.0 → docstringify-1.0.0}/pyproject.toml +1 -1
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify/__init__.py +1 -1
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify/converters/base.py +13 -6
- docstringify-1.0.0/src/docstringify/exceptions.py +25 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify/nodes/function.py +41 -17
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify/traversal/transformer.py +81 -14
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify/traversal/visitor.py +8 -8
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify.egg-info/PKG-INFO +30 -10
- docstringify-0.7.0/src/docstringify/exceptions.py +0 -6
- {docstringify-0.7.0 → docstringify-1.0.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/.github/dependabot.yml +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/.github/pull_request_template.md +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/.github/release.yml +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/.github/workflows/ci.yml +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/.github/workflows/greetings.yml +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/.github/workflows/pypi-publish.yml +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/.gitignore +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/.pre-commit-hooks.yaml +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/CODE_OF_CONDUCT.md +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/CONTRIBUTING.md +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/LICENSE +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/setup.cfg +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify/__main__.py +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify/cli.py +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify/components.py +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify/converters/__init__.py +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify/converters/google.py +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify/converters/numpydoc.py +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify/converters/stub.py +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify/nodes/__init__.py +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify/nodes/base.py +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify/traversal/__init__.py +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify.egg-info/SOURCES.txt +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify.egg-info/dependency_links.txt +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify.egg-info/entry_points.txt +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify.egg-info/requires.txt +0 -0
- {docstringify-0.7.0 → docstringify-1.0.0}/src/docstringify.egg-info/top_level.txt +0 -0
|
@@ -12,20 +12,20 @@ repos:
|
|
|
12
12
|
- id: trailing-whitespace
|
|
13
13
|
|
|
14
14
|
- repo: https://github.com/stefmolin/docstringify
|
|
15
|
-
rev: 0.
|
|
15
|
+
rev: 0.7.0
|
|
16
16
|
hooks:
|
|
17
17
|
- id: docstringify
|
|
18
18
|
args: [--make-changes-inplace=numpydoc]
|
|
19
19
|
|
|
20
20
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
21
|
-
rev: v0.11.
|
|
21
|
+
rev: v0.11.10
|
|
22
22
|
hooks:
|
|
23
|
-
- id: ruff
|
|
23
|
+
- id: ruff-check
|
|
24
24
|
args: [--fix, --exit-non-zero-on-fix, --show-fixes]
|
|
25
25
|
- id: ruff-format
|
|
26
26
|
|
|
27
27
|
- repo: https://github.com/tox-dev/pyproject-fmt
|
|
28
|
-
rev: v2.
|
|
28
|
+
rev: v2.6.0
|
|
29
29
|
hooks:
|
|
30
30
|
- id: pyproject-fmt
|
|
31
31
|
args: [--keep-full-version, --no-print-diff]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: docstringify
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: Flag missing docstrings and, optionally, generate them from signatures and type annotations.
|
|
5
5
|
Author-email: Stefanie Molin <docstringify@stefaniemolin.com>
|
|
6
6
|
License: MIT License
|
|
@@ -29,7 +29,7 @@ Project-URL: Documentation, https://github.com/stefmolin/docstringify
|
|
|
29
29
|
Project-URL: Homepage, https://github.com/stefmolin/docstringify
|
|
30
30
|
Project-URL: Source, https://github.com/stefmolin/docstringify
|
|
31
31
|
Keywords: ast,docstring generation,google-style docstring,numpydoc-style docstring,pre-commit
|
|
32
|
-
Classifier: Development Status ::
|
|
32
|
+
Classifier: Development Status :: 4 - Beta
|
|
33
33
|
Classifier: Programming Language :: Python
|
|
34
34
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
35
35
|
Classifier: Programming Language :: Python :: 3.9
|
|
@@ -103,11 +103,11 @@ You can use Docstringify in three modes:
|
|
|
103
103
|
|
|
104
104
|
### Pre-commit hook
|
|
105
105
|
|
|
106
|
-
Add the following to your `.pre-commit-config.yaml` file to block commits with missing docstrings
|
|
106
|
+
Add the following to your `.pre-commit-config.yaml` file to block commits with missing docstrings before any formatters like `ruff`:
|
|
107
107
|
|
|
108
108
|
```yaml
|
|
109
109
|
- repo: https://github.com/stefmolin/docstringify
|
|
110
|
-
rev: 0.
|
|
110
|
+
rev: 1.0.0
|
|
111
111
|
hooks:
|
|
112
112
|
- id: docstringify
|
|
113
113
|
```
|
|
@@ -116,17 +116,17 @@ By default, all docstrings are required. If you want to be more lenient, you can
|
|
|
116
116
|
|
|
117
117
|
```yaml
|
|
118
118
|
- repo: https://github.com/stefmolin/docstringify
|
|
119
|
-
rev: 0.
|
|
119
|
+
rev: 1.0.0
|
|
120
120
|
hooks:
|
|
121
121
|
- id: docstringify
|
|
122
122
|
args: [--threshold=0.75]
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
-
If you would like to see suggested docstring templates (inferred from type annotations for functions and methods), provide the `--suggest-changes` argument, along with the docstring style you want to use (options are `google`, `numpydoc`, and `stub`). Here, we ask for
|
|
125
|
+
If you would like to see suggested docstring templates (inferred from type annotations for functions and methods), provide the `--suggest-changes` argument, along with the docstring style you want to use (options are `google`, `numpydoc`, and `stub`). Here, we ask for stub suggestions (just single lines of `"""__description__"""`):
|
|
126
126
|
|
|
127
127
|
```yaml
|
|
128
128
|
- repo: https://github.com/stefmolin/docstringify
|
|
129
|
-
rev: 0.
|
|
129
|
+
rev: 1.0.0
|
|
130
130
|
hooks:
|
|
131
131
|
- id: docstringify
|
|
132
132
|
args: [--suggest-changes=numpydoc]
|
|
@@ -136,13 +136,21 @@ Use `--make-changes` to create a copy of each file with docstring templates. Her
|
|
|
136
136
|
|
|
137
137
|
```yaml
|
|
138
138
|
- repo: https://github.com/stefmolin/docstringify
|
|
139
|
-
rev: 0.
|
|
139
|
+
rev: 1.0.0
|
|
140
140
|
hooks:
|
|
141
141
|
- id: docstringify
|
|
142
142
|
args: [--make-changes=google]
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
-
If you want the changes to be made in place, change `--make-changes` to `--make-changes-inplace` – make sure you only operate on files that are in version control with this setting.
|
|
145
|
+
If you want the changes to be made in place, change `--make-changes` to `--make-changes-inplace` – make sure you only operate on files that are in version control with this setting. Here, we ask for [numpydoc-style docstring](https://numpydoc.readthedocs.io/en/latest/format.html#) suggestions:
|
|
146
|
+
|
|
147
|
+
```yaml
|
|
148
|
+
- repo: https://github.com/stefmolin/docstringify
|
|
149
|
+
rev: 1.0.0
|
|
150
|
+
hooks:
|
|
151
|
+
- id: docstringify
|
|
152
|
+
args: [--make-changes-inplace=numpydoc]
|
|
153
|
+
```
|
|
146
154
|
|
|
147
155
|
Be sure to check out the [pre-commit documentation](https://pre-commit.com/#pre-commit-configyaml---hooks) for additional configuration options.
|
|
148
156
|
|
|
@@ -216,7 +224,19 @@ test.say_hello is missing a docstring
|
|
|
216
224
|
Docstring templates written to /.../test_docstringify.py
|
|
217
225
|
```
|
|
218
226
|
|
|
219
|
-
If you want to overwrite the file with the edits, pass `overwrite=True` to `DocstringTransformer()
|
|
227
|
+
If you want to overwrite the file with the edits, pass `overwrite=True` to `DocstringTransformer()`:
|
|
228
|
+
|
|
229
|
+
```pycon
|
|
230
|
+
>>> from docstringify.converters import GoogleDocstringConverter
|
|
231
|
+
>>> from docstringify.traversal import DocstringTransformer
|
|
232
|
+
>>> transformer = DocstringTransformer(
|
|
233
|
+
... 'test.py', converter=GoogleDocstringConverter, overwrite=True
|
|
234
|
+
... )
|
|
235
|
+
>>> transformer.process_file()
|
|
236
|
+
test is missing a docstring
|
|
237
|
+
test.say_hello is missing a docstring
|
|
238
|
+
Docstring templates written to /.../test.py
|
|
239
|
+
```
|
|
220
240
|
|
|
221
241
|
## Contributing
|
|
222
242
|
|
|
@@ -54,11 +54,11 @@ You can use Docstringify in three modes:
|
|
|
54
54
|
|
|
55
55
|
### Pre-commit hook
|
|
56
56
|
|
|
57
|
-
Add the following to your `.pre-commit-config.yaml` file to block commits with missing docstrings
|
|
57
|
+
Add the following to your `.pre-commit-config.yaml` file to block commits with missing docstrings before any formatters like `ruff`:
|
|
58
58
|
|
|
59
59
|
```yaml
|
|
60
60
|
- repo: https://github.com/stefmolin/docstringify
|
|
61
|
-
rev: 0.
|
|
61
|
+
rev: 1.0.0
|
|
62
62
|
hooks:
|
|
63
63
|
- id: docstringify
|
|
64
64
|
```
|
|
@@ -67,17 +67,17 @@ By default, all docstrings are required. If you want to be more lenient, you can
|
|
|
67
67
|
|
|
68
68
|
```yaml
|
|
69
69
|
- repo: https://github.com/stefmolin/docstringify
|
|
70
|
-
rev: 0.
|
|
70
|
+
rev: 1.0.0
|
|
71
71
|
hooks:
|
|
72
72
|
- id: docstringify
|
|
73
73
|
args: [--threshold=0.75]
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
If you would like to see suggested docstring templates (inferred from type annotations for functions and methods), provide the `--suggest-changes` argument, along with the docstring style you want to use (options are `google`, `numpydoc`, and `stub`). Here, we ask for
|
|
76
|
+
If you would like to see suggested docstring templates (inferred from type annotations for functions and methods), provide the `--suggest-changes` argument, along with the docstring style you want to use (options are `google`, `numpydoc`, and `stub`). Here, we ask for stub suggestions (just single lines of `"""__description__"""`):
|
|
77
77
|
|
|
78
78
|
```yaml
|
|
79
79
|
- repo: https://github.com/stefmolin/docstringify
|
|
80
|
-
rev: 0.
|
|
80
|
+
rev: 1.0.0
|
|
81
81
|
hooks:
|
|
82
82
|
- id: docstringify
|
|
83
83
|
args: [--suggest-changes=numpydoc]
|
|
@@ -87,13 +87,21 @@ Use `--make-changes` to create a copy of each file with docstring templates. Her
|
|
|
87
87
|
|
|
88
88
|
```yaml
|
|
89
89
|
- repo: https://github.com/stefmolin/docstringify
|
|
90
|
-
rev: 0.
|
|
90
|
+
rev: 1.0.0
|
|
91
91
|
hooks:
|
|
92
92
|
- id: docstringify
|
|
93
93
|
args: [--make-changes=google]
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
If you want the changes to be made in place, change `--make-changes` to `--make-changes-inplace` – make sure you only operate on files that are in version control with this setting.
|
|
96
|
+
If you want the changes to be made in place, change `--make-changes` to `--make-changes-inplace` – make sure you only operate on files that are in version control with this setting. Here, we ask for [numpydoc-style docstring](https://numpydoc.readthedocs.io/en/latest/format.html#) suggestions:
|
|
97
|
+
|
|
98
|
+
```yaml
|
|
99
|
+
- repo: https://github.com/stefmolin/docstringify
|
|
100
|
+
rev: 1.0.0
|
|
101
|
+
hooks:
|
|
102
|
+
- id: docstringify
|
|
103
|
+
args: [--make-changes-inplace=numpydoc]
|
|
104
|
+
```
|
|
97
105
|
|
|
98
106
|
Be sure to check out the [pre-commit documentation](https://pre-commit.com/#pre-commit-configyaml---hooks) for additional configuration options.
|
|
99
107
|
|
|
@@ -167,7 +175,19 @@ test.say_hello is missing a docstring
|
|
|
167
175
|
Docstring templates written to /.../test_docstringify.py
|
|
168
176
|
```
|
|
169
177
|
|
|
170
|
-
If you want to overwrite the file with the edits, pass `overwrite=True` to `DocstringTransformer()
|
|
178
|
+
If you want to overwrite the file with the edits, pass `overwrite=True` to `DocstringTransformer()`:
|
|
179
|
+
|
|
180
|
+
```pycon
|
|
181
|
+
>>> from docstringify.converters import GoogleDocstringConverter
|
|
182
|
+
>>> from docstringify.traversal import DocstringTransformer
|
|
183
|
+
>>> transformer = DocstringTransformer(
|
|
184
|
+
... 'test.py', converter=GoogleDocstringConverter, overwrite=True
|
|
185
|
+
... )
|
|
186
|
+
>>> transformer.process_file()
|
|
187
|
+
test is missing a docstring
|
|
188
|
+
test.say_hello is missing a docstring
|
|
189
|
+
Docstring templates written to /.../test.py
|
|
190
|
+
```
|
|
171
191
|
|
|
172
192
|
## Contributing
|
|
173
193
|
|
|
@@ -23,7 +23,7 @@ authors = [
|
|
|
23
23
|
]
|
|
24
24
|
requires-python = ">=3.9"
|
|
25
25
|
classifiers = [
|
|
26
|
-
"Development Status ::
|
|
26
|
+
"Development Status :: 4 - Beta",
|
|
27
27
|
"Programming Language :: Python",
|
|
28
28
|
"Programming Language :: Python :: 3 :: Only",
|
|
29
29
|
"Programming Language :: Python :: 3.9",
|
|
@@ -184,7 +184,9 @@ class DocstringConverter(ABC):
|
|
|
184
184
|
return self._returns_section_template.format(returns=return_text)
|
|
185
185
|
return ''
|
|
186
186
|
|
|
187
|
-
def format_docstring(
|
|
187
|
+
def format_docstring(
|
|
188
|
+
self, docstring: str | list[str], indent: int, quote: bool | None = None
|
|
189
|
+
) -> str:
|
|
188
190
|
"""
|
|
189
191
|
Format the docstring with the requested level of indentation and surrounding
|
|
190
192
|
triple quotes, if requested.
|
|
@@ -195,23 +197,28 @@ class DocstringConverter(ABC):
|
|
|
195
197
|
The docstring as a string or list of strings that form lines in the docstring.
|
|
196
198
|
indent : int
|
|
197
199
|
The number of spaces by which to indent the docstring.
|
|
200
|
+
quote : bool | None, default=None
|
|
201
|
+
Whether to surround the docstring in triple quotes, passing this overrides
|
|
202
|
+
the ``quote`` setting specified upon initialization.
|
|
198
203
|
|
|
199
204
|
Returns
|
|
200
205
|
-------
|
|
201
206
|
str
|
|
202
207
|
The docstring indented to ``indent`` spaces will be returned either as a
|
|
203
|
-
quoted docstring if the converter was initialized with ``quote=True
|
|
204
|
-
|
|
208
|
+
quoted docstring if the converter was initialized with ``quote=True`` or
|
|
209
|
+
``quote=True`` was passed when calling this method, or an unquoted
|
|
210
|
+
docstring otherwise.
|
|
205
211
|
"""
|
|
206
|
-
|
|
212
|
+
quote_char = '"""' if quote or self._quote else ''
|
|
207
213
|
prefix = sep = ''
|
|
214
|
+
|
|
208
215
|
if isinstance(docstring, str):
|
|
209
|
-
docstring = [
|
|
216
|
+
docstring = [quote_char, docstring, quote_char]
|
|
210
217
|
elif isinstance(docstring, list):
|
|
211
218
|
if len(docstring) > 1:
|
|
212
219
|
prefix = ' ' * indent if indent else ''
|
|
213
220
|
sep = '\n'
|
|
214
|
-
docstring = [
|
|
221
|
+
docstring = [quote_char, *docstring, quote_char or f'{prefix}']
|
|
215
222
|
else:
|
|
216
223
|
raise InvalidDocstringError(type(docstring).__name__)
|
|
217
224
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Docstringify exceptions."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class EmptyDocstringError(ValueError):
|
|
5
|
+
"""
|
|
6
|
+
Raised when trying to write the modified source code back to a file if a docstring
|
|
7
|
+
is somehow empty.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
def __init__(self) -> None:
|
|
11
|
+
super().__init__('Docstring is empty')
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class InvalidDocstringError(TypeError):
|
|
15
|
+
"""
|
|
16
|
+
Raised when trying to create a docstring with invalid input.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
docstring_class : str
|
|
21
|
+
The class of the docstring object. This is used for the error message.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, docstring_class: str) -> None:
|
|
25
|
+
super().__init__(f'Expected str or list[str] docstring, got {docstring_class}')
|
|
@@ -23,11 +23,17 @@ class FunctionDocstringNode(DocstringNode):
|
|
|
23
23
|
parent: DocstringNode,
|
|
24
24
|
) -> None:
|
|
25
25
|
super().__init__(node, module_name, source_code, parent)
|
|
26
|
+
|
|
26
27
|
self.decorators: list[str] = [
|
|
27
|
-
|
|
28
|
+
decorator
|
|
29
|
+
for decorator_node in node.decorator_list
|
|
30
|
+
if (decorator := self.get_source_segment(decorator_node))
|
|
28
31
|
]
|
|
29
32
|
|
|
30
|
-
self.
|
|
33
|
+
self.is_overload: bool = (
|
|
34
|
+
'overload' in self.decorators or 'typing.overload' in self.decorators
|
|
35
|
+
)
|
|
36
|
+
self.is_method: bool = self.parent is not None and isinstance(
|
|
31
37
|
self.parent.ast_node, ast.ClassDef
|
|
32
38
|
)
|
|
33
39
|
self.is_abstract_method: bool = self.is_method and (
|
|
@@ -39,15 +45,26 @@ class FunctionDocstringNode(DocstringNode):
|
|
|
39
45
|
self.is_method and 'staticmethod' in self.decorators
|
|
40
46
|
)
|
|
41
47
|
self.is_instance_method: bool = (
|
|
42
|
-
self.is_method
|
|
48
|
+
self.is_method
|
|
49
|
+
and (not self.is_class_method)
|
|
50
|
+
and (not self.is_static_method)
|
|
43
51
|
)
|
|
44
52
|
|
|
45
53
|
# don't require docstring for the __init__ method if the class has a docstring
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
# or if the function/method is just a typing.overload signature
|
|
55
|
+
self.docstring_required: bool = (
|
|
56
|
+
not (
|
|
57
|
+
self.is_method
|
|
58
|
+
and self.name == '__init__'
|
|
59
|
+
and self.parent is not None
|
|
60
|
+
and self.parent.docstring
|
|
61
|
+
)
|
|
62
|
+
and not self.is_overload
|
|
48
63
|
)
|
|
49
64
|
|
|
50
|
-
self.arguments: ast.arguments
|
|
65
|
+
self.arguments: ast.arguments = node.args
|
|
66
|
+
|
|
67
|
+
self.returns: ast.AST | None = node.returns
|
|
51
68
|
self.return_annotation: str | None = self._extract_return_annotation()
|
|
52
69
|
self.return_statements: list[ast.Return] = []
|
|
53
70
|
|
|
@@ -64,7 +81,8 @@ class FunctionDocstringNode(DocstringNode):
|
|
|
64
81
|
|
|
65
82
|
return (
|
|
66
83
|
f'"{default_value}"'
|
|
67
|
-
if isinstance(default_value, str)
|
|
84
|
+
if isinstance(default_value, str)
|
|
85
|
+
and (not default_value.startswith('`'))
|
|
68
86
|
else default_value
|
|
69
87
|
)
|
|
70
88
|
return NO_DEFAULT
|
|
@@ -95,8 +113,9 @@ class FunctionDocstringNode(DocstringNode):
|
|
|
95
113
|
|
|
96
114
|
def _extract_positional_args(self) -> list[Parameter]:
|
|
97
115
|
if (default_count := len(positional_defaults := self.arguments.defaults)) < (
|
|
98
|
-
positional_arguments_count :=
|
|
99
|
-
|
|
116
|
+
positional_arguments_count := (
|
|
117
|
+
len(self.arguments.posonlyargs) + len(self.arguments.args)
|
|
118
|
+
)
|
|
100
119
|
):
|
|
101
120
|
positional_defaults = [NO_DEFAULT] * (
|
|
102
121
|
positional_arguments_count - default_count
|
|
@@ -155,23 +174,28 @@ class FunctionDocstringNode(DocstringNode):
|
|
|
155
174
|
return params
|
|
156
175
|
|
|
157
176
|
def _extract_return_annotation(self) -> str | None:
|
|
158
|
-
if
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
177
|
+
if self.returns is None:
|
|
178
|
+
return self.returns
|
|
179
|
+
|
|
180
|
+
if isinstance(self.returns, ast.Constant):
|
|
181
|
+
return self.returns.value
|
|
182
|
+
|
|
183
|
+
if isinstance(self.returns, ast.Name):
|
|
184
|
+
return self.returns.id
|
|
185
|
+
|
|
186
|
+
return self.get_source_segment(self.returns)
|
|
165
187
|
|
|
166
188
|
def extract_returns(self) -> str | None:
|
|
167
189
|
if self.return_annotation:
|
|
168
190
|
return self.return_annotation
|
|
191
|
+
|
|
169
192
|
if any(
|
|
170
|
-
not isinstance(return_value := return_node.value, ast.Constant)
|
|
193
|
+
not isinstance((return_value := return_node.value), ast.Constant)
|
|
171
194
|
or return_value.value
|
|
172
195
|
for return_node in self.return_statements
|
|
173
196
|
):
|
|
174
197
|
return RETURN_TYPE_PLACEHOLDER
|
|
198
|
+
|
|
175
199
|
return None
|
|
176
200
|
|
|
177
201
|
def to_function(self) -> Function:
|
|
@@ -6,8 +6,10 @@ on the source code.
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
import ast
|
|
9
|
+
from textwrap import indent
|
|
9
10
|
from typing import TYPE_CHECKING
|
|
10
11
|
|
|
12
|
+
from ..exceptions import EmptyDocstringError
|
|
11
13
|
from .visitor import DocstringVisitor
|
|
12
14
|
|
|
13
15
|
if TYPE_CHECKING:
|
|
@@ -60,11 +62,80 @@ class DocstringTransformer(ast.NodeTransformer, DocstringVisitor):
|
|
|
60
62
|
+ ''.join(self.source_file.suffixes)
|
|
61
63
|
)
|
|
62
64
|
)
|
|
63
|
-
edited_code =
|
|
65
|
+
edited_code = self._convert_to_source_code()
|
|
64
66
|
output.write_text(edited_code)
|
|
65
67
|
print(f'Docstring templates written to {output}')
|
|
66
68
|
|
|
67
|
-
def
|
|
69
|
+
def _convert_to_source_code(self) -> str:
|
|
70
|
+
"""
|
|
71
|
+
Convert the modified AST back to source code, preserving the original format and
|
|
72
|
+
any comments.
|
|
73
|
+
|
|
74
|
+
Returns
|
|
75
|
+
-------
|
|
76
|
+
str
|
|
77
|
+
The original source code with the docstrings injected.
|
|
78
|
+
"""
|
|
79
|
+
source_code = self.source_code.splitlines()
|
|
80
|
+
output_lines = []
|
|
81
|
+
write_line = start_line = 0
|
|
82
|
+
|
|
83
|
+
for missing_docstring in self.missing_docstrings:
|
|
84
|
+
docstring_node = missing_docstring.ast_node.body[0]
|
|
85
|
+
prefix = docstring_node.col_offset
|
|
86
|
+
suffix = ''
|
|
87
|
+
|
|
88
|
+
# write everything before docstring
|
|
89
|
+
if not isinstance(missing_docstring.ast_node, ast.Module):
|
|
90
|
+
# line before a code node
|
|
91
|
+
start_line = missing_docstring.ast_node.body[1].lineno - 1
|
|
92
|
+
|
|
93
|
+
if isinstance(missing_docstring.ast_node, ast.ClassDef):
|
|
94
|
+
start_line = docstring_node.lineno
|
|
95
|
+
|
|
96
|
+
if len(body := missing_docstring.ast_node.body) == 2:
|
|
97
|
+
code_node = body[1]
|
|
98
|
+
line_number = code_node.lineno - 1
|
|
99
|
+
line = source_code[line_number]
|
|
100
|
+
|
|
101
|
+
if line.strip() != (function_body := line[code_node.col_offset :]):
|
|
102
|
+
# if the function body is on the same line as the signature, cut it out
|
|
103
|
+
# in order to inject the docstring in the right spot
|
|
104
|
+
source_code[line_number] = source_code[line_number][
|
|
105
|
+
: code_node.col_offset
|
|
106
|
+
].rstrip()
|
|
107
|
+
|
|
108
|
+
# add the logic under the docstring
|
|
109
|
+
start_line = missing_docstring.ast_node.body[1].lineno
|
|
110
|
+
suffix = function_body
|
|
111
|
+
|
|
112
|
+
output_lines += source_code[write_line:start_line]
|
|
113
|
+
|
|
114
|
+
# write docstring
|
|
115
|
+
if not (docstring := missing_docstring.docstring):
|
|
116
|
+
raise EmptyDocstringError
|
|
117
|
+
|
|
118
|
+
output_lines.append(
|
|
119
|
+
indent(
|
|
120
|
+
self.docstring_converter.format_docstring(
|
|
121
|
+
docstring.splitlines(),
|
|
122
|
+
indent=0,
|
|
123
|
+
quote=True,
|
|
124
|
+
)
|
|
125
|
+
+ (f'\n{suffix}' if suffix else ''),
|
|
126
|
+
' ' * prefix,
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# update current location in file
|
|
131
|
+
write_line = start_line
|
|
132
|
+
|
|
133
|
+
# write the rest of the file
|
|
134
|
+
output_lines += source_code[write_line:]
|
|
135
|
+
|
|
136
|
+
return '\n'.join(output_lines) + '\n'
|
|
137
|
+
|
|
138
|
+
def handle_missing_docstring(self, docstring_node: DocstringNode) -> None:
|
|
68
139
|
"""
|
|
69
140
|
Handle missing docstrings by injecting a suggested docstring template based on
|
|
70
141
|
the source code into the AST.
|
|
@@ -74,19 +145,16 @@ class DocstringTransformer(ast.NodeTransformer, DocstringVisitor):
|
|
|
74
145
|
docstring_node : DocstringNode
|
|
75
146
|
An instance of :class:`.DocstringNode`, which wraps an AST node and adds
|
|
76
147
|
additional context relevant for Docstringify.
|
|
77
|
-
|
|
78
|
-
Returns
|
|
79
|
-
-------
|
|
80
|
-
DocstringNode
|
|
81
|
-
An instance of :class:`.DocstringNode`, which wraps an AST node and adds
|
|
82
|
-
additional context relevant for Docstringify. The AST node it contains will
|
|
83
|
-
have a new node for the docstring template added to its body.
|
|
84
148
|
"""
|
|
149
|
+
indent = (
|
|
150
|
+
0
|
|
151
|
+
if isinstance(docstring_node.ast_node, ast.Module)
|
|
152
|
+
else docstring_node.ast_node.col_offset + 4
|
|
153
|
+
)
|
|
154
|
+
|
|
85
155
|
suggested_docstring = self.docstring_converter.suggest_docstring(
|
|
86
156
|
docstring_node,
|
|
87
|
-
indent=
|
|
88
|
-
if isinstance(docstring_node.ast_node, ast.Module)
|
|
89
|
-
else docstring_node.ast_node.col_offset + 4,
|
|
157
|
+
indent=indent,
|
|
90
158
|
)
|
|
91
159
|
docstring_ast_node = ast.Expr(ast.Constant(suggested_docstring))
|
|
92
160
|
|
|
@@ -98,8 +166,7 @@ class DocstringTransformer(ast.NodeTransformer, DocstringVisitor):
|
|
|
98
166
|
docstring_node.ast_node.body.insert(0, docstring_ast_node)
|
|
99
167
|
|
|
100
168
|
docstring_node.ast_node = ast.fix_missing_locations(docstring_node.ast_node)
|
|
101
|
-
|
|
102
|
-
return docstring_node
|
|
169
|
+
docstring_ast_node.col_offset = indent
|
|
103
170
|
|
|
104
171
|
def process_file(self) -> None:
|
|
105
172
|
"""
|
|
@@ -112,8 +112,8 @@ class DocstringVisitor(ast.NodeVisitor):
|
|
|
112
112
|
|
|
113
113
|
def process_docstring(self, docstring_node: DocstringNode) -> DocstringNode:
|
|
114
114
|
"""
|
|
115
|
-
Process a docstring node, appending it to :attr:`.missing_docstrings` if a
|
|
116
|
-
is required, but there isn't one.
|
|
115
|
+
Process a docstring node, appending it to :attr:`.missing_docstrings` if a
|
|
116
|
+
docstring is required, but there isn't one.
|
|
117
117
|
|
|
118
118
|
Parameters
|
|
119
119
|
----------
|
|
@@ -181,12 +181,12 @@ class DocstringVisitor(ast.NodeVisitor):
|
|
|
181
181
|
ast.AsyncFunctionDef | ast.ClassDef | ast.FunctionDef | ast.Module
|
|
182
182
|
The AST node that was visited.
|
|
183
183
|
"""
|
|
184
|
-
|
|
185
|
-
node,
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
184
|
+
if isinstance(node, ast.Module):
|
|
185
|
+
docstring_node = docstring_class(node, self.module_name, self.source_code)
|
|
186
|
+
else:
|
|
187
|
+
docstring_node = docstring_class(
|
|
188
|
+
node, self.module_name, self.source_code, parent=self.stack[-1]
|
|
189
|
+
)
|
|
190
190
|
|
|
191
191
|
self.stack.append(docstring_node)
|
|
192
192
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: docstringify
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: Flag missing docstrings and, optionally, generate them from signatures and type annotations.
|
|
5
5
|
Author-email: Stefanie Molin <docstringify@stefaniemolin.com>
|
|
6
6
|
License: MIT License
|
|
@@ -29,7 +29,7 @@ Project-URL: Documentation, https://github.com/stefmolin/docstringify
|
|
|
29
29
|
Project-URL: Homepage, https://github.com/stefmolin/docstringify
|
|
30
30
|
Project-URL: Source, https://github.com/stefmolin/docstringify
|
|
31
31
|
Keywords: ast,docstring generation,google-style docstring,numpydoc-style docstring,pre-commit
|
|
32
|
-
Classifier: Development Status ::
|
|
32
|
+
Classifier: Development Status :: 4 - Beta
|
|
33
33
|
Classifier: Programming Language :: Python
|
|
34
34
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
35
35
|
Classifier: Programming Language :: Python :: 3.9
|
|
@@ -103,11 +103,11 @@ You can use Docstringify in three modes:
|
|
|
103
103
|
|
|
104
104
|
### Pre-commit hook
|
|
105
105
|
|
|
106
|
-
Add the following to your `.pre-commit-config.yaml` file to block commits with missing docstrings
|
|
106
|
+
Add the following to your `.pre-commit-config.yaml` file to block commits with missing docstrings before any formatters like `ruff`:
|
|
107
107
|
|
|
108
108
|
```yaml
|
|
109
109
|
- repo: https://github.com/stefmolin/docstringify
|
|
110
|
-
rev: 0.
|
|
110
|
+
rev: 1.0.0
|
|
111
111
|
hooks:
|
|
112
112
|
- id: docstringify
|
|
113
113
|
```
|
|
@@ -116,17 +116,17 @@ By default, all docstrings are required. If you want to be more lenient, you can
|
|
|
116
116
|
|
|
117
117
|
```yaml
|
|
118
118
|
- repo: https://github.com/stefmolin/docstringify
|
|
119
|
-
rev: 0.
|
|
119
|
+
rev: 1.0.0
|
|
120
120
|
hooks:
|
|
121
121
|
- id: docstringify
|
|
122
122
|
args: [--threshold=0.75]
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
-
If you would like to see suggested docstring templates (inferred from type annotations for functions and methods), provide the `--suggest-changes` argument, along with the docstring style you want to use (options are `google`, `numpydoc`, and `stub`). Here, we ask for
|
|
125
|
+
If you would like to see suggested docstring templates (inferred from type annotations for functions and methods), provide the `--suggest-changes` argument, along with the docstring style you want to use (options are `google`, `numpydoc`, and `stub`). Here, we ask for stub suggestions (just single lines of `"""__description__"""`):
|
|
126
126
|
|
|
127
127
|
```yaml
|
|
128
128
|
- repo: https://github.com/stefmolin/docstringify
|
|
129
|
-
rev: 0.
|
|
129
|
+
rev: 1.0.0
|
|
130
130
|
hooks:
|
|
131
131
|
- id: docstringify
|
|
132
132
|
args: [--suggest-changes=numpydoc]
|
|
@@ -136,13 +136,21 @@ Use `--make-changes` to create a copy of each file with docstring templates. Her
|
|
|
136
136
|
|
|
137
137
|
```yaml
|
|
138
138
|
- repo: https://github.com/stefmolin/docstringify
|
|
139
|
-
rev: 0.
|
|
139
|
+
rev: 1.0.0
|
|
140
140
|
hooks:
|
|
141
141
|
- id: docstringify
|
|
142
142
|
args: [--make-changes=google]
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
-
If you want the changes to be made in place, change `--make-changes` to `--make-changes-inplace` – make sure you only operate on files that are in version control with this setting.
|
|
145
|
+
If you want the changes to be made in place, change `--make-changes` to `--make-changes-inplace` – make sure you only operate on files that are in version control with this setting. Here, we ask for [numpydoc-style docstring](https://numpydoc.readthedocs.io/en/latest/format.html#) suggestions:
|
|
146
|
+
|
|
147
|
+
```yaml
|
|
148
|
+
- repo: https://github.com/stefmolin/docstringify
|
|
149
|
+
rev: 1.0.0
|
|
150
|
+
hooks:
|
|
151
|
+
- id: docstringify
|
|
152
|
+
args: [--make-changes-inplace=numpydoc]
|
|
153
|
+
```
|
|
146
154
|
|
|
147
155
|
Be sure to check out the [pre-commit documentation](https://pre-commit.com/#pre-commit-configyaml---hooks) for additional configuration options.
|
|
148
156
|
|
|
@@ -216,7 +224,19 @@ test.say_hello is missing a docstring
|
|
|
216
224
|
Docstring templates written to /.../test_docstringify.py
|
|
217
225
|
```
|
|
218
226
|
|
|
219
|
-
If you want to overwrite the file with the edits, pass `overwrite=True` to `DocstringTransformer()
|
|
227
|
+
If you want to overwrite the file with the edits, pass `overwrite=True` to `DocstringTransformer()`:
|
|
228
|
+
|
|
229
|
+
```pycon
|
|
230
|
+
>>> from docstringify.converters import GoogleDocstringConverter
|
|
231
|
+
>>> from docstringify.traversal import DocstringTransformer
|
|
232
|
+
>>> transformer = DocstringTransformer(
|
|
233
|
+
... 'test.py', converter=GoogleDocstringConverter, overwrite=True
|
|
234
|
+
... )
|
|
235
|
+
>>> transformer.process_file()
|
|
236
|
+
test is missing a docstring
|
|
237
|
+
test.say_hello is missing a docstring
|
|
238
|
+
Docstring templates written to /.../test.py
|
|
239
|
+
```
|
|
220
240
|
|
|
221
241
|
## Contributing
|
|
222
242
|
|
|
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
|
|
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
|
|
File without changes
|