docstring-tailor 0.3.1__tar.gz → 0.4.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.
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/.gitignore +0 -2
- docstring_tailor-0.4.1/.pre-commit-hooks.yaml +8 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/PKG-INFO +125 -19
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/README.md +123 -17
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/pyproject.toml +1 -1
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/cli_config.py +1 -1
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/constants.py +58 -14
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/ir_model.py +12 -4
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/main.py +170 -22
- docstring_tailor-0.4.1/src/docstring_tailor/parser/directive_based/sphinx_docstring_parser.py +577 -0
- docstring_tailor-0.3.1/src/docstring_tailor/parser/indentation_based/indentation_based_parser.py → docstring_tailor-0.4.1/src/docstring_tailor/parser/docstring_parser_base.py +28 -179
- docstring_tailor-0.4.1/src/docstring_tailor/parser/indentation_based/indentation_based_parser.py +200 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/parser/indentation_based/numpy_docstring_parser.py +3 -2
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/parser/indentation_based/structured_list_parser.py +35 -19
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/parser/parser_factory.py +10 -8
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/renderer/base_renderer.py +5 -1
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/renderer/google_renderer.py +23 -11
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/renderer/numpy_renderer.py +21 -7
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/renderer/renderer_factory.py +2 -0
- docstring_tailor-0.4.1/src/docstring_tailor/renderer/sphinx_renderer.py +290 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/utils/utils_keyword_translation.py +115 -35
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/utils/utils_parsing.py +8 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/tests/cases/formatting_cases.py +107 -17
- docstring_tailor-0.4.1/tests/fixtures/google/docstring_elements/structured_list_malformed/structured_list_malformed_80.py +11 -0
- docstring_tailor-0.4.1/tests/fixtures/google/docstring_elements/structured_list_malformed/structured_list_malformed_wrong_input.py +12 -0
- docstring_tailor-0.4.1/tests/fixtures/numpy/docstring_elements/named_paragraph_code_block/named_paragraph_code_block_80.py +8 -0
- docstring_tailor-0.4.1/tests/fixtures/numpy/docstring_elements/named_paragraph_code_block/named_paragraph_code_block_blank_lines.py +14 -0
- docstring_tailor-0.4.1/tests/fixtures/numpy/docstring_elements/named_paragraph_code_repl/named_paragraph_code_repl_80.py +26 -0
- docstring_tailor-0.4.1/tests/fixtures/numpy/docstring_elements/named_paragraph_code_repl/named_paragraph_code_repl_blank_lines.py +37 -0
- docstring_tailor-0.4.1/tests/fixtures/numpy/docstring_elements/named_paragraph_paragraph/named_paragraph_paragraph_100.py +7 -0
- docstring_tailor-0.4.1/tests/fixtures/numpy/docstring_elements/named_paragraph_paragraph/named_paragraph_paragraph_60.py +9 -0
- docstring_tailor-0.4.1/tests/fixtures/numpy/docstring_elements/named_paragraph_paragraph/named_paragraph_paragraph_80.py +8 -0
- docstring_tailor-0.4.1/tests/fixtures/numpy/docstring_elements/named_paragraph_paragraph/named_paragraph_paragraph_wrong_input.py +24 -0
- docstring_tailor-0.4.1/tests/fixtures/numpy/docstring_elements/paragraph_and_simple_list/paragraph_and_simple_list_100.py +23 -0
- docstring_tailor-0.4.1/tests/fixtures/numpy/docstring_elements/paragraph_and_simple_list/paragraph_and_simple_list_60.py +31 -0
- docstring_tailor-0.4.1/tests/fixtures/numpy/docstring_elements/paragraph_and_simple_list/paragraph_and_simple_list_80.py +25 -0
- docstring_tailor-0.4.1/tests/fixtures/numpy/docstring_elements/paragraph_and_simple_list/paragraph_and_simple_list_wrong_input.py +41 -0
- docstring_tailor-0.4.1/tests/fixtures/numpy/docstring_elements/structured_list/structured_list_100.py +37 -0
- docstring_tailor-0.4.1/tests/fixtures/numpy/docstring_elements/structured_list/structured_list_60.py +44 -0
- docstring_tailor-0.4.1/tests/fixtures/numpy/docstring_elements/structured_list/structured_list_80.py +38 -0
- docstring_tailor-0.4.1/tests/fixtures/numpy/docstring_elements/structured_list/structured_list_wrong_input.py +90 -0
- docstring_tailor-0.4.1/tests/fixtures/numpy/docstring_elements/structured_list_malformed/structured_list_malformed_80.py +17 -0
- docstring_tailor-0.4.1/tests/fixtures/numpy/docstring_elements/structured_list_malformed/structured_list_malformed_wrong_input.py +17 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/uv.lock +1 -1
- docstring_tailor-0.3.1/temp.md +0 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/LICENSE +0 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/makefile +0 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/__init__.py +0 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/docstring_visitor.py +0 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/parser/indentation_based/google_docstring_parser.py +0 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/utils/__init__.py +0 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/utils/utils_cli.py +0 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/utils/utils_file_system.py +0 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/utils/utils_formatting.py +0 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/utils/utils_list_detection.py +0 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/src/docstring_tailor/utils/utils_printing.py +0 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/tests/cases/__init__.py +0 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/tests/cases/config_model.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/class_docstring/class_docstring_100.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/class_docstring/class_docstring_60.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/class_docstring/class_docstring_80.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/code_block_multiple/code_block_multiple_80.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/code_block_multiple/code_block_multiple_blank_lines.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/code_block_singular/code_block_singular_80.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/code_block_singular/code_block_singular_blank_lines.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/code_repl_multiple/code_repl_multiple_80.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/code_repl_multiple/code_repl_multiple_blank_lines.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/code_repl_singular/code_repl_singular_80.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/code_repl_singular/code_repl_singular_blank_lines.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/empty/empty_80.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/empty/empty_blank_lines.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/empty/empty_no_space.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/named_paragraph_code_block/named_paragraph_code_block_80.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/named_paragraph_code_block/named_paragraph_code_block_blank_lines.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/named_paragraph_code_repl/named_paragraph_code_repl_80.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/named_paragraph_code_repl/named_paragraph_code_repl_blank_lines.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/named_paragraph_paragraph/named_paragraph_paragraph_100.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/named_paragraph_paragraph/named_paragraph_paragraph_60.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/named_paragraph_paragraph/named_paragraph_paragraph_80.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/named_paragraph_paragraph/named_paragraph_paragraph_wrong_input.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/paragraph_and_simple_list/paragraph_and_simple_list_100.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/paragraph_and_simple_list/paragraph_and_simple_list_60.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/paragraph_and_simple_list/paragraph_and_simple_list_80.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/paragraph_and_simple_list/paragraph_and_simple_list_wrong_input.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/paragraph_multi_line/paragraph_multi_line_100.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/paragraph_multi_line/paragraph_multi_line_60.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/paragraph_multi_line/paragraph_multi_line_80.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/paragraph_multi_line/paragraph_multi_line_wrong_input.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/paragraph_one_line/paragraph_one_line_100.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/paragraph_one_line/paragraph_one_line_60.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/paragraph_one_line/paragraph_one_line_80.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/paragraph_one_line/paragraph_one_line_wrong_input.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/simple_list/simple_list_100.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/simple_list/simple_list_60.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/simple_list/simple_list_80.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/simple_list/simple_list_wrong_input.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/structured_list/structured_list_100.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/structured_list/structured_list_60.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/structured_list/structured_list_80.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/docstring_elements/structured_list/structured_list_wrong_input.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/function_docstring/function_docstring_100.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/function_docstring/function_docstring_60.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/function_docstring/function_docstring_80.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/module_docstring/module_docstring_100.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/module_docstring/module_docstring_60.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/module_docstring/module_docstring_80.py +0 -0
- {docstring_tailor-0.3.1/tests/fixtures → docstring_tailor-0.4.1/tests/fixtures/google}/readme_examples/readme_examples.py +0 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/tests/test_docstring_tailor.py +0 -0
- {docstring_tailor-0.3.1 → docstring_tailor-0.4.1}/tests/utils/utils_testing.py +0 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Hook definition consumed via `repo: https://github.com/AukeB/docstring-tailor`.
|
|
2
|
+
# Consumers must supply the style through `args`, e.g. args: ["style", "google"].
|
|
3
|
+
- id: docstring-tailor
|
|
4
|
+
name: docstring-tailor
|
|
5
|
+
description: Format Python docstrings to a configurable line length and style.
|
|
6
|
+
entry: docstring_tailor format
|
|
7
|
+
language: python
|
|
8
|
+
types: [python]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: docstring-tailor
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Formats Python docstrings to PEP 257 style with configurable line length.
|
|
5
5
|
Author-email: Auke Bruinsma <afbruinsma@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -44,16 +44,17 @@ Formats Python docstrings to PEP 257 style with configurable line length.
|
|
|
44
44
|
## Table of Contents
|
|
45
45
|
1. [Demo](#demo)
|
|
46
46
|
2. [Installation](#Installation)
|
|
47
|
-
3. [Quick start](#
|
|
47
|
+
3. [Quick start](#quick-start)
|
|
48
48
|
4. [API Overview](#api-overview)
|
|
49
49
|
- [Command](#command)
|
|
50
50
|
- [Options](#options)
|
|
51
51
|
- [Examples](#examples)
|
|
52
|
-
5. [
|
|
53
|
-
6. [
|
|
54
|
-
7. [
|
|
55
|
-
8. [
|
|
56
|
-
9. [
|
|
52
|
+
5. [Pre-commit and prek hook](#pre-commit-and-prek-hook)
|
|
53
|
+
6. [Example docstrings](#example-docstrings)
|
|
54
|
+
7. [What Your Line Length Says About You!](#what-your-line-length-says-about-you)
|
|
55
|
+
8. [Resources](#resources)
|
|
56
|
+
9. [Release Notes](#release-notes)
|
|
57
|
+
10. [Roadmap](#roadmap)
|
|
57
58
|
|
|
58
59
|
## Demo
|
|
59
60
|
|
|
@@ -116,7 +117,7 @@ line-length = 88
|
|
|
116
117
|
line-length = 88
|
|
117
118
|
```
|
|
118
119
|
|
|
119
|
-
Define a docstring style.
|
|
120
|
+
Define a docstring style. Three styles are currently supported: [Google](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html), [NumPy](https://numpydoc.readthedocs.io/en/latest/format.html), and [Sphinx](https://www.sphinx-doc.org/en/master/usage/domains/python.html#info-field-lists). The style always has to be configured explicitly.
|
|
120
121
|
|
|
121
122
|
```bash
|
|
122
123
|
uv run docstring_tailor format --style numpy
|
|
@@ -135,6 +136,8 @@ To convert existing docstrings from one style to another, use the `convert` comm
|
|
|
135
136
|
uv run docstring_tailor convert my_file.py --from-style google --to-style numpy
|
|
136
137
|
```
|
|
137
138
|
|
|
139
|
+
`docstring-tailor` can also run as a [pre-commit](https://pre-commit.com/) / [prek](https://github.com/j178/prek) hook so docstrings stay formatted on every commit, see [Pre-commit and prek hook](#pre-commit-and-prek-hook).
|
|
140
|
+
|
|
138
141
|
## API Overview
|
|
139
142
|
|
|
140
143
|
### Commands
|
|
@@ -165,20 +168,20 @@ If no paths are provided, `docstring_tailor` will attempt to locate and format f
|
|
|
165
168
|
|
|
166
169
|
| <div style="width:140px">Option</div> | <div style="width:50px">Type</div> | <div style="width:80px">Default</div> | Description |
|
|
167
170
|
|---|---|---|---|
|
|
168
|
-
| `--line-length` | `int` | 100
|
|
169
|
-
| `--style` | `str` |
|
|
170
|
-
| `--exclude` | `str` | —
|
|
171
|
-
| `--diff` | flag | —
|
|
171
|
+
| `--line-length` | `int` | 100 | Maximum number of characters allowed per line after formatting. |
|
|
172
|
+
| `--style` | `str` | *required* | Docstring style to format to. `google`, `numpy` or `sphinx`. |
|
|
173
|
+
| `--exclude` | `str` | — | A glob pattern for paths to exclude. Can be passed multiple times. Single-path patterns (e.g. `tests`, `*.pyi`) match by name anywhere in the tree. Relative patterns (e.g. `src/generated/*.py`) match against the path relative to the project root. |
|
|
174
|
+
| `--diff` | flag | — | Print a unified diff of changes to stdout instead of modifying files. No files are written when this flag is set. |
|
|
172
175
|
|
|
173
176
|
#### `convert`
|
|
174
177
|
|
|
175
178
|
| <div style="width:140px">Option</div> | <div style="width:50px">Type</div> | <div style="width:80px">Default</div> | Description |
|
|
176
179
|
|---|---|---|---|
|
|
177
|
-
| `--from-style` | `str` | *required* | Docstring style to convert from. `google` or `
|
|
178
|
-
| `--to-style` | `str` | *required* | Docstring style to convert to. `google` or `
|
|
179
|
-
| `--line-length` | `int` | 100
|
|
180
|
-
| `--exclude` | `str` | —
|
|
181
|
-
| `--diff` | flag | —
|
|
180
|
+
| `--from-style` | `str` | *required* | Docstring style to convert from. `google`, `numpy` or `sphinx`. |
|
|
181
|
+
| `--to-style` | `str` | *required* | Docstring style to convert to. `google`, `numpy` or `sphinx`. Must differ from `--from-style`. |
|
|
182
|
+
| `--line-length` | `int` | 100 | Maximum number of characters allowed per line after formatting. |
|
|
183
|
+
| `--exclude` | `str` | — | A glob pattern for paths to exclude. Can be passed multiple times. Single-path patterns (e.g. `tests`, `*.pyi`) match by name anywhere in the tree. Relative patterns (e.g. `src/generated/*.py`) match against the path relative to the project root. |
|
|
184
|
+
| `--diff` | flag | — | Print a unified diff of changes to stdout instead of modifying files. No files are written when this flag is set. |
|
|
182
185
|
|
|
183
186
|
`--from-style` and `--to-style` have no config-file or default fallback — both must be given explicitly on every `convert` invocation, and must be different from each other.
|
|
184
187
|
|
|
@@ -217,6 +220,56 @@ style = "google"
|
|
|
217
220
|
exclude = ["tests", "src/generated/*.py"]
|
|
218
221
|
```
|
|
219
222
|
|
|
223
|
+
## Pre-commit and prek hook
|
|
224
|
+
|
|
225
|
+
`docstring-tailor` can run as a [pre-commit](https://pre-commit.com/) hook, and works unchanged with [prek](https://github.com/j178/prek), the drop-in reimplementation, so docstrings stay formatted automatically on every commit. When the hook reformats a file it rewrites it in place and stops the commit so you can review and re-stage the change, exactly like the Ruff or Black hooks.
|
|
226
|
+
|
|
227
|
+
### Hosted hook
|
|
228
|
+
|
|
229
|
+
Add the following to your `.pre-commit-config.yaml`:
|
|
230
|
+
|
|
231
|
+
```yaml
|
|
232
|
+
repos:
|
|
233
|
+
- repo: https://github.com/AukeB/docstring-tailor
|
|
234
|
+
rev: 0.4.1
|
|
235
|
+
hooks:
|
|
236
|
+
- id: docstring-tailor
|
|
237
|
+
args: ["--style", "google"]
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
- `--style` is required, use `google`, `numpy` or `sphinx`. Pass any other option (`--line-length`, `--exclude`) through `args`, or set them in a `[tool.docstring_tailor]` config block instead. `args` is a single flat list where every flag and every value is its own quoted element (e.g. `["--style", "google", "--line-length", "88"]`, with numbers quoted). Install the hook once with `pre-commit install` (or `prek install`).
|
|
241
|
+
|
|
242
|
+
Prefer TOML? prek also reads a native `prek.toml`, a drop-in alternative to `.pre-commit-config.yaml` (upstream `pre-commit` ignores it). The same hook in `prek.toml`
|
|
243
|
+
|
|
244
|
+
```toml
|
|
245
|
+
[[repos]]
|
|
246
|
+
repo = "https://github.com/AukeB/docstring-tailor"
|
|
247
|
+
rev = "v0.4.1"
|
|
248
|
+
hooks= [
|
|
249
|
+
{ id = "docstring-tailor", args = ["--style", "google"] },
|
|
250
|
+
]
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
`prek util yaml-to-toml` converts an existing YAML config for you.
|
|
254
|
+
|
|
255
|
+
### Local hook
|
|
256
|
+
|
|
257
|
+
If your project already installs `docstring-tailor` (for example as a `uv` dev dependency), you can run it as a `repo: local` hook. pre-commit then never clones a remote repo, which is handy in locked-down or offline environments:
|
|
258
|
+
|
|
259
|
+
```yaml
|
|
260
|
+
repos:
|
|
261
|
+
- repo: local
|
|
262
|
+
hooks:
|
|
263
|
+
- id: docstring-tailor
|
|
264
|
+
name: docstring-tailor
|
|
265
|
+
entry: uv run docstring_tailor format --style google
|
|
266
|
+
language: system
|
|
267
|
+
types: [python]
|
|
268
|
+
pass_filenames: true
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
For CI, run `docstring-tailor format --diff` instead: it never writes files and exits non-zero when any docstring would change, so it can gate a pipeline.
|
|
272
|
+
|
|
220
273
|
## Example docstrings
|
|
221
274
|
|
|
222
275
|
1. [Module docstring](#module-docstring)
|
|
@@ -408,6 +461,47 @@ def example_function(example_argument_1: str, example_argument_2: int) -> str:
|
|
|
408
461
|
- In the `Examples` section, start the Python REPL with `>>>` and use `...` for continuation lines, matching Pydoc conventions — same as Google style.
|
|
409
462
|
- These same section keywords can also be used in module or class docstrings — for example, a `Parameters` section in a class docstring, or an `Examples` section in a module docstring.
|
|
410
463
|
|
|
464
|
+
**Sphinx / reST**
|
|
465
|
+
|
|
466
|
+
```python
|
|
467
|
+
def example_function(example_argument_1: str, example_argument_2: int) -> str:
|
|
468
|
+
"""Demonstrates a Sphinx-style function docstring with multiple
|
|
469
|
+
sections.
|
|
470
|
+
|
|
471
|
+
This function exists purely as a formatting example and
|
|
472
|
+
illustrates how parameters, return values, and raised exceptions
|
|
473
|
+
are documented using reStructuredText info fields.
|
|
474
|
+
|
|
475
|
+
:param example_argument_1: First example input value used to
|
|
476
|
+
construct a formatted result string.
|
|
477
|
+
:type example_argument_1: str
|
|
478
|
+
:param example_argument_2: Second example input value used to
|
|
479
|
+
influence the transformation logic.
|
|
480
|
+
:type example_argument_2: int
|
|
481
|
+
:returns: A formatted string combining both input arguments into a
|
|
482
|
+
single human-readable representation.
|
|
483
|
+
:rtype: str
|
|
484
|
+
:raises ValueError: Raised when example_argument_2 is negative or
|
|
485
|
+
zero, as only positive integers are considered valid in this
|
|
486
|
+
demonstration.
|
|
487
|
+
|
|
488
|
+
.. note::
|
|
489
|
+
Additional informational directives such as ``.. note::`` and
|
|
490
|
+
``.. warning::`` are recognized and preserved during
|
|
491
|
+
formatting.
|
|
492
|
+
"""
|
|
493
|
+
if example_argument_2 <= 0:
|
|
494
|
+
raise ValueError("example_argument_2 must be positive")
|
|
495
|
+
|
|
496
|
+
return f"{example_argument_1}-{example_argument_2}"
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
- Parameters are documented with `:param name: description` info fields, and their types with a separate `:type name: type` field. An inline form (`:param str name: description`) is also accepted on input and is split into the two-field form on output.
|
|
500
|
+
- Types are genuinely optional in reST: a `:param:` without a matching `:type:` is preserved as-is, and `docstring_tailor` never fabricates a type when one is absent.
|
|
501
|
+
- Return values use `:returns:` (with `:return:` accepted as an alias), and the return type uses `:rtype:`.
|
|
502
|
+
- Raised exceptions use `:raises Exception: description` (`:raise`, `:except` and `:exception` are accepted as aliases).
|
|
503
|
+
- Informational directives such as `.. note::`, `.. warning::`, `.. seealso::` and `.. example::` are recognized and their indented bodies are formatted while the directive header is preserved.
|
|
504
|
+
|
|
411
505
|
### Codeblocks
|
|
412
506
|
|
|
413
507
|
**Google / Numpy** (identical for this example)
|
|
@@ -589,6 +683,8 @@ You don't wrap text because wrapping is a skill issue. You let photons travel un
|
|
|
589
683
|
| <div style="width:70px">Resource</div> | <div style="width:100px">Description</div> | <div style="width:130px">Link</div>
|
|
590
684
|
|---|---|---|
|
|
591
685
|
| PEP 257 - Docstring Conventions | Documents the semantics and conventions associated with Python docstrings. | [Link](https://peps.python.org/pep-0257/) |
|
|
686
|
+
| PEP 287 - reStructuredText Docstring Format | Specifies a reStructuredText-based markup convention for Python docstrings, the basis of the Sphinx/reST style. | [Link](https://peps.python.org/pep-0287) |
|
|
687
|
+
| PEP 484 - Type Hints | Provides a standard syntax for type annotations in Python. | [Link](https://peps.python.org/pep-0484/) |
|
|
592
688
|
| Google Python Style Guide | Lists *dos and don'ts* for Python programs. | [Link](https://google.github.io/styleguide/pyguide.html#s3.8-comments-and-docstrings) |
|
|
593
689
|
| Numpy Style Guide | Describes the syntax and best practices for docstrings used with the numpydoc extension for Sphinx | [Link](https://numpydoc.readthedocs.io/en/latest/format.html) |
|
|
594
690
|
| Types of indentation | Wikipedia article that defines different kinds of indentation | [Link](https://en.wikipedia.org/wiki/Indentation_(typesetting)) |
|
|
@@ -610,6 +706,8 @@ You don't wrap text because wrapping is a skill issue. You let photons travel un
|
|
|
610
706
|
| `0.2.1` | 2026-06-11 | Feature update | <ul><li>Added the `-V`/`--version` command to the CLI.</li><li>Added the `--exclude` command to the CLI.</li><li>Added the `--diff` command to the CLI.</li><li>Added the 'Demo' part to to the `README.md`.</ul> |
|
|
611
707
|
| `0.3.0` | 2026-07-20 | Feature update & bug fixes | <ul><li>Introduced a style-agnostic intermediate representation (IR) model and refactored parsing into an abstract base class hierarchy. Google and NumPy docstrings now parse into the same IR via `IndentationBasedParser` subclasses, enabling lossless conversion between styles. Structured list parsing is delegated to style-specific implementations to handle syntactic differences (Google's inline `name (type):` vs. NumPy's `name : type` on separate lines).</li><li>Refactored rendering to match the parser architecture: `DocstringRendererBase` (ABC) with style-specific subclasses that implement two hooks — section header formatting (Google's `Args:` vs. NumPy's `Parameters` + underline) and section body indentation rules (confirmed against numpy's own docstrings to keep bodies flush with headers, unlike Google). Keyword translation between styles happens automatically during conversion.</li><li>Code sections and Python REPL blocks now also can be created outside the 'Example(s)' section.</li><li>Fixed a bug when codeblock sections contain blank lines.</li><li>Fixed a bug when the docstring starts immediately with an (un)ordered list.</li><li>Removed `detect-lists` as CLI parameter, because the logic should always be applied if the docstring contains (un)ordered lists.</li><li>Changed behaviour for empty docstrings so that it consistent with Ruff.</li></ul> |
|
|
612
708
|
| `0.3.1` | 2026-07-21 | Small fixes | <ul><li>The `style` parameter does not have a default argument anymore, instead it always has to be configured explicitly by the user.</li><li>Fixed a bug where one-line docstrings inside indented scopes (e.g. class or method bodies) were wrapped to multiple lines prematurely, due to indentation being counted twice when checking against the configured line length.</li> <li>Added an exception, consistent with Ruff, allowing a one-line docstring to exceed the configured line length by up to 3 characters when only the closing triple quotes would otherwise be pushed onto their own line. This prevents docstring_tailor and Ruff from repeatedly reformatting the same docstring back and forth when both are run as pre-commit hooks.</li></ul> |
|
|
709
|
+
| `0.4.0` | 2026-07-24 | Feature update | <ul><li>Added support for the Sphinx/reST docstring `style`, covering both parsing and rendering. Sphinx docstrings now parse into the same style-agnostic intermediate representation (IR) as Google and NumPy, enabling lossless conversion in all directions between the three styles (`format --style sphinx` and `convert` to/from `sphinx`).</li><li>Extracted a shared `DocstringParserBase` holding the style-agnostic flat-content pipeline; `IndentationBasedParser` (Google/NumPy) and the new directive-based `SphinxDocstringParser` both build on it, reusing the existing pipeline rather than duplicating logic.</li><li>The Sphinx parser handles info-field lists (`:param:`/`:type:`, `:returns:`/`:rtype:`, `:raises:`), the inline parameter form (`:param str name:`), field/type pairing by name, tag aliases (`:return:`, `:raise:`, `:except:`, `:exception:`), and informational directives (`.. note::`, `.. warning::`, `.. seealso::`, `.. example::`).</li><li>The Sphinx renderer emits a contiguous info-field list with separate `:type:`/`:rtype:` lines, with hanging-indent wrapping of field bodies.</li><li>Modelled an absent parameter type as `None` in the IR (never fabricated), and updated the Google and NumPy renderers to degrade gracefully when a type is undocumented.</li><li>Reorganized the golden-file test fixtures into per-style folders (`google/`, `sphinx/`, `convert/`), with the fixture style folder derived automatically from each test case's configuration.</li></ul> |
|
|
710
|
+
| `0.4.1` | 2026-09-12 | Robustness & bug fixes | <ul><li> Made structured-list parsing resilient to malformed entries: a missing `:` separator or a missing `(type)` annotation no longer crashes. The entry's text is preserved (with name and type left unset) and rendered as written, so formatting never fails on imperfect docstrings.</li><li> Modelled a `Raises` entry that lacks a `:` as an unnamed error (`error_type` set to `None`) in the IR, consistent with how unclassifiable parameter entries are handled, and guarded the Google, Numpy and Sphinx renderes accordingly.</li><li> The CLI now processes each file independently: files that cannot be read, decoded, or parsed as Python are reported with a specific error message and skipped, so a single malformed file no longer aborts the whole run.</li><li> Added a Ruff-style run summary (e.g. `2 files reformatted, 1 file left unchanged`) and stopped rewriting files whose content did not change.</li><li>Aligned exit codes with Ruff: `format` exits non-zero only on errors (write mode) or when changes are needed (`--diff`), and `convert` exits non-zero only on errors, enabling reliable use in pre-commit and CI.</li><li>Removed a leftover debug `print` from the CLI output.</li><li> Published a `pre-commit-config.yaml` hook definition so `docstring-tailor` can run as a pre-commit / prek hook via `repo:`, with hosted, local, and `prek.toml` setups documented in the README </li></ul> |
|
|
613
711
|
|
|
614
712
|
## Roadmap
|
|
615
713
|
|
|
@@ -627,8 +725,16 @@ You don't wrap text because wrapping is a skill issue. You let photons travel un
|
|
|
627
725
|
- Formatting module for the remaining docstring formats (Sphinx, Epydoc), driven
|
|
628
726
|
entirely by the same IR already used for Google and NumPy.
|
|
629
727
|
|
|
728
|
+
### Finish the Sphinx/reST implementation
|
|
729
|
+
|
|
730
|
+
The core Sphinx style (parameters, returns, raises, and the common admonitions) is supported, but full coverage of every Sphinx construct is not there yet. Currently, unrecognized constructs are passed through as plain paragraph text — never dropped, but not parsed into a structured section, so they don't wrap as fields and don't convert to a Google/NumPy equivalent. The following would close that gap:
|
|
731
|
+
|
|
732
|
+
- Class attribute fields (`:ivar:`, `:cvar:`, `:var:` paired with `:vartype:`), mapped onto the same `Attributes` section Google and NumPy already expose, so class docstrings round-trip and convert like the other styles. Requires a new field-pairing path in the parser, an `Attributes` render path in the renderer, and a fix to the keyword translation tables (which currently fold Attributes into Parameters).
|
|
733
|
+
- Keyword-only argument fields (`:keyword:` / `:kwarg:` with `:kwtype:`), aliased onto the existing parameter/type handling.
|
|
734
|
+
- The broader set of admonition directives (`.. tip::`, `.. important::`, `.. caution::`, `.. attention::`, `.. hint::`, `.. danger::`, `.. error::`, `.. todo::`, `.. deprecated::`, `.. versionadded::`, `.. versionchanged::`), added to the directive-to-header map so their bodies format as named paragraphs. Headers without a Google/NumPy equivalent pass through unchanged on conversion.
|
|
735
|
+
- Literal-body directives (`.. code-block::`, `.. math::`) — more involved, because their content must be preserved verbatim and never reflowed, and there is no clean equivalent in the current IR. Needs a dedicated literal-block IR node before it can be handled safely.
|
|
736
|
+
|
|
630
737
|
### Nice to have
|
|
631
|
-
- Make sure the package can be used as a pre-commit hook.
|
|
632
738
|
- LSP (Language Server Protocol) support, enabling real-time feedback on malformed
|
|
633
739
|
docstrings directly in editors like VS Code, PyCharm and Neovim. Built on top of
|
|
634
740
|
the validation layer and the existing parser, with `pygls` handling the protocol.
|
|
@@ -11,16 +11,17 @@ Formats Python docstrings to PEP 257 style with configurable line length.
|
|
|
11
11
|
## Table of Contents
|
|
12
12
|
1. [Demo](#demo)
|
|
13
13
|
2. [Installation](#Installation)
|
|
14
|
-
3. [Quick start](#
|
|
14
|
+
3. [Quick start](#quick-start)
|
|
15
15
|
4. [API Overview](#api-overview)
|
|
16
16
|
- [Command](#command)
|
|
17
17
|
- [Options](#options)
|
|
18
18
|
- [Examples](#examples)
|
|
19
|
-
5. [
|
|
20
|
-
6. [
|
|
21
|
-
7. [
|
|
22
|
-
8. [
|
|
23
|
-
9. [
|
|
19
|
+
5. [Pre-commit and prek hook](#pre-commit-and-prek-hook)
|
|
20
|
+
6. [Example docstrings](#example-docstrings)
|
|
21
|
+
7. [What Your Line Length Says About You!](#what-your-line-length-says-about-you)
|
|
22
|
+
8. [Resources](#resources)
|
|
23
|
+
9. [Release Notes](#release-notes)
|
|
24
|
+
10. [Roadmap](#roadmap)
|
|
24
25
|
|
|
25
26
|
## Demo
|
|
26
27
|
|
|
@@ -83,7 +84,7 @@ line-length = 88
|
|
|
83
84
|
line-length = 88
|
|
84
85
|
```
|
|
85
86
|
|
|
86
|
-
Define a docstring style.
|
|
87
|
+
Define a docstring style. Three styles are currently supported: [Google](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html), [NumPy](https://numpydoc.readthedocs.io/en/latest/format.html), and [Sphinx](https://www.sphinx-doc.org/en/master/usage/domains/python.html#info-field-lists). The style always has to be configured explicitly.
|
|
87
88
|
|
|
88
89
|
```bash
|
|
89
90
|
uv run docstring_tailor format --style numpy
|
|
@@ -102,6 +103,8 @@ To convert existing docstrings from one style to another, use the `convert` comm
|
|
|
102
103
|
uv run docstring_tailor convert my_file.py --from-style google --to-style numpy
|
|
103
104
|
```
|
|
104
105
|
|
|
106
|
+
`docstring-tailor` can also run as a [pre-commit](https://pre-commit.com/) / [prek](https://github.com/j178/prek) hook so docstrings stay formatted on every commit, see [Pre-commit and prek hook](#pre-commit-and-prek-hook).
|
|
107
|
+
|
|
105
108
|
## API Overview
|
|
106
109
|
|
|
107
110
|
### Commands
|
|
@@ -132,20 +135,20 @@ If no paths are provided, `docstring_tailor` will attempt to locate and format f
|
|
|
132
135
|
|
|
133
136
|
| <div style="width:140px">Option</div> | <div style="width:50px">Type</div> | <div style="width:80px">Default</div> | Description |
|
|
134
137
|
|---|---|---|---|
|
|
135
|
-
| `--line-length` | `int` | 100
|
|
136
|
-
| `--style` | `str` |
|
|
137
|
-
| `--exclude` | `str` | —
|
|
138
|
-
| `--diff` | flag | —
|
|
138
|
+
| `--line-length` | `int` | 100 | Maximum number of characters allowed per line after formatting. |
|
|
139
|
+
| `--style` | `str` | *required* | Docstring style to format to. `google`, `numpy` or `sphinx`. |
|
|
140
|
+
| `--exclude` | `str` | — | A glob pattern for paths to exclude. Can be passed multiple times. Single-path patterns (e.g. `tests`, `*.pyi`) match by name anywhere in the tree. Relative patterns (e.g. `src/generated/*.py`) match against the path relative to the project root. |
|
|
141
|
+
| `--diff` | flag | — | Print a unified diff of changes to stdout instead of modifying files. No files are written when this flag is set. |
|
|
139
142
|
|
|
140
143
|
#### `convert`
|
|
141
144
|
|
|
142
145
|
| <div style="width:140px">Option</div> | <div style="width:50px">Type</div> | <div style="width:80px">Default</div> | Description |
|
|
143
146
|
|---|---|---|---|
|
|
144
|
-
| `--from-style` | `str` | *required* | Docstring style to convert from. `google` or `
|
|
145
|
-
| `--to-style` | `str` | *required* | Docstring style to convert to. `google` or `
|
|
146
|
-
| `--line-length` | `int` | 100
|
|
147
|
-
| `--exclude` | `str` | —
|
|
148
|
-
| `--diff` | flag | —
|
|
147
|
+
| `--from-style` | `str` | *required* | Docstring style to convert from. `google`, `numpy` or `sphinx`. |
|
|
148
|
+
| `--to-style` | `str` | *required* | Docstring style to convert to. `google`, `numpy` or `sphinx`. Must differ from `--from-style`. |
|
|
149
|
+
| `--line-length` | `int` | 100 | Maximum number of characters allowed per line after formatting. |
|
|
150
|
+
| `--exclude` | `str` | — | A glob pattern for paths to exclude. Can be passed multiple times. Single-path patterns (e.g. `tests`, `*.pyi`) match by name anywhere in the tree. Relative patterns (e.g. `src/generated/*.py`) match against the path relative to the project root. |
|
|
151
|
+
| `--diff` | flag | — | Print a unified diff of changes to stdout instead of modifying files. No files are written when this flag is set. |
|
|
149
152
|
|
|
150
153
|
`--from-style` and `--to-style` have no config-file or default fallback — both must be given explicitly on every `convert` invocation, and must be different from each other.
|
|
151
154
|
|
|
@@ -184,6 +187,56 @@ style = "google"
|
|
|
184
187
|
exclude = ["tests", "src/generated/*.py"]
|
|
185
188
|
```
|
|
186
189
|
|
|
190
|
+
## Pre-commit and prek hook
|
|
191
|
+
|
|
192
|
+
`docstring-tailor` can run as a [pre-commit](https://pre-commit.com/) hook, and works unchanged with [prek](https://github.com/j178/prek), the drop-in reimplementation, so docstrings stay formatted automatically on every commit. When the hook reformats a file it rewrites it in place and stops the commit so you can review and re-stage the change, exactly like the Ruff or Black hooks.
|
|
193
|
+
|
|
194
|
+
### Hosted hook
|
|
195
|
+
|
|
196
|
+
Add the following to your `.pre-commit-config.yaml`:
|
|
197
|
+
|
|
198
|
+
```yaml
|
|
199
|
+
repos:
|
|
200
|
+
- repo: https://github.com/AukeB/docstring-tailor
|
|
201
|
+
rev: 0.4.1
|
|
202
|
+
hooks:
|
|
203
|
+
- id: docstring-tailor
|
|
204
|
+
args: ["--style", "google"]
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
- `--style` is required, use `google`, `numpy` or `sphinx`. Pass any other option (`--line-length`, `--exclude`) through `args`, or set them in a `[tool.docstring_tailor]` config block instead. `args` is a single flat list where every flag and every value is its own quoted element (e.g. `["--style", "google", "--line-length", "88"]`, with numbers quoted). Install the hook once with `pre-commit install` (or `prek install`).
|
|
208
|
+
|
|
209
|
+
Prefer TOML? prek also reads a native `prek.toml`, a drop-in alternative to `.pre-commit-config.yaml` (upstream `pre-commit` ignores it). The same hook in `prek.toml`
|
|
210
|
+
|
|
211
|
+
```toml
|
|
212
|
+
[[repos]]
|
|
213
|
+
repo = "https://github.com/AukeB/docstring-tailor"
|
|
214
|
+
rev = "v0.4.1"
|
|
215
|
+
hooks= [
|
|
216
|
+
{ id = "docstring-tailor", args = ["--style", "google"] },
|
|
217
|
+
]
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
`prek util yaml-to-toml` converts an existing YAML config for you.
|
|
221
|
+
|
|
222
|
+
### Local hook
|
|
223
|
+
|
|
224
|
+
If your project already installs `docstring-tailor` (for example as a `uv` dev dependency), you can run it as a `repo: local` hook. pre-commit then never clones a remote repo, which is handy in locked-down or offline environments:
|
|
225
|
+
|
|
226
|
+
```yaml
|
|
227
|
+
repos:
|
|
228
|
+
- repo: local
|
|
229
|
+
hooks:
|
|
230
|
+
- id: docstring-tailor
|
|
231
|
+
name: docstring-tailor
|
|
232
|
+
entry: uv run docstring_tailor format --style google
|
|
233
|
+
language: system
|
|
234
|
+
types: [python]
|
|
235
|
+
pass_filenames: true
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
For CI, run `docstring-tailor format --diff` instead: it never writes files and exits non-zero when any docstring would change, so it can gate a pipeline.
|
|
239
|
+
|
|
187
240
|
## Example docstrings
|
|
188
241
|
|
|
189
242
|
1. [Module docstring](#module-docstring)
|
|
@@ -375,6 +428,47 @@ def example_function(example_argument_1: str, example_argument_2: int) -> str:
|
|
|
375
428
|
- In the `Examples` section, start the Python REPL with `>>>` and use `...` for continuation lines, matching Pydoc conventions — same as Google style.
|
|
376
429
|
- These same section keywords can also be used in module or class docstrings — for example, a `Parameters` section in a class docstring, or an `Examples` section in a module docstring.
|
|
377
430
|
|
|
431
|
+
**Sphinx / reST**
|
|
432
|
+
|
|
433
|
+
```python
|
|
434
|
+
def example_function(example_argument_1: str, example_argument_2: int) -> str:
|
|
435
|
+
"""Demonstrates a Sphinx-style function docstring with multiple
|
|
436
|
+
sections.
|
|
437
|
+
|
|
438
|
+
This function exists purely as a formatting example and
|
|
439
|
+
illustrates how parameters, return values, and raised exceptions
|
|
440
|
+
are documented using reStructuredText info fields.
|
|
441
|
+
|
|
442
|
+
:param example_argument_1: First example input value used to
|
|
443
|
+
construct a formatted result string.
|
|
444
|
+
:type example_argument_1: str
|
|
445
|
+
:param example_argument_2: Second example input value used to
|
|
446
|
+
influence the transformation logic.
|
|
447
|
+
:type example_argument_2: int
|
|
448
|
+
:returns: A formatted string combining both input arguments into a
|
|
449
|
+
single human-readable representation.
|
|
450
|
+
:rtype: str
|
|
451
|
+
:raises ValueError: Raised when example_argument_2 is negative or
|
|
452
|
+
zero, as only positive integers are considered valid in this
|
|
453
|
+
demonstration.
|
|
454
|
+
|
|
455
|
+
.. note::
|
|
456
|
+
Additional informational directives such as ``.. note::`` and
|
|
457
|
+
``.. warning::`` are recognized and preserved during
|
|
458
|
+
formatting.
|
|
459
|
+
"""
|
|
460
|
+
if example_argument_2 <= 0:
|
|
461
|
+
raise ValueError("example_argument_2 must be positive")
|
|
462
|
+
|
|
463
|
+
return f"{example_argument_1}-{example_argument_2}"
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
- Parameters are documented with `:param name: description` info fields, and their types with a separate `:type name: type` field. An inline form (`:param str name: description`) is also accepted on input and is split into the two-field form on output.
|
|
467
|
+
- Types are genuinely optional in reST: a `:param:` without a matching `:type:` is preserved as-is, and `docstring_tailor` never fabricates a type when one is absent.
|
|
468
|
+
- Return values use `:returns:` (with `:return:` accepted as an alias), and the return type uses `:rtype:`.
|
|
469
|
+
- Raised exceptions use `:raises Exception: description` (`:raise`, `:except` and `:exception` are accepted as aliases).
|
|
470
|
+
- Informational directives such as `.. note::`, `.. warning::`, `.. seealso::` and `.. example::` are recognized and their indented bodies are formatted while the directive header is preserved.
|
|
471
|
+
|
|
378
472
|
### Codeblocks
|
|
379
473
|
|
|
380
474
|
**Google / Numpy** (identical for this example)
|
|
@@ -556,6 +650,8 @@ You don't wrap text because wrapping is a skill issue. You let photons travel un
|
|
|
556
650
|
| <div style="width:70px">Resource</div> | <div style="width:100px">Description</div> | <div style="width:130px">Link</div>
|
|
557
651
|
|---|---|---|
|
|
558
652
|
| PEP 257 - Docstring Conventions | Documents the semantics and conventions associated with Python docstrings. | [Link](https://peps.python.org/pep-0257/) |
|
|
653
|
+
| PEP 287 - reStructuredText Docstring Format | Specifies a reStructuredText-based markup convention for Python docstrings, the basis of the Sphinx/reST style. | [Link](https://peps.python.org/pep-0287) |
|
|
654
|
+
| PEP 484 - Type Hints | Provides a standard syntax for type annotations in Python. | [Link](https://peps.python.org/pep-0484/) |
|
|
559
655
|
| Google Python Style Guide | Lists *dos and don'ts* for Python programs. | [Link](https://google.github.io/styleguide/pyguide.html#s3.8-comments-and-docstrings) |
|
|
560
656
|
| Numpy Style Guide | Describes the syntax and best practices for docstrings used with the numpydoc extension for Sphinx | [Link](https://numpydoc.readthedocs.io/en/latest/format.html) |
|
|
561
657
|
| Types of indentation | Wikipedia article that defines different kinds of indentation | [Link](https://en.wikipedia.org/wiki/Indentation_(typesetting)) |
|
|
@@ -577,6 +673,8 @@ You don't wrap text because wrapping is a skill issue. You let photons travel un
|
|
|
577
673
|
| `0.2.1` | 2026-06-11 | Feature update | <ul><li>Added the `-V`/`--version` command to the CLI.</li><li>Added the `--exclude` command to the CLI.</li><li>Added the `--diff` command to the CLI.</li><li>Added the 'Demo' part to to the `README.md`.</ul> |
|
|
578
674
|
| `0.3.0` | 2026-07-20 | Feature update & bug fixes | <ul><li>Introduced a style-agnostic intermediate representation (IR) model and refactored parsing into an abstract base class hierarchy. Google and NumPy docstrings now parse into the same IR via `IndentationBasedParser` subclasses, enabling lossless conversion between styles. Structured list parsing is delegated to style-specific implementations to handle syntactic differences (Google's inline `name (type):` vs. NumPy's `name : type` on separate lines).</li><li>Refactored rendering to match the parser architecture: `DocstringRendererBase` (ABC) with style-specific subclasses that implement two hooks — section header formatting (Google's `Args:` vs. NumPy's `Parameters` + underline) and section body indentation rules (confirmed against numpy's own docstrings to keep bodies flush with headers, unlike Google). Keyword translation between styles happens automatically during conversion.</li><li>Code sections and Python REPL blocks now also can be created outside the 'Example(s)' section.</li><li>Fixed a bug when codeblock sections contain blank lines.</li><li>Fixed a bug when the docstring starts immediately with an (un)ordered list.</li><li>Removed `detect-lists` as CLI parameter, because the logic should always be applied if the docstring contains (un)ordered lists.</li><li>Changed behaviour for empty docstrings so that it consistent with Ruff.</li></ul> |
|
|
579
675
|
| `0.3.1` | 2026-07-21 | Small fixes | <ul><li>The `style` parameter does not have a default argument anymore, instead it always has to be configured explicitly by the user.</li><li>Fixed a bug where one-line docstrings inside indented scopes (e.g. class or method bodies) were wrapped to multiple lines prematurely, due to indentation being counted twice when checking against the configured line length.</li> <li>Added an exception, consistent with Ruff, allowing a one-line docstring to exceed the configured line length by up to 3 characters when only the closing triple quotes would otherwise be pushed onto their own line. This prevents docstring_tailor and Ruff from repeatedly reformatting the same docstring back and forth when both are run as pre-commit hooks.</li></ul> |
|
|
676
|
+
| `0.4.0` | 2026-07-24 | Feature update | <ul><li>Added support for the Sphinx/reST docstring `style`, covering both parsing and rendering. Sphinx docstrings now parse into the same style-agnostic intermediate representation (IR) as Google and NumPy, enabling lossless conversion in all directions between the three styles (`format --style sphinx` and `convert` to/from `sphinx`).</li><li>Extracted a shared `DocstringParserBase` holding the style-agnostic flat-content pipeline; `IndentationBasedParser` (Google/NumPy) and the new directive-based `SphinxDocstringParser` both build on it, reusing the existing pipeline rather than duplicating logic.</li><li>The Sphinx parser handles info-field lists (`:param:`/`:type:`, `:returns:`/`:rtype:`, `:raises:`), the inline parameter form (`:param str name:`), field/type pairing by name, tag aliases (`:return:`, `:raise:`, `:except:`, `:exception:`), and informational directives (`.. note::`, `.. warning::`, `.. seealso::`, `.. example::`).</li><li>The Sphinx renderer emits a contiguous info-field list with separate `:type:`/`:rtype:` lines, with hanging-indent wrapping of field bodies.</li><li>Modelled an absent parameter type as `None` in the IR (never fabricated), and updated the Google and NumPy renderers to degrade gracefully when a type is undocumented.</li><li>Reorganized the golden-file test fixtures into per-style folders (`google/`, `sphinx/`, `convert/`), with the fixture style folder derived automatically from each test case's configuration.</li></ul> |
|
|
677
|
+
| `0.4.1` | 2026-09-12 | Robustness & bug fixes | <ul><li> Made structured-list parsing resilient to malformed entries: a missing `:` separator or a missing `(type)` annotation no longer crashes. The entry's text is preserved (with name and type left unset) and rendered as written, so formatting never fails on imperfect docstrings.</li><li> Modelled a `Raises` entry that lacks a `:` as an unnamed error (`error_type` set to `None`) in the IR, consistent with how unclassifiable parameter entries are handled, and guarded the Google, Numpy and Sphinx renderes accordingly.</li><li> The CLI now processes each file independently: files that cannot be read, decoded, or parsed as Python are reported with a specific error message and skipped, so a single malformed file no longer aborts the whole run.</li><li> Added a Ruff-style run summary (e.g. `2 files reformatted, 1 file left unchanged`) and stopped rewriting files whose content did not change.</li><li>Aligned exit codes with Ruff: `format` exits non-zero only on errors (write mode) or when changes are needed (`--diff`), and `convert` exits non-zero only on errors, enabling reliable use in pre-commit and CI.</li><li>Removed a leftover debug `print` from the CLI output.</li><li> Published a `pre-commit-config.yaml` hook definition so `docstring-tailor` can run as a pre-commit / prek hook via `repo:`, with hosted, local, and `prek.toml` setups documented in the README </li></ul> |
|
|
580
678
|
|
|
581
679
|
## Roadmap
|
|
582
680
|
|
|
@@ -594,8 +692,16 @@ You don't wrap text because wrapping is a skill issue. You let photons travel un
|
|
|
594
692
|
- Formatting module for the remaining docstring formats (Sphinx, Epydoc), driven
|
|
595
693
|
entirely by the same IR already used for Google and NumPy.
|
|
596
694
|
|
|
695
|
+
### Finish the Sphinx/reST implementation
|
|
696
|
+
|
|
697
|
+
The core Sphinx style (parameters, returns, raises, and the common admonitions) is supported, but full coverage of every Sphinx construct is not there yet. Currently, unrecognized constructs are passed through as plain paragraph text — never dropped, but not parsed into a structured section, so they don't wrap as fields and don't convert to a Google/NumPy equivalent. The following would close that gap:
|
|
698
|
+
|
|
699
|
+
- Class attribute fields (`:ivar:`, `:cvar:`, `:var:` paired with `:vartype:`), mapped onto the same `Attributes` section Google and NumPy already expose, so class docstrings round-trip and convert like the other styles. Requires a new field-pairing path in the parser, an `Attributes` render path in the renderer, and a fix to the keyword translation tables (which currently fold Attributes into Parameters).
|
|
700
|
+
- Keyword-only argument fields (`:keyword:` / `:kwarg:` with `:kwtype:`), aliased onto the existing parameter/type handling.
|
|
701
|
+
- The broader set of admonition directives (`.. tip::`, `.. important::`, `.. caution::`, `.. attention::`, `.. hint::`, `.. danger::`, `.. error::`, `.. todo::`, `.. deprecated::`, `.. versionadded::`, `.. versionchanged::`), added to the directive-to-header map so their bodies format as named paragraphs. Headers without a Google/NumPy equivalent pass through unchanged on conversion.
|
|
702
|
+
- Literal-body directives (`.. code-block::`, `.. math::`) — more involved, because their content must be preserved verbatim and never reflowed, and there is no clean equivalent in the current IR. Needs a dedicated literal-block IR node before it can be handled safely.
|
|
703
|
+
|
|
597
704
|
### Nice to have
|
|
598
|
-
- Make sure the package can be used as a pre-commit hook.
|
|
599
705
|
- LSP (Language Server Protocol) support, enabling real-time feedback on malformed
|
|
600
706
|
docstrings directly in editors like VS Code, PyCharm and Neovim. Built on top of
|
|
601
707
|
the validation layer and the existing parser, with `pygls` handling the protocol.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "docstring-tailor"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.1"
|
|
4
4
|
description = "Formats Python docstrings to PEP 257 style with configurable line length."
|
|
5
5
|
authors = [{ name = "Auke Bruinsma", email = "afbruinsma@gmail.com" }]
|
|
6
6
|
license = { file = "LICENSE" }
|
|
@@ -16,7 +16,7 @@ class DocstringStyle(str, Enum):
|
|
|
16
16
|
epydoc = "epydoc"
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
SUPPORTED_STYLES = {DocstringStyle.google, DocstringStyle.numpy}
|
|
19
|
+
SUPPORTED_STYLES = {DocstringStyle.google, DocstringStyle.numpy, DocstringStyle.sphinx}
|
|
20
20
|
|
|
21
21
|
# Argument: '--line-length'
|
|
22
22
|
LINE_LENGTH_MIN: int = 30
|
|
@@ -21,6 +21,7 @@ RE_PATTERN_STRUCTURED_LIST_NAME_AND_TYPE = re.compile(
|
|
|
21
21
|
)
|
|
22
22
|
RE_PATTERN_NUMPY_SECTION_UNDERLINE = re.compile(r"^-+$")
|
|
23
23
|
|
|
24
|
+
|
|
24
25
|
# =========================================
|
|
25
26
|
# Constants used for all docstring formats.
|
|
26
27
|
# =========================================
|
|
@@ -53,23 +54,13 @@ UNORDERED_LIST_MARKER: str = "- "
|
|
|
53
54
|
ORDERED_LIST_SEPARATOR = ". "
|
|
54
55
|
|
|
55
56
|
|
|
56
|
-
# ====================================================
|
|
57
|
-
# Constants used for multiple docstings (but not all).
|
|
58
|
-
# ====================================================
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
# Related to StructuredList sections (Use in Google and Numpy format)
|
|
62
|
-
STRUCTURED_LIST_DESCRIPTION_SEPARATOR: str = ":"
|
|
63
|
-
PARAMETER_TYPE_ANNOTATION_OPEN: str = "("
|
|
64
|
-
PARAMETER_TYPE_ANNOTATION_CLOSE: str = ")"
|
|
65
|
-
|
|
66
|
-
|
|
67
57
|
# ============================================
|
|
68
58
|
# Constants used for single docstring formats.
|
|
69
59
|
# ============================================
|
|
70
60
|
|
|
71
61
|
|
|
72
|
-
# Google
|
|
62
|
+
# === Google ===
|
|
63
|
+
|
|
73
64
|
GOOGLE_NAMED_PARAGRAPH_SECTIONS = frozenset(
|
|
74
65
|
{
|
|
75
66
|
"Note",
|
|
@@ -91,14 +82,17 @@ GOOGLE_ALL_SECTION_KEYWORDS = (
|
|
|
91
82
|
GOOGLE_NAMED_PARAGRAPH_SECTIONS | GOOGLE_STRUCTURED_LIST_SECTIONS
|
|
92
83
|
)
|
|
93
84
|
|
|
85
|
+
GOOGLE_STRUCTURED_LIST_DESCRIPTION_SEPARATOR: str = ":"
|
|
86
|
+
|
|
87
|
+
# === NumPy ---
|
|
94
88
|
|
|
95
|
-
# NumPy
|
|
96
89
|
NUMPY_ITEM_SECTIONS = frozenset(
|
|
97
90
|
{"Attributes", "Methods", "Parameters", "Raises", "Receives", "Returns", "Yields"}
|
|
98
91
|
)
|
|
99
92
|
NUMPY_PLAIN_SECTIONS = frozenset({"Examples", "Notes", "References", "See Also"})
|
|
100
93
|
NUMPY_SECTION_HEADERS = NUMPY_ITEM_SECTIONS | NUMPY_PLAIN_SECTIONS
|
|
101
94
|
|
|
95
|
+
NUMPY_STRUCTURED_LIST_NAME_TYPE_SEPARATOR: str = ":"
|
|
102
96
|
|
|
103
97
|
# Sphinx/reST-style
|
|
104
98
|
SPHINX_ITEM_DIRECTIVES = frozenset({":param", ":raises", ":returns", ":rtype", ":type"})
|
|
@@ -107,8 +101,58 @@ SPHINX_PLAIN_DIRECTIVES = frozenset(
|
|
|
107
101
|
)
|
|
108
102
|
SPHINX_DIRECTIVES = SPHINX_ITEM_DIRECTIVES | SPHINX_PLAIN_DIRECTIVES
|
|
109
103
|
|
|
104
|
+
# === Sphinx ===
|
|
105
|
+
|
|
106
|
+
# Sphinx field tags, grouped by the IR section they map to. Aliases (singular
|
|
107
|
+
# and plural spellings) are accepted on input; the renderer emits one canonical
|
|
108
|
+
# spelling per group. ':param' also accepts an inline type ':param <type>
|
|
109
|
+
# <name>:', handled by the parser.
|
|
110
|
+
SPHINX_PARAM_TAGS = frozenset({":param", ":parameter", ":arg", ":argument"})
|
|
111
|
+
SPHINX_TYPE_TAGS = frozenset({":type"})
|
|
112
|
+
SPHINX_RETURN_TAGS = frozenset({":return", ":returns"})
|
|
113
|
+
SPHINX_RTYPE_TAGS = frozenset({":rtype"})
|
|
114
|
+
SPHINX_RAISE_TAGS = frozenset({":raise", ":raises", ":except", ":exception"})
|
|
115
|
+
|
|
116
|
+
# All field tags that open a structured-list entry (as opposed to type metadata
|
|
117
|
+
# for a preceding entry). Used to detect the start of a field-list block.
|
|
118
|
+
SPHINX_FIELD_TAGS = (
|
|
119
|
+
SPHINX_PARAM_TAGS
|
|
120
|
+
| SPHINX_TYPE_TAGS
|
|
121
|
+
| SPHINX_RETURN_TAGS
|
|
122
|
+
| SPHINX_RTYPE_TAGS
|
|
123
|
+
| SPHINX_RAISE_TAGS
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Canonical section keywords used for Sphinx StructuredList nodes in the IR, so
|
|
127
|
+
# keyword translation and rendering share one vocabulary.
|
|
128
|
+
SPHINX_KEYWORD_PARAMETERS: str = "Parameters"
|
|
129
|
+
SPHINX_KEYWORD_RETURNS: str = "Returns"
|
|
130
|
+
SPHINX_KEYWORD_RAISES: str = "Raises"
|
|
131
|
+
|
|
132
|
+
# Canonical Sphinx directive-to-header mapping for admonition sections rendered
|
|
133
|
+
# as NamedParagraph nodes.
|
|
134
|
+
SPHINX_DIRECTIVE_HEADERS: dict[str, str] = {
|
|
135
|
+
".. note::": "Note",
|
|
136
|
+
".. warning::": "Warning",
|
|
137
|
+
".. seealso::": "See Also",
|
|
138
|
+
".. example::": "Example",
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
# Reverse mapping: canonical header to its Sphinx directive marker, for
|
|
142
|
+
# rendering NamedParagraph nodes back to reST directives.
|
|
143
|
+
SPHINX_HEADER_DIRECTIVES: dict[str, str] = {
|
|
144
|
+
header: directive for directive, header in SPHINX_DIRECTIVE_HEADERS.items()
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Canonical Sphinx field-tag spellings emitted my the renderer.
|
|
148
|
+
SPHINX_RENDER_PARAM_TAG: str = ":param"
|
|
149
|
+
SPHINX_RENDER_TYPE_TAG: str = ":type"
|
|
150
|
+
SPHINX_RENDER_RETURNS_TAG: str = ":returns"
|
|
151
|
+
SPHINX_RENDER_RTYPE_TAG: str = ":rtype"
|
|
152
|
+
SPHINX_RENDER_RAISES_TAG: str = ":raises"
|
|
153
|
+
|
|
154
|
+
# === Epydoc ===
|
|
110
155
|
|
|
111
|
-
# Epydoc-style docstring tag markers.
|
|
112
156
|
EPYDOC_ITEM_TAGS = frozenset({"@param", "@raise", "@return", "@rtype", "@type"})
|
|
113
157
|
EPYDOC_PLAIN_TAGS = frozenset({"@note", "@warning"})
|
|
114
158
|
EPYDOC_TAGS = EPYDOC_ITEM_TAGS | EPYDOC_PLAIN_TAGS
|
|
@@ -62,12 +62,17 @@ class StructuredListParameter:
|
|
|
62
62
|
name (str | None): The variable or attribute name, or None when the
|
|
63
63
|
entry has no name -- the conventional shape for Returns and Yields
|
|
64
64
|
entries, which document only the type.
|
|
65
|
-
type (str): The annotated type of the variable
|
|
65
|
+
type (str | None): The annotated type of the variable, or None when no
|
|
66
|
+
type was documented. Types are optional in some styles (e.g.
|
|
67
|
+
Sphinx/reST field lists relying on PEP 484 signature hints), so
|
|
68
|
+
absence is modelled explicitly rather than as an empty string,
|
|
69
|
+
mirroring the name field. Renderers omit the type entirely when it
|
|
70
|
+
is None.
|
|
66
71
|
description (str): The description of the variable.
|
|
67
72
|
"""
|
|
68
73
|
|
|
69
74
|
name: str | None
|
|
70
|
-
type: str
|
|
75
|
+
type: str | None
|
|
71
76
|
description: str
|
|
72
77
|
|
|
73
78
|
|
|
@@ -77,11 +82,14 @@ class StructuredListError:
|
|
|
77
82
|
section.
|
|
78
83
|
|
|
79
84
|
Attributes:
|
|
80
|
-
error_type (str): The exception type being raised
|
|
85
|
+
error_type (str | None): The exception type being raised, or None when
|
|
86
|
+
the entry had no ':' separator and could not be split. The whole
|
|
87
|
+
entry is preserved in description instead, mirroring how an
|
|
88
|
+
unclassifiable parameter entry is handled.
|
|
81
89
|
description (str): The description of when the error is raised.
|
|
82
90
|
"""
|
|
83
91
|
|
|
84
|
-
error_type: str
|
|
92
|
+
error_type: str | None
|
|
85
93
|
description: str
|
|
86
94
|
|
|
87
95
|
|