docstringify 0.2.0__tar.gz → 0.4.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.
Files changed (39) hide show
  1. docstringify-0.4.0/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
  2. docstringify-0.4.0/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  3. docstringify-0.4.0/.github/workflows/greetings.yml +16 -0
  4. {docstringify-0.2.0 → docstringify-0.4.0}/PKG-INFO +28 -12
  5. {docstringify-0.2.0 → docstringify-0.4.0}/README.md +27 -11
  6. {docstringify-0.2.0 → docstringify-0.4.0}/src/docstringify/__init__.py +1 -1
  7. docstringify-0.4.0/src/docstringify/cli.py +95 -0
  8. docstringify-0.4.0/src/docstringify/converters/base.py +94 -0
  9. {docstringify-0.2.0 → docstringify-0.4.0}/src/docstringify/converters/google.py +9 -11
  10. {docstringify-0.2.0 → docstringify-0.4.0}/src/docstringify/converters/numpydoc.py +8 -10
  11. docstringify-0.4.0/src/docstringify/exceptions.py +6 -0
  12. docstringify-0.4.0/src/docstringify/nodes/__init__.py +1 -0
  13. docstringify-0.4.0/src/docstringify/nodes/base.py +53 -0
  14. docstringify-0.4.0/src/docstringify/nodes/function.py +159 -0
  15. docstringify-0.4.0/src/docstringify/traversal/__init__.py +9 -0
  16. docstringify-0.4.0/src/docstringify/traversal/transformer.py +64 -0
  17. docstringify-0.4.0/src/docstringify/traversal/visitor.py +100 -0
  18. {docstringify-0.2.0 → docstringify-0.4.0}/src/docstringify.egg-info/PKG-INFO +28 -12
  19. {docstringify-0.2.0 → docstringify-0.4.0}/src/docstringify.egg-info/SOURCES.txt +11 -2
  20. docstringify-0.2.0/src/docstringify/cli.py +0 -73
  21. docstringify-0.2.0/src/docstringify/converters/base.py +0 -51
  22. docstringify-0.2.0/src/docstringify/visitor.py +0 -196
  23. {docstringify-0.2.0 → docstringify-0.4.0}/.github/dependabot.yml +0 -0
  24. {docstringify-0.2.0 → docstringify-0.4.0}/.github/release.yml +0 -0
  25. {docstringify-0.2.0 → docstringify-0.4.0}/.github/workflows/ci.yml +0 -0
  26. {docstringify-0.2.0 → docstringify-0.4.0}/.github/workflows/pypi-publish.yml +0 -0
  27. {docstringify-0.2.0 → docstringify-0.4.0}/.gitignore +0 -0
  28. {docstringify-0.2.0 → docstringify-0.4.0}/.pre-commit-config.yaml +0 -0
  29. {docstringify-0.2.0 → docstringify-0.4.0}/.pre-commit-hooks.yaml +0 -0
  30. {docstringify-0.2.0 → docstringify-0.4.0}/LICENSE +0 -0
  31. {docstringify-0.2.0 → docstringify-0.4.0}/pyproject.toml +0 -0
  32. {docstringify-0.2.0 → docstringify-0.4.0}/setup.cfg +0 -0
  33. {docstringify-0.2.0 → docstringify-0.4.0}/src/docstringify/__main__.py +0 -0
  34. {docstringify-0.2.0 → docstringify-0.4.0}/src/docstringify/components.py +0 -0
  35. {docstringify-0.2.0 → docstringify-0.4.0}/src/docstringify/converters/__init__.py +0 -0
  36. {docstringify-0.2.0 → docstringify-0.4.0}/src/docstringify.egg-info/dependency_links.txt +0 -0
  37. {docstringify-0.2.0 → docstringify-0.4.0}/src/docstringify.egg-info/entry_points.txt +0 -0
  38. {docstringify-0.2.0 → docstringify-0.4.0}/src/docstringify.egg-info/requires.txt +0 -0
  39. {docstringify-0.2.0 → docstringify-0.4.0}/src/docstringify.egg-info/top_level.txt +0 -0
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: Bug report
3
+ about: Something isn't working
4
+ title: "[BUG] "
5
+ labels: bug
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Required attestation**
11
+ - [ ] I have checked that this issue has not already been reported.
12
+ - [ ] I have confirmed this bug exists on the latest version of docstringify.
13
+ - [ ] I have confirmed this bug exists on the `main` branch of docstringify.
14
+
15
+ **Describe the bug**
16
+ A clear and concise description of what the bug is.
17
+
18
+ **To Reproduce**
19
+ Steps to reproduce the behavior.
20
+
21
+ **Expected behavior**
22
+ A clear and concise description of what you expected to happen.
23
+
24
+ **Screenshots**
25
+ If applicable, add screenshots to help explain your problem.
26
+
27
+ **Environment**
28
+ - OS: [e.g. iOS]
29
+ - Python Version [e.g. 3.12]
30
+
31
+ **Additional context**
32
+ Add any other context about the problem here.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Is your feature request related to a problem? Please describe.**
11
+ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
+
13
+ **Describe the solution you'd like**
14
+ A clear and concise description of what you want to happen.
15
+
16
+ **Describe alternatives you've considered**
17
+ A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+ **Additional context**
20
+ Add any other context or screenshots about the feature request here.
@@ -0,0 +1,16 @@
1
+ name: Greetings
2
+
3
+ on: [pull_request_target, issues]
4
+
5
+ jobs:
6
+ greeting:
7
+ runs-on: ubuntu-latest
8
+ permissions:
9
+ issues: write
10
+ pull-requests: write
11
+ steps:
12
+ - uses: actions/first-interaction@34f15e814fe48ac9312ccf29db4e74fa767cbab7 # v1.3.0
13
+ with:
14
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
15
+ issue-message: "It looks like this is your first issue here – welcome! Please familiarize yourself with the [contributing guidelines](https://github.com/stefmolin/docstringify/blob/main/CONTRIBUTING.md), if you haven't already."
16
+ pr-message: "Congratulations on making your first pull request to `docstringify`! Please familiarize yourself with the [contributing guidelines](https://github.com/stefmolin/docstringify/blob/main/CONTRIBUTING.md), if you haven't already."
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: docstringify
3
- Version: 0.2.0
3
+ Version: 0.4.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
@@ -60,7 +60,7 @@ Add the following to your `.pre-commit-config.yaml` file to block commits with m
60
60
 
61
61
  ```yaml
62
62
  - repo: https://github.com/stefmolin/docstringify
63
- rev: 0.2.0
63
+ rev: 0.4.0
64
64
  hooks:
65
65
  - id: docstringify
66
66
  ```
@@ -69,32 +69,34 @@ By default, all docstrings are required. If you want to be more lenient, you can
69
69
 
70
70
  ```yaml
71
71
  - repo: https://github.com/stefmolin/docstringify
72
- rev: 0.2.0
72
+ rev: 0.4.0
73
73
  hooks:
74
74
  - id: docstringify
75
75
  args: [--threshold=0.75]
76
76
  ```
77
77
 
78
- If you would like to see suggested docstring templates (inferred from type annotations for functions and methods), provide the `--suggest-changes` argument. By default, these will be [numpydoc-style docstrings](https://numpydoc.readthedocs.io/en/latest/format.html#):
78
+ 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` and `numpydoc`). Here, we ask for [numpydoc-style docstring](https://numpydoc.readthedocs.io/en/latest/format.html#) suggestions:
79
79
 
80
80
  ```yaml
81
81
  - repo: https://github.com/stefmolin/docstringify
82
- rev: 0.2.0
82
+ rev: 0.4.0
83
83
  hooks:
84
84
  - id: docstringify
85
- args: [--suggest-changes]
85
+ args: [--suggest-changes=numpydoc]
86
86
  ```
87
87
 
88
- For [Google-style docstrings](https://www.sphinx-doc.org/en/master/usage/extensions/example_google.html), specify `--style google`:
88
+ Use `--make-changes` to create a copy of each file with docstring templates. Here, we ask for changes using the [Google docstring style](https://www.sphinx-doc.org/en/master/usage/extensions/example_google.html):
89
89
 
90
90
  ```yaml
91
91
  - repo: https://github.com/stefmolin/docstringify
92
- rev: 0.2.0
92
+ rev: 0.4.0
93
93
  hooks:
94
94
  - id: docstringify
95
- args: [--suggest-changes, --style=google]
95
+ args: [--make-changes=google]
96
96
  ```
97
97
 
98
+ If you want the changes to be made in place, change `--make-changes` to `--make-changes-inplace` &ndash; make sure you only operate on files that are in version control with this setting. Note that the resulting format of your file may be a little different (spacing, newlines, *etc.*).
99
+
98
100
  Be sure to check out the [pre-commit documentation](https://pre-commit.com/#pre-commit-configyaml---hooks) for additional configuration options.
99
101
 
100
102
  ### Command line
@@ -124,7 +126,7 @@ $ python -m pip install docstringify
124
126
  Then, use the `DocstringVisitor()` class on individual files to see spots where docstrings are missing:
125
127
 
126
128
  ```pycon
127
- >>> from docstringify.visitor import DocstringVisitor
129
+ >>> from docstringify.traversal import DocstringVisitor
128
130
  >>> visitor = DocstringVisitor('test.py')
129
131
  >>> visitor.process_file()
130
132
  test is missing a docstring
@@ -135,8 +137,8 @@ If you would like to see suggested docstring templates (inferred from type annot
135
137
 
136
138
  ```pycon
137
139
  >>> from docstringify.converters import NumpydocDocstringConverter
138
- >>> from docstringify.visitor import DocstringVisitor
139
- >>> visitor = DocstringVisitor('test.py', converter=NumpydocDocstringConverter())
140
+ >>> from docstringify.traversal import DocstringVisitor
141
+ >>> visitor = DocstringVisitor('test.py', converter=NumpydocDocstringConverter)
140
142
  >>> visitor.process_file()
141
143
  test is missing a docstring
142
144
  Hint:
@@ -154,3 +156,17 @@ name : str, default="World"
154
156
  """
155
157
 
156
158
  ```
159
+
160
+ To make changes to your files, you will need to use the `DocstringTransformer` instead. With the `DocstringTransformer`, the converter is required:
161
+
162
+ ```pycon
163
+ >>> from docstringify.converters import GoogleDocstringConverter
164
+ >>> from docstringify.traversal import DocstringTransformer
165
+ >>> transformer = DocstringTransformer('test.py', converter=GoogleDocstringConverter)
166
+ >>> transformer.process_file()
167
+ test is missing a docstring
168
+ test.say_hello is missing a docstring
169
+ Docstring templates written to /.../test_docstringify.py
170
+ ```
171
+
172
+ If you want to overwrite the file with the edits, pass `overwrite=True` to `DocstringTransformer()`.
@@ -9,7 +9,7 @@ Add the following to your `.pre-commit-config.yaml` file to block commits with m
9
9
 
10
10
  ```yaml
11
11
  - repo: https://github.com/stefmolin/docstringify
12
- rev: 0.2.0
12
+ rev: 0.4.0
13
13
  hooks:
14
14
  - id: docstringify
15
15
  ```
@@ -18,32 +18,34 @@ By default, all docstrings are required. If you want to be more lenient, you can
18
18
 
19
19
  ```yaml
20
20
  - repo: https://github.com/stefmolin/docstringify
21
- rev: 0.2.0
21
+ rev: 0.4.0
22
22
  hooks:
23
23
  - id: docstringify
24
24
  args: [--threshold=0.75]
25
25
  ```
26
26
 
27
- If you would like to see suggested docstring templates (inferred from type annotations for functions and methods), provide the `--suggest-changes` argument. By default, these will be [numpydoc-style docstrings](https://numpydoc.readthedocs.io/en/latest/format.html#):
27
+ 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` and `numpydoc`). Here, we ask for [numpydoc-style docstring](https://numpydoc.readthedocs.io/en/latest/format.html#) suggestions:
28
28
 
29
29
  ```yaml
30
30
  - repo: https://github.com/stefmolin/docstringify
31
- rev: 0.2.0
31
+ rev: 0.4.0
32
32
  hooks:
33
33
  - id: docstringify
34
- args: [--suggest-changes]
34
+ args: [--suggest-changes=numpydoc]
35
35
  ```
36
36
 
37
- For [Google-style docstrings](https://www.sphinx-doc.org/en/master/usage/extensions/example_google.html), specify `--style google`:
37
+ Use `--make-changes` to create a copy of each file with docstring templates. Here, we ask for changes using the [Google docstring style](https://www.sphinx-doc.org/en/master/usage/extensions/example_google.html):
38
38
 
39
39
  ```yaml
40
40
  - repo: https://github.com/stefmolin/docstringify
41
- rev: 0.2.0
41
+ rev: 0.4.0
42
42
  hooks:
43
43
  - id: docstringify
44
- args: [--suggest-changes, --style=google]
44
+ args: [--make-changes=google]
45
45
  ```
46
46
 
47
+ If you want the changes to be made in place, change `--make-changes` to `--make-changes-inplace` &ndash; make sure you only operate on files that are in version control with this setting. Note that the resulting format of your file may be a little different (spacing, newlines, *etc.*).
48
+
47
49
  Be sure to check out the [pre-commit documentation](https://pre-commit.com/#pre-commit-configyaml---hooks) for additional configuration options.
48
50
 
49
51
  ### Command line
@@ -73,7 +75,7 @@ $ python -m pip install docstringify
73
75
  Then, use the `DocstringVisitor()` class on individual files to see spots where docstrings are missing:
74
76
 
75
77
  ```pycon
76
- >>> from docstringify.visitor import DocstringVisitor
78
+ >>> from docstringify.traversal import DocstringVisitor
77
79
  >>> visitor = DocstringVisitor('test.py')
78
80
  >>> visitor.process_file()
79
81
  test is missing a docstring
@@ -84,8 +86,8 @@ If you would like to see suggested docstring templates (inferred from type annot
84
86
 
85
87
  ```pycon
86
88
  >>> from docstringify.converters import NumpydocDocstringConverter
87
- >>> from docstringify.visitor import DocstringVisitor
88
- >>> visitor = DocstringVisitor('test.py', converter=NumpydocDocstringConverter())
89
+ >>> from docstringify.traversal import DocstringVisitor
90
+ >>> visitor = DocstringVisitor('test.py', converter=NumpydocDocstringConverter)
89
91
  >>> visitor.process_file()
90
92
  test is missing a docstring
91
93
  Hint:
@@ -103,3 +105,17 @@ name : str, default="World"
103
105
  """
104
106
 
105
107
  ```
108
+
109
+ To make changes to your files, you will need to use the `DocstringTransformer` instead. With the `DocstringTransformer`, the converter is required:
110
+
111
+ ```pycon
112
+ >>> from docstringify.converters import GoogleDocstringConverter
113
+ >>> from docstringify.traversal import DocstringTransformer
114
+ >>> transformer = DocstringTransformer('test.py', converter=GoogleDocstringConverter)
115
+ >>> transformer.process_file()
116
+ test is missing a docstring
117
+ test.say_hello is missing a docstring
118
+ Docstring templates written to /.../test_docstringify.py
119
+ ```
120
+
121
+ If you want to overwrite the file with the edits, pass `overwrite=True` to `DocstringTransformer()`.
@@ -3,4 +3,4 @@ Flag missing docstrings and, optionally, generate them from signatures and
3
3
  type annotations.
4
4
  """
5
5
 
6
- __version__ = '0.2.0'
6
+ __version__ = '0.4.0'
@@ -0,0 +1,95 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import sys
5
+ from functools import partial
6
+ from typing import Sequence
7
+
8
+ from . import __version__
9
+ from .converters import GoogleDocstringConverter, NumpydocDocstringConverter
10
+ from .traversal import DocstringTransformer, DocstringVisitor
11
+
12
+ PROG = __package__
13
+
14
+ STYLES = {
15
+ 'google': GoogleDocstringConverter,
16
+ 'numpydoc': NumpydocDocstringConverter,
17
+ }
18
+ CLI_DEFAULTS = {'threshold': 1.0}
19
+
20
+
21
+ def main(argv: Sequence[str] | None = None) -> int:
22
+ parser = argparse.ArgumentParser(prog=PROG)
23
+ parser.add_argument(
24
+ 'filenames',
25
+ nargs='*',
26
+ help='Filenames to process',
27
+ )
28
+ parser.add_argument(
29
+ '--version', action='version', version=f'%(prog)s {__version__}'
30
+ )
31
+
32
+ run_group = parser.add_argument_group('Run options')
33
+ handle_missing_docstring = run_group.add_mutually_exclusive_group(required=False)
34
+ handle_missing_docstring.add_argument(
35
+ '--make-changes',
36
+ choices=STYLES.keys(),
37
+ help='Whether to insert docstring templates for items missing docstrings',
38
+ )
39
+ handle_missing_docstring.add_argument(
40
+ '--make-changes-inplace',
41
+ choices=STYLES.keys(),
42
+ help=(
43
+ 'Whether to insert docstring templates for items missing docstrings, '
44
+ 'overwriting the original file'
45
+ ),
46
+ )
47
+ handle_missing_docstring.add_argument(
48
+ '--suggest-changes',
49
+ choices=STYLES.keys(),
50
+ help='Whether to print out docstring templates for items missing docstrings',
51
+ )
52
+ run_group.add_argument(
53
+ '--threshold',
54
+ type=float,
55
+ default=CLI_DEFAULTS['threshold'],
56
+ help='The percentage of docstrings that must be present to pass',
57
+ )
58
+ args = parser.parse_args(argv)
59
+
60
+ if style := args.make_changes or args.make_changes_inplace or args.suggest_changes:
61
+ converter = STYLES[style]
62
+ else:
63
+ converter = None
64
+
65
+ get_docstring_processor = (
66
+ partial(
67
+ DocstringTransformer,
68
+ converter=converter,
69
+ **{'overwrite': bool(args.make_changes_inplace)},
70
+ )
71
+ if args.make_changes or args.make_changes_inplace
72
+ else partial(DocstringVisitor, converter=converter)
73
+ )
74
+
75
+ docstrings_processed = missing_docstrings = 0
76
+ for file in args.filenames:
77
+ processor = get_docstring_processor(file)
78
+ processor.process_file()
79
+ missing_docstrings += processor.missing_docstrings
80
+ docstrings_processed += processor.docstrings_inspected
81
+
82
+ if docstrings_processed and (
83
+ missing_percentage := (missing_docstrings / docstrings_processed)
84
+ ) > (1 - args.threshold):
85
+ print(f'Missing {missing_percentage:.0%} of docstrings', file=sys.stderr)
86
+ print(
87
+ f'Your settings require {args.threshold:.0%} of docstrings to be present',
88
+ file=sys.stderr,
89
+ )
90
+ return 1
91
+ return 0
92
+
93
+
94
+ if __name__ == '__main__':
95
+ raise SystemExit(main())
@@ -0,0 +1,94 @@
1
+ """Base docstring converter."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import ast
6
+ import textwrap
7
+ from abc import ABC, abstractmethod
8
+ from typing import TYPE_CHECKING
9
+
10
+ from ..components import Function
11
+ from ..exceptions import InvalidDocstringError
12
+
13
+ if TYPE_CHECKING:
14
+ from ..components import Parameter
15
+ from ..nodes.base import DocstringNode
16
+
17
+
18
+ class DocstringConverter(ABC):
19
+ def __init__(
20
+ self,
21
+ parameters_section_template: str,
22
+ returns_section_template: str,
23
+ quote: bool,
24
+ ) -> None:
25
+ self.parameters_section_template = parameters_section_template
26
+ self.returns_section_template = returns_section_template
27
+ self._quote = quote
28
+
29
+ @abstractmethod
30
+ def to_class_docstring(self, class_name: str, indent: int) -> str:
31
+ pass
32
+
33
+ @abstractmethod
34
+ def to_function_docstring(self, function: Function, indent: int) -> str:
35
+ pass
36
+
37
+ @abstractmethod
38
+ def to_module_docstring(self, module_name: str) -> str:
39
+ pass
40
+
41
+ @abstractmethod
42
+ def format_parameter(self, parameter: Parameter) -> str:
43
+ pass
44
+
45
+ def parameters_section(self, parameters: tuple[Parameter, ...]) -> str:
46
+ if parameters:
47
+ return self.parameters_section_template.format(
48
+ parameters='\n'.join(
49
+ self.format_parameter(parameter) for parameter in parameters
50
+ )
51
+ )
52
+ return ''
53
+
54
+ @abstractmethod
55
+ def format_return(self, return_type: str | None) -> str:
56
+ pass
57
+
58
+ def returns_section(self, return_type: str | None) -> str:
59
+ if return_text := self.format_return(return_type):
60
+ return self.returns_section_template.format(returns=return_text)
61
+ return ''
62
+
63
+ def quote_docstring(self, docstring: str | list[str], indent: int) -> str:
64
+ quote = '"""' if self._quote else ''
65
+ prefix = sep = ''
66
+ if isinstance(docstring, str):
67
+ docstring = [quote, docstring, quote]
68
+ elif isinstance(docstring, list):
69
+ if len(docstring) > 1:
70
+ prefix = ' ' * indent if indent else ''
71
+ sep = '\n'
72
+ docstring = [quote, *docstring, f'{prefix}{quote}']
73
+ else:
74
+ raise InvalidDocstringError(type(docstring).__name__)
75
+
76
+ return textwrap.indent(sep.join(docstring), prefix)
77
+
78
+ def suggest_docstring(
79
+ self,
80
+ docstring_node: DocstringNode,
81
+ indent: int = 0,
82
+ ) -> str:
83
+ if isinstance(docstring_node.ast_node, ast.AsyncFunctionDef | ast.FunctionDef):
84
+ return self.to_function_docstring(
85
+ Function(
86
+ docstring_node.extract_arguments(), docstring_node.extract_returns()
87
+ ),
88
+ indent=indent,
89
+ )
90
+
91
+ if isinstance(docstring_node.ast_node, ast.Module):
92
+ return self.to_module_docstring(docstring_node.module_name)
93
+
94
+ return self.to_class_docstring(docstring_node.name, indent=indent)
@@ -7,15 +7,15 @@ from .base import DocstringConverter
7
7
 
8
8
 
9
9
  class GoogleDocstringConverter(DocstringConverter):
10
- def __init__(self) -> None:
10
+ def __init__(self, quote: bool) -> None:
11
11
  super().__init__(
12
12
  parameters_section_template='Args:\n{parameters}',
13
13
  returns_section_template='Returns:\n {returns}',
14
+ quote=quote,
14
15
  )
15
16
 
16
- def to_function_docstring(self, function: Function) -> str:
17
- # TODO: the visitor needs the triple quotes but the transformer does not
18
- docstring = [f'"""{DESCRIPTION_PLACEHOLDER}']
17
+ def to_function_docstring(self, function: Function, indent: int) -> str:
18
+ docstring = [DESCRIPTION_PLACEHOLDER]
19
19
 
20
20
  if parameters_section := self.parameters_section(function.parameters):
21
21
  docstring.extend(['', parameters_section])
@@ -23,14 +23,12 @@ class GoogleDocstringConverter(DocstringConverter):
23
23
  if returns_section := self.returns_section(function.return_type):
24
24
  docstring.extend(['', returns_section])
25
25
 
26
- sep = '' if len(docstring) == 1 else '\n'
27
-
28
- return sep.join([*docstring, '"""'])
26
+ return self.quote_docstring(docstring, indent=indent)
29
27
 
30
28
  def format_parameter(self, parameter: Parameter) -> str:
31
29
  category = f'{f", {parameter.category}" if parameter.category else ""}'
32
30
  return (
33
- f' {parameter.name} ({parameter.type_}{category}): {DESCRIPTION_PLACEHOLDER} '
31
+ f' {parameter.name} ({parameter.type_}{category}): {DESCRIPTION_PLACEHOLDER}'
34
32
  f'{f" Defaults to {parameter.default}." if parameter.default != NO_DEFAULT else ""}'
35
33
  )
36
34
 
@@ -40,7 +38,7 @@ class GoogleDocstringConverter(DocstringConverter):
40
38
  return ''
41
39
 
42
40
  def to_module_docstring(self, module_name: str) -> str:
43
- return f'"""{DESCRIPTION_PLACEHOLDER}"""'
41
+ return self.quote_docstring(DESCRIPTION_PLACEHOLDER, indent=0)
44
42
 
45
- def to_class_docstring(self, class_name: str) -> str:
46
- return f'"""{DESCRIPTION_PLACEHOLDER}"""'
43
+ def to_class_docstring(self, class_name: str, indent: int) -> str:
44
+ return self.quote_docstring(DESCRIPTION_PLACEHOLDER, indent=indent)
@@ -7,15 +7,15 @@ from .base import DocstringConverter
7
7
 
8
8
 
9
9
  class NumpydocDocstringConverter(DocstringConverter):
10
- def __init__(self) -> None:
10
+ def __init__(self, quote: bool) -> None:
11
11
  super().__init__(
12
12
  parameters_section_template='Parameters\n----------\n{parameters}',
13
13
  returns_section_template='Returns\n-------\n{returns}',
14
+ quote=quote,
14
15
  )
15
16
 
16
- def to_function_docstring(self, function: Function) -> str:
17
- # TODO: the visitor needs the triple quotes but the transformer does not
18
- docstring = ['"""', DESCRIPTION_PLACEHOLDER]
17
+ def to_function_docstring(self, function: Function, indent: int) -> str:
18
+ docstring = [DESCRIPTION_PLACEHOLDER]
19
19
 
20
20
  if parameters_section := self.parameters_section(function.parameters):
21
21
  docstring.extend(['', parameters_section])
@@ -23,9 +23,7 @@ class NumpydocDocstringConverter(DocstringConverter):
23
23
  if returns_section := self.returns_section(function.return_type):
24
24
  docstring.extend(['', returns_section])
25
25
 
26
- sep = '' if len(docstring) == 2 else '\n'
27
-
28
- return sep.join([*docstring, '"""'])
26
+ return self.quote_docstring(docstring, indent=indent)
29
27
 
30
28
  def format_parameter(self, parameter: Parameter) -> str:
31
29
  return (
@@ -41,7 +39,7 @@ class NumpydocDocstringConverter(DocstringConverter):
41
39
  return ''
42
40
 
43
41
  def to_module_docstring(self, module_name: str) -> str:
44
- return f'"""{DESCRIPTION_PLACEHOLDER}"""'
42
+ return self.quote_docstring(DESCRIPTION_PLACEHOLDER, indent=0)
45
43
 
46
- def to_class_docstring(self, class_name: str) -> str:
47
- return f'"""{DESCRIPTION_PLACEHOLDER}"""'
44
+ def to_class_docstring(self, class_name: str, indent: int) -> str:
45
+ return self.quote_docstring(DESCRIPTION_PLACEHOLDER, indent=indent)
@@ -0,0 +1,6 @@
1
+ """Docstringify exceptions."""
2
+
3
+
4
+ class InvalidDocstringError(ValueError):
5
+ def __init__(self, docstring_class: str) -> None:
6
+ super().__init__(f'Expected str or list[str] docstring, got {docstring_class}')
@@ -0,0 +1 @@
1
+ """Docstringify nodes."""
@@ -0,0 +1,53 @@
1
+ from __future__ import annotations
2
+
3
+ import ast
4
+ from functools import partial
5
+ from typing import Callable, overload
6
+
7
+
8
+ class DocstringNode:
9
+ @overload
10
+ def __init__(
11
+ self,
12
+ node: ast.Module,
13
+ module_name: str,
14
+ source_code: str,
15
+ parent: None = None,
16
+ ) -> None: ...
17
+
18
+ @overload
19
+ def __init__(
20
+ self,
21
+ node: ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef,
22
+ module_name: str,
23
+ source_code: str,
24
+ parent: DocstringNode,
25
+ ) -> None: ...
26
+
27
+ def __init__(
28
+ self,
29
+ node: ast.Module | ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef,
30
+ module_name: str,
31
+ source_code: str,
32
+ parent: DocstringNode | None = None,
33
+ ) -> None:
34
+ self.module_name: str = module_name
35
+ self.parent: DocstringNode | None = parent
36
+
37
+ self.ast_node: (
38
+ ast.Module | ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef
39
+ ) = node
40
+ self.name: str = getattr(node, 'name', self.module_name)
41
+
42
+ self.docstring = ast.get_docstring(node)
43
+ self.get_source_segment: Callable[[ast.AST], str] = partial(
44
+ ast.get_source_segment, source_code
45
+ )
46
+
47
+ @property
48
+ def fully_qualified_name(self) -> str:
49
+ return (
50
+ f'{self.parent.fully_qualified_name}.{self.name}'
51
+ if self.parent
52
+ else self.name
53
+ )