docstring-tailor 0.2.1.dev6__tar.gz → 0.3.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 (108) hide show
  1. docstring_tailor-0.3.0/PKG-INFO +636 -0
  2. docstring_tailor-0.3.0/README.md +603 -0
  3. {docstring_tailor-0.2.1.dev6 → docstring_tailor-0.3.0}/makefile +2 -2
  4. {docstring_tailor-0.2.1.dev6 → docstring_tailor-0.3.0}/pyproject.toml +2 -2
  5. docstring_tailor-0.3.0/src/docstring_tailor/__init__.py +1 -0
  6. {docstring_tailor-0.2.1.dev6 → docstring_tailor-0.3.0}/src/docstring_tailor/cli_config.py +1 -4
  7. docstring_tailor-0.3.0/src/docstring_tailor/constants.py +114 -0
  8. docstring_tailor-0.3.0/src/docstring_tailor/docstring_visitor.py +255 -0
  9. docstring_tailor-0.3.0/src/docstring_tailor/ir_model.py +132 -0
  10. docstring_tailor-0.3.0/src/docstring_tailor/main.py +295 -0
  11. docstring_tailor-0.3.0/src/docstring_tailor/parser/indentation_based/google_docstring_parser.py +121 -0
  12. docstring_tailor-0.3.0/src/docstring_tailor/parser/indentation_based/indentation_based_parser.py +387 -0
  13. docstring_tailor-0.3.0/src/docstring_tailor/parser/indentation_based/numpy_docstring_parser.py +162 -0
  14. docstring_tailor-0.3.0/src/docstring_tailor/parser/indentation_based/structured_list_parser.py +213 -0
  15. docstring_tailor-0.3.0/src/docstring_tailor/parser/parser_factory.py +41 -0
  16. docstring_tailor-0.3.0/src/docstring_tailor/renderer/base_renderer.py +623 -0
  17. docstring_tailor-0.3.0/src/docstring_tailor/renderer/google_renderer.py +77 -0
  18. docstring_tailor-0.3.0/src/docstring_tailor/renderer/numpy_renderer.py +99 -0
  19. docstring_tailor-0.3.0/src/docstring_tailor/renderer/renderer_factory.py +35 -0
  20. docstring_tailor-0.3.0/src/docstring_tailor/utils/__init__.py +1 -0
  21. {docstring_tailor-0.2.1.dev6 → docstring_tailor-0.3.0}/src/docstring_tailor/utils/utils_cli.py +4 -3
  22. {docstring_tailor-0.2.1.dev6 → docstring_tailor-0.3.0}/src/docstring_tailor/utils/utils_file_system.py +17 -14
  23. docstring_tailor-0.3.0/src/docstring_tailor/utils/utils_formatting.py +76 -0
  24. docstring_tailor-0.3.0/src/docstring_tailor/utils/utils_keyword_translation.py +128 -0
  25. docstring_tailor-0.3.0/src/docstring_tailor/utils/utils_list_detection.py +197 -0
  26. docstring_tailor-0.3.0/src/docstring_tailor/utils/utils_parsing.py +121 -0
  27. docstring_tailor-0.3.0/src/docstring_tailor/utils/utils_printing.py +76 -0
  28. docstring_tailor-0.3.0/temp.md +0 -0
  29. {docstring_tailor-0.2.1.dev6 → docstring_tailor-0.3.0}/tests/cases/config_model.py +54 -1
  30. docstring_tailor-0.3.0/tests/cases/formatting_cases.py +226 -0
  31. docstring_tailor-0.3.0/tests/fixtures/class_docstring/class_docstring_100.py +93 -0
  32. docstring_tailor-0.3.0/tests/fixtures/class_docstring/class_docstring_60.py +113 -0
  33. docstring_tailor-0.3.0/tests/fixtures/class_docstring/class_docstring_80.py +100 -0
  34. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/code_block_multiple/code_block_multiple_80.py +17 -0
  35. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/code_block_multiple/code_block_multiple_blank_lines.py +29 -0
  36. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/code_block_singular/code_block_singular_80.py +6 -0
  37. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/code_block_singular/code_block_singular_blank_lines.py +12 -0
  38. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/code_repl_multiple/code_repl_multiple_80.py +28 -0
  39. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/code_repl_multiple/code_repl_multiple_blank_lines.py +40 -0
  40. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/code_repl_singular/code_repl_singular_80.py +24 -0
  41. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/code_repl_singular/code_repl_singular_blank_lines.py +36 -0
  42. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/empty/empty_80.py +1 -0
  43. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/named_paragraph_code_block/named_paragraph_code_block_80.py +7 -0
  44. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/named_paragraph_code_block/named_paragraph_code_block_blank_lines.py +13 -0
  45. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/named_paragraph_code_repl/named_paragraph_code_repl_80.py +25 -0
  46. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/named_paragraph_code_repl/named_paragraph_code_repl_blank_lines.py +36 -0
  47. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/named_paragraph_paragraph/named_paragraph_paragraph_100.py +7 -0
  48. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/named_paragraph_paragraph/named_paragraph_paragraph_60.py +9 -0
  49. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/named_paragraph_paragraph/named_paragraph_paragraph_80.py +7 -0
  50. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/named_paragraph_paragraph/named_paragraph_paragraph_wrong_input.py +21 -0
  51. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/paragraph_and_simple_list/paragraph_and_simple_list_100.py +23 -0
  52. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/paragraph_and_simple_list/paragraph_and_simple_list_60.py +31 -0
  53. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/paragraph_and_simple_list/paragraph_and_simple_list_80.py +25 -0
  54. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/paragraph_and_simple_list/paragraph_and_simple_list_wrong_input.py +41 -0
  55. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/paragraph_multi_line/paragraph_multi_line_100.py +4 -0
  56. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/paragraph_multi_line/paragraph_multi_line_60.py +5 -0
  57. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/paragraph_multi_line/paragraph_multi_line_80.py +5 -0
  58. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/paragraph_multi_line/paragraph_multi_line_wrong_input.py +13 -0
  59. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/paragraph_one_line/paragraph_one_line_100.py +1 -0
  60. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/paragraph_one_line/paragraph_one_line_60.py +1 -0
  61. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/paragraph_one_line/paragraph_one_line_80.py +1 -0
  62. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/paragraph_one_line/paragraph_one_line_wrong_input.py +16 -0
  63. docstring_tailor-0.2.1.dev6/tests/fixtures/module_docstring_ordered_list/module_docstring_ordered_list_100.py → docstring_tailor-0.3.0/tests/fixtures/docstring_elements/simple_list/simple_list_100.py +12 -5
  64. docstring_tailor-0.2.1.dev6/tests/fixtures/module_docstring_ordered_list/module_docstring_ordered_list_60.py → docstring_tailor-0.3.0/tests/fixtures/docstring_elements/simple_list/simple_list_60.py +15 -6
  65. docstring_tailor-0.2.1.dev6/tests/fixtures/module_docstring_ordered_list/module_docstring_ordered_list_80.py → docstring_tailor-0.3.0/tests/fixtures/docstring_elements/simple_list/simple_list_80.py +12 -5
  66. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/simple_list/simple_list_wrong_input.py +35 -0
  67. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/structured_list/structured_list_100.py +22 -0
  68. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/structured_list/structured_list_60.py +31 -0
  69. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/structured_list/structured_list_80.py +27 -0
  70. docstring_tailor-0.3.0/tests/fixtures/docstring_elements/structured_list/structured_list_wrong_input.py +48 -0
  71. docstring_tailor-0.2.1.dev6/tests/fixtures/function_docstring_complex/function_docstring_complex_100.py → docstring_tailor-0.3.0/tests/fixtures/function_docstring/function_docstring_100.py +48 -23
  72. docstring_tailor-0.2.1.dev6/tests/fixtures/function_docstring_complex/function_docstring_complex_60.py → docstring_tailor-0.3.0/tests/fixtures/function_docstring/function_docstring_60.py +54 -29
  73. docstring_tailor-0.2.1.dev6/tests/fixtures/function_docstring_complex/function_docstring_complex_80.py → docstring_tailor-0.3.0/tests/fixtures/function_docstring/function_docstring_80.py +48 -25
  74. docstring_tailor-0.3.0/tests/fixtures/module_docstring/module_docstring_100.py +52 -0
  75. docstring_tailor-0.3.0/tests/fixtures/module_docstring/module_docstring_60.py +61 -0
  76. docstring_tailor-0.3.0/tests/fixtures/module_docstring/module_docstring_80.py +57 -0
  77. docstring_tailor-0.2.1.dev6/tests/test_formatting.py → docstring_tailor-0.3.0/tests/test_docstring_tailor.py +1 -1
  78. {docstring_tailor-0.2.1.dev6/src/docstring_tailor → docstring_tailor-0.3.0/tests}/utils/utils_testing.py +5 -4
  79. {docstring_tailor-0.2.1.dev6 → docstring_tailor-0.3.0}/uv.lock +1 -1
  80. docstring_tailor-0.2.1.dev6/PKG-INFO +0 -426
  81. docstring_tailor-0.2.1.dev6/README.md +0 -393
  82. docstring_tailor-0.2.1.dev6/src/docstring_tailor/constants.py +0 -64
  83. docstring_tailor-0.2.1.dev6/src/docstring_tailor/docstring_visitor.py +0 -272
  84. docstring_tailor-0.2.1.dev6/src/docstring_tailor/main.py +0 -148
  85. docstring_tailor-0.2.1.dev6/src/docstring_tailor/multi_line_docstring_formatter.py +0 -477
  86. docstring_tailor-0.2.1.dev6/src/docstring_tailor/utils/__init__.py +0 -1
  87. docstring_tailor-0.2.1.dev6/src/docstring_tailor/utils/utils_formatting.py +0 -86
  88. docstring_tailor-0.2.1.dev6/src/docstring_tailor/utils/utils_list_detection.py +0 -88
  89. docstring_tailor-0.2.1.dev6/tests/cases/__init__.py +0 -1
  90. docstring_tailor-0.2.1.dev6/tests/cases/formatting_cases.py +0 -90
  91. docstring_tailor-0.2.1.dev6/tests/fixtures/all_docstrings/all_docstrings_100.py +0 -71
  92. docstring_tailor-0.2.1.dev6/tests/fixtures/all_docstrings/all_docstrings_60.py +0 -107
  93. docstring_tailor-0.2.1.dev6/tests/fixtures/all_docstrings/all_docstrings_80.py +0 -85
  94. docstring_tailor-0.2.1.dev6/tests/fixtures/all_docstrings/all_docstrings_too_long.py +0 -32
  95. docstring_tailor-0.2.1.dev6/tests/fixtures/all_docstrings/all_docstrings_too_short.py +0 -167
  96. docstring_tailor-0.2.1.dev6/tests/fixtures/module_docstring_blank_lines/module_docstring_blank_lines.py +0 -8
  97. docstring_tailor-0.2.1.dev6/tests/fixtures/module_docstring_blank_lines/module_docstring_blank_lines_100.py +0 -6
  98. docstring_tailor-0.2.1.dev6/tests/fixtures/module_docstring_example_backticks/module_docstring_example_backticks.py +0 -13
  99. docstring_tailor-0.2.1.dev6/tests/fixtures/module_docstring_example_backticks/module_docstring_example_backticks_60.py +0 -14
  100. docstring_tailor-0.2.1.dev6/tests/fixtures/module_docstring_example_tildes/module_docstring_example_tildes.py +0 -13
  101. docstring_tailor-0.2.1.dev6/tests/fixtures/module_docstring_example_tildes/module_docstring_example_tildes_60.py +0 -14
  102. docstring_tailor-0.2.1.dev6/tests/fixtures/module_docstring_ordered_list/module_docstring_ordered_list_too_long.py +0 -7
  103. {docstring_tailor-0.2.1.dev6 → docstring_tailor-0.3.0}/.gitignore +0 -0
  104. {docstring_tailor-0.2.1.dev6 → docstring_tailor-0.3.0}/LICENSE +0 -0
  105. {docstring_tailor-0.2.1.dev6/src/docstring_tailor → docstring_tailor-0.3.0/tests/cases}/__init__.py +0 -0
  106. /docstring_tailor-0.2.1.dev6/tests/fixtures/module_docstring_empty/module_docstring_empty_blank_lines.py → /docstring_tailor-0.3.0/tests/fixtures/docstring_elements/empty/empty_blank_lines.py +0 -0
  107. /docstring_tailor-0.2.1.dev6/tests/fixtures/module_docstring_empty/module_docstring_empty.py → /docstring_tailor-0.3.0/tests/fixtures/docstring_elements/empty/empty_no_space.py +0 -0
  108. {docstring_tailor-0.2.1.dev6 → docstring_tailor-0.3.0}/tests/fixtures/readme_examples/readme_examples.py +0 -0
@@ -0,0 +1,636 @@
1
+ Metadata-Version: 2.4
2
+ Name: docstring-tailor
3
+ Version: 0.3.0
4
+ Summary: Formats Python docstrings to PEP 257 style with configurable line length.
5
+ Author-email: Auke Bruinsma <afbruinsma@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Auke Bruinsma
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ License-File: LICENSE
28
+ Classifier: License :: OSI Approved :: MIT License
29
+ Requires-Python: >=3.11
30
+ Requires-Dist: libcst>=1.8.6
31
+ Requires-Dist: typer>=0.26.4
32
+ Description-Content-Type: text/markdown
33
+
34
+ # Docstring Tailor 🪡
35
+
36
+ Formats Python docstrings to PEP 257 style with configurable line length.
37
+
38
+ [![PyPI Version](https://img.shields.io/pypi/v/docstring-tailor?color=lightblue)](https://pypi.org/project/docstring-tailor/)
39
+ [![License](https://img.shields.io/pypi/l/docstring-tailor?color=lightblue)](https://pypi.org/project/docstring-tailor/)
40
+ [![Wheel](https://img.shields.io/pypi/wheel/docstring-tailor?color=lightblue)](https://pypi.org/project/docstring-tailor/)
41
+ [![Downloads](https://img.shields.io/pypi/dm/docstring-tailor?color=lightblue)](https://pypi.org/project/docstring-tailor/)
42
+
43
+
44
+ ## Table of Contents
45
+ 1. [Demo](#demo)
46
+ 2. [Installation](#Installation)
47
+ 3. [Quick start](#quick_start)
48
+ 4. [API Overview](#api-overview)
49
+ - [Command](#command)
50
+ - [Options](#options)
51
+ - [Examples](#examples)
52
+ 5. [Example docstrings](#example-docstrings)
53
+ 6. [What Your Line Length Says About You!](#what-your-line-length-says-about-you)
54
+ 7. [Resources](#resources)
55
+ 8. [Release Notes](#release_notes)
56
+ 9. [Roadmap](#roadmap)
57
+
58
+ ## Demo
59
+
60
+ <details>
61
+ <summary><b>Show demo</b></summary>
62
+
63
+ <br>
64
+
65
+ - `docstring-tailor` formats docstrings to fit a given line length, while preserving its structure throughout — For exapmle blank lines, argument indentation, continuation line alignment, and code blocks in the Examples section all remain intact.
66
+ - **Note**: The slider in the [Marimo notebook](https://marimo.io/) is not part of the package. It was created solely to illustrate how the output changes continuously as the line length varies.
67
+
68
+ <br>
69
+
70
+ <img src="https://github.com/user-attachments/assets/983ae257-472d-465e-9924-9d90bca10f0d" alt="" />
71
+ <img src="https://i.imgur.com/2uG5HNh.gif" alt="" />
72
+
73
+ If the image does not show, click [here](https://github.com/user-attachments/assets/983ae257-472d-465e-9924-9d90bca10f0d).
74
+
75
+ </details>
76
+
77
+ ## Installation
78
+
79
+ Installation with [UV](https://docs.astral.sh/uv/) (recommended)
80
+ ```bash
81
+ uv add --dev docstring-tailor
82
+ ```
83
+
84
+ Or with pip:
85
+
86
+ ```bash
87
+ pip install docstring-tailor
88
+ ```
89
+
90
+ ## Quick start
91
+
92
+ Run on a single file or directory:
93
+
94
+ ```bash
95
+ uv run docstring_tailor format my_file.py
96
+ uv run docstring_tailor format my_folder
97
+ ```
98
+ Multiple files and/or folders are also accepted. Without a file path or folder path, it will try to locate the `src` folder.
99
+
100
+ The default line length is 100. To customise it:
101
+
102
+ ```bash
103
+ uv run docstring_tailor format --line-length 88
104
+ ```
105
+
106
+ Configure it permanently in `pyproject.toml` or in `docstring_tailor.toml`:
107
+
108
+ ```toml
109
+ # pyproject.toml
110
+ [tool.docstring_tailor]
111
+ line-length = 88
112
+ ```
113
+
114
+ ```toml
115
+ # docstring_tailor.toml
116
+ line-length = 88
117
+ ```
118
+
119
+ Define a docstring style. Two styles are currently supported: [Google](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) and [NumPy](https://numpydoc.readthedocs.io/en/latest/format.html). Google is the default. Explicit configuration:
120
+
121
+ ```bash
122
+ uv run docstring_tailor format --style numpy
123
+ ```
124
+
125
+ or in `pyproject.toml`
126
+
127
+ ```toml
128
+ [tool.docstring_tailor]
129
+ style = "google"
130
+ ```
131
+
132
+ To convert existing docstrings from one style to another, use the `convert` command instead:
133
+
134
+ ```bash
135
+ uv run docstring_tailor convert my_file.py --from-style google --to-style numpy
136
+ ```
137
+
138
+ ## API Overview
139
+
140
+ ### Commands
141
+
142
+ `docstring_tailor` has two commands: `format` and `convert`.
143
+
144
+ **`format`** reformats docstrings in place, in a single style — use this for everyday formatting (adjusting line length, wrapping, whitespace) without changing the docstring style itself.
145
+
146
+ **`convert`** reparses and re-renders docstrings from one style into another — use this the one time you need to migrate a codebase, or part of one, between styles.
147
+
148
+ ```bash
149
+ uv run docstring_tailor format [PATHS ...] [OPTIONS]
150
+ uv run docstring_tailor convert [PATHS ...] --from-style STYLE --to-style STYLE [OPTIONS]
151
+ ```
152
+ `PATHS` may contain one or more files and/or directories.
153
+ Examples:
154
+ ```bash
155
+ uv run docstring_tailor format my_file.py
156
+ uv run docstring_tailor format src/
157
+ uv run docstring_tailor format src/ tests/test_file.py
158
+ uv run docstring_tailor convert src/ --from-style google --to-style numpy
159
+ ```
160
+ If no paths are provided, `docstring_tailor` will attempt to locate and format files inside the `src` directory.
161
+
162
+ ### Options
163
+
164
+ #### `format`
165
+
166
+ | <div style="width:140px">Option</div> | <div style="width:50px">Type</div> | <div style="width:80px">Default</div> | Description |
167
+ |---|---|---|---|
168
+ | `--line-length` | `int` | 100 | Maximum number of characters allowed per line after formatting. |
169
+ | `--style` | `str` | google | Docstring style to format to. `google` or `numpy`. |
170
+ | `--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. |
171
+ | `--diff` | flag | — | Print a unified diff of changes to stdout instead of modifying files. No files are written when this flag is set. |
172
+
173
+ #### `convert`
174
+
175
+ | <div style="width:140px">Option</div> | <div style="width:50px">Type</div> | <div style="width:80px">Default</div> | Description |
176
+ |---|---|---|---|
177
+ | `--from-style` | `str` | *required* | Docstring style to convert from. `google` or `numpy`. |
178
+ | `--to-style` | `str` | *required* | Docstring style to convert to. `google` or `numpy`. Must differ from `--from-style`. |
179
+ | `--line-length` | `int` | 100 | Maximum number of characters allowed per line after formatting. |
180
+ | `--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. |
181
+ | `--diff` | flag | — | Print a unified diff of changes to stdout instead of modifying files. No files are written when this flag is set. |
182
+
183
+ `--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
+
185
+ #### Global
186
+
187
+ | <div style="width:140px">Option</div> | <div style="width:50px">Type</div> | <div style="width:80px">Default</div> | Description |
188
+ |---|---|---|---|
189
+ | `--version`, `-V` | flag | — | Print the installed version and exit. |
190
+ | `--help` | flag | — | Show the help message and exit. |
191
+
192
+ ### Examples
193
+
194
+ `CLI`
195
+ ```bash
196
+ uv run docstring_tailor format src/ --line-length 88
197
+ uv run docstring_tailor format my_file.py --style numpy
198
+ uv run docstring_tailor format src/ --exclude tests --exclude "src/generated/*.py"
199
+ uv run docstring_tailor format src/ --diff
200
+ uv run docstring_tailor convert src/ --from-style google --to-style numpy
201
+ uv run docstring_tailor convert my_file.py --from-style numpy --to-style google --diff
202
+ uv run docstring_tailor --version
203
+ uv run docstring_tailor --help
204
+ ```
205
+ `pyproject.toml`
206
+ ```toml
207
+ [tool.docstring_tailor]
208
+ line-length = 88
209
+ style = "google"
210
+ exclude = ["tests", "src/generated/*.py"]
211
+ ```
212
+
213
+ `docstring_tailor.toml`
214
+ ```toml
215
+ line-length = 88
216
+ style = "google"
217
+ exclude = ["tests", "src/generated/*.py"]
218
+ ```
219
+
220
+ ## Example docstrings
221
+
222
+ 1. [Module docstring](#module-docstring)
223
+ 2. [Class docstring](#class-docstring)
224
+ 3. [Function docstring](#function-docstring)
225
+ 4. [Codeblocks](#codeblocks)
226
+ 5. [Other sections](#other-sections)
227
+ 6. [Unordered and numbered lists](#unordered-and-numbered-lists)
228
+
229
+ ### Module docstring
230
+
231
+ **Google / Numpy** (identical for this example)
232
+
233
+ ```python
234
+ """Demonstrates a minimal Google or Numpy style module docstring.
235
+
236
+ This module exists as a formatting example and illustrates the typical
237
+ structure of a docstring, including a concise summary line followed by
238
+ an additional descriptive paragraph.
239
+ """
240
+ ```
241
+
242
+ - Leave a **blank line** between the summary line and the more elaborate description. This applies to both styles.
243
+
244
+ ### Class docstring
245
+
246
+ **Google**
247
+
248
+ ```python
249
+ class ExampleConfiguration:
250
+ """Represents a configuration object used to demonstrate Google-
251
+ style class docstrings.
252
+
253
+ This class is provided as a formatting example and illustrates how
254
+ attributes are documented consistently in the Google docstring
255
+ style.
256
+
257
+ Attributes:
258
+ example_argument_1 (str): Stores the first configuration value
259
+ provided at initialization.
260
+ example_argument_2 (int): Stores the second configuration
261
+ value provided at initialization.
262
+ """
263
+ ```
264
+
265
+ - The `Attributes` section is recognized by the formatter and triggers special indentation rules for attribute entries.
266
+ - If an attribute's description runs onto the next line, it **must be indented one extra level beyond the attribute name**. The formatter relies on this indentation to tell a new attribute entry apart from a continuation of the previous one.
267
+
268
+ **NumPy**
269
+
270
+ ```python
271
+ class ExampleConfiguration:
272
+ """Represents a configuration object used to demonstrate NumPy-
273
+ style class docstrings.
274
+
275
+ This class is provided as a formatting example and illustrates how
276
+ attributes are documented consistently in the NumPy docstring
277
+ style.
278
+
279
+ Attributes
280
+ ----------
281
+ example_argument_1 : str
282
+ Stores the first configuration value provided at
283
+ initialization.
284
+ example_argument_2 : int
285
+ Stores the second configuration value provided at
286
+ initialization.
287
+ """
288
+ ```
289
+
290
+ - The `Attributes` section header is followed by a line of dashes matching its length (`----------`).
291
+ - Each attribute is written as `name : type`, and its description starts on the next line. Continuation lines are indented to line up with the description, one level deeper than the `name : type` line.
292
+
293
+ ### Function docstring
294
+
295
+ **Google**
296
+
297
+ ```python
298
+ def example_function(example_argument_1: str, example_argument_2: int) -> str:
299
+ """Demonstrates a Google-style function docstring with multiple
300
+ sections.
301
+
302
+ This function exists purely as a formatting example and
303
+ illustrates how Args, Returns, Examples, and Raises sections are
304
+ structured in a Google-style docstring.
305
+
306
+ Args:
307
+ example_argument_1 (str): First example input value used to
308
+ construct a formatted result string.
309
+ example_argument_2 (int): Second example input value used to
310
+ influence the transformation logic.
311
+
312
+ Returns:
313
+ str: A formatted string combining both input arguments into a
314
+ single human-readable representation.
315
+
316
+ Examples:
317
+ Basic usage with typical inputs produces a simple combined
318
+ string:
319
+
320
+ >>> example_function("alpha", 3)
321
+ 'alpha-3'
322
+
323
+ You can also write text in between two Python REPL sections,
324
+ and this part will be formatted, while the two small code
325
+ sections won't be formatted.
326
+
327
+ >>> example_function(
328
+ ... "beta",
329
+ ... 7,
330
+ ... )
331
+ 'beta-7'
332
+
333
+ Raises:
334
+ ValueError: Raised when example_argument_2 is negative or
335
+ zero, as only positive integers are considered valid in
336
+ this demonstration.
337
+ """
338
+ if example_argument_2 <= 0:
339
+ raise ValueError("example_argument_2 must be positive")
340
+
341
+ return f"{example_argument_1}-{example_argument_2}"
342
+ ```
343
+
344
+ - `Args`, `Returns`, `Examples`, and `Raises` are all keywords recognized by the package.
345
+ - You can use either `Args` or `Arguments` for the argument section, and `Example` or `Examples` for the example section.
346
+ - As with the `Attributes` section above, add **one extra level of indentation** for the description of an argument, return value, or error, whenever it spans multiple lines.
347
+ - In the `Example(s)` section, start the Python REPL with `>>>` and use `...` for continuation lines, matching Pydoc conventions.
348
+ - These same keywords can also be used in module or class docstrings — for example, an `Args` section in a class docstring, or an `Example(s)` section in a module docstring.
349
+
350
+ **NumPy**
351
+
352
+ ```python
353
+ def example_function(example_argument_1: str, example_argument_2: int) -> str:
354
+ """Demonstrates a NumPy-style function docstring with multiple
355
+ sections.
356
+
357
+ This function exists purely as a formatting example and
358
+ illustrates how Parameters, Returns, Examples, and Raises sections
359
+ are structured in a NumPy-style docstring.
360
+
361
+ Parameters
362
+ ----------
363
+ example_argument_1 : str
364
+ First example input value used to construct a formatted
365
+ result string.
366
+ example_argument_2 : int
367
+ Second example input value used to influence the
368
+ transformation logic.
369
+
370
+ Returns
371
+ -------
372
+ str
373
+ A formatted string combining both input arguments into a
374
+ single human-readable representation.
375
+
376
+ Examples
377
+ --------
378
+ Basic usage with typical inputs produces a simple combined string:
379
+
380
+ >>> example_function("alpha", 3)
381
+ 'alpha-3'
382
+
383
+ You can also write text in between two Python REPL sections, and
384
+ this part will be formatted, while the two small code sections
385
+ won't be formatted.
386
+
387
+ >>> example_function(
388
+ ... "beta",
389
+ ... 7,
390
+ ... )
391
+ 'beta-7'
392
+
393
+ Raises
394
+ ------
395
+ ValueError
396
+ Raised when example_argument_2 is negative or zero, as only
397
+ positive integers are considered valid in this demonstration.
398
+ """
399
+ if example_argument_2 <= 0:
400
+ raise ValueError("example_argument_2 must be positive")
401
+
402
+ return f"{example_argument_1}-{example_argument_2}"
403
+ ```
404
+
405
+ - `Parameters`, `Returns`, `Examples`, and `Raises` are all keywords recognized by the package. Each is followed by an underline of dashes matching the header's length.
406
+ - Parameters and return values are documented as `name : type` (or just `type` for a return value), with the description indented on the following line(s).
407
+ - As with the `Attributes` section above, add **one extra level of indentation** for a description that spans multiple lines, so it lines up under the `name : type` entry rather than under the header.
408
+ - In the `Examples` section, start the Python REPL with `>>>` and use `...` for continuation lines, matching Pydoc conventions — same as Google style.
409
+ - 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
+
411
+ ### Codeblocks
412
+
413
+ **Google / Numpy** (identical for this example)
414
+
415
+ ```python
416
+ """Demonstrates a Google or Numpy style module docstring containing
417
+ a code block.
418
+
419
+ This module-level docstring is used as a formatting example and shows
420
+ how code blocks can be embedded inside docstrings using fenced
421
+ delimiters.
422
+
423
+ ~~~
424
+ def example_function(x, y):
425
+ return x + y
426
+ ~~~
427
+ ```
428
+
429
+ - A code block can appear inside the `Example(s)` section, but it doesn't have to — it can also stand on its own outside of it.
430
+ - Either backticks (` ``` `) or tildes (`~~~`) can be used to fence the block, the same way code blocks work in markdown files. This is true for both styles.
431
+
432
+ ### Other sections
433
+
434
+ **Google — Yields**
435
+
436
+ ```python
437
+ def example_generator(n):
438
+ """Generators have a ``Yields`` section instead of a ``Returns``
439
+ section.
440
+
441
+ Args:
442
+ n (int): The upper limit of the range to generate, from 0 to
443
+ `n` - 1.
444
+
445
+ Yields:
446
+ int: The next number in the range of 0 to `n` - 1.
447
+ """
448
+ ```
449
+
450
+ **NumPy — Yields**
451
+
452
+ ```python
453
+ def example_generator(n):
454
+ """Generators have a ``Yields`` section instead of a ``Returns``
455
+ section.
456
+
457
+ Parameters
458
+ ----------
459
+ n : int
460
+ The upper limit of the range to generate, from 0 to `n` - 1.
461
+
462
+ Yields
463
+ ------
464
+ int
465
+ The next number in the range of 0 to `n` - 1.
466
+ """
467
+ ```
468
+
469
+ - `Yields` is supported as a drop-in replacement for `Returns` in both styles, for use in generator functions.
470
+
471
+ **Google — Note**
472
+
473
+ ```python
474
+ """Demonstrates a Google-style module docstring containing a Note
475
+ section.
476
+
477
+ This module-level docstring is used as a formatting example and
478
+ illustrates how additional informational sections can be included
479
+ alongside the main description in a Google-style docstring.
480
+
481
+ Note:
482
+ The formatting of this docstring is intentionally designed to test
483
+ how note sections are detected and preserved during docstring
484
+ transformation.
485
+ """
486
+ ```
487
+
488
+ - `Note` is a supported keyword; you can use either `Note` or `Notes`.
489
+
490
+ **NumPy — Notes**
491
+
492
+ ```python
493
+ """Demonstrates a NumPy-style module docstring containing a Notes
494
+ section.
495
+
496
+ This module-level docstring is used as a formatting example and
497
+ illustrates how additional informational sections can be included
498
+ alongside the main description in a NumPy-style docstring.
499
+
500
+ Notes
501
+ -----
502
+ The formatting of this docstring is intentionally designed to test how
503
+ notes sections are detected and preserved during docstring
504
+ transformation.
505
+ """
506
+ ```
507
+
508
+ - `Notes` is the conventional NumPy keyword for this section (as opposed to `Note`, which is the singular form used in Google style).
509
+
510
+ ### Unordered and numbered lists
511
+
512
+ The following examples are valid for **both Google and NumPy** styles, since list formatting isn't tied to a particular section keyword.
513
+
514
+ ```python
515
+ """Demonstrates that docstrings can include structured lists.
516
+
517
+ This module-level docstring is used as a formatting example and shows
518
+ how unordered lists can be represented inside a docstring. Each list
519
+ item is recognized by the formatter and rendered on a separate line.
520
+
521
+ Items:
522
+ - Demonstrates that docstrings may contain structured unordered lists
523
+ that are parsed and formatted consistently by the docstring
524
+ formatter.
525
+ - Shows that each list item is treated as an independent element and
526
+ is wrapped separately when exceeding the configured line length.
527
+ - Illustrates that long list items may span multiple lines while
528
+ preserving indentation and list structure integrity across
529
+ formatting operations.
530
+ """
531
+ ```
532
+
533
+ ```python
534
+ """Demonstrates that docstrings can include structured lists.
535
+
536
+ This module-level docstring is used as a formatting example and shows
537
+ how numbered lists can be represented inside a docstring. Each list
538
+ item is recognized by the formatter and rendered on a separate line.
539
+
540
+ Steps:
541
+ 1. Demonstrates that docstrings may contain structured numbered lists
542
+ that are parsed and formatted consistently by the docstring
543
+ formatter.
544
+ 2. Shows that each list item is treated as a separate logical element
545
+ and is wrapped independently when exceeding the configured line
546
+ length.
547
+ 3. Illustrates that long list items may span multiple lines while
548
+ preserving indentation and list structure integrity across
549
+ formatting operations.
550
+ """
551
+ ```
552
+
553
+ - Unordered and numbered lists can be a nice addition to a docstring from time to time. As with the sections above, **indentation** is what the formatter uses to detect where a new list item begins.
554
+
555
+ ## What Your `line-length` says about You!
556
+
557
+ ### 60 characters per line — The Minimalist Monk 🧘
558
+
559
+ You believe every character has a purpose and every extra column is a personal failure 😤. You keep your docstrings short, your functions tiny, and your emotional attachment to whitespace surprisingly strong. You don't need room to explain your code because your code should be obvious. If a sentence doesn't fit in 60 characters, you simply rewrite the sentence until it does. Brevity is not a preference for you. Brevity is a lifestyle.
560
+
561
+ ### 80 characters per line — The Digital Archaeologist 🦖
562
+
563
+ You have been programming for at least half a century, and you still remember when computers were mysterious machines instead of fancy boxes running websites. You believe software peaked before color displays, and honestly, you are a little disappointed with where things went 😔. You know that everything after assembly was already too high-level and a mistake, because real programming means understanding the machine, not asking some framework to do the work for you. You hate GenAI because you believe thinking is the programmer's job. Python? Far too comfortable. Your motto is simple: real programmers toggle individual transistors. Everything after that is just frameworks ⚙️.
564
+
565
+ ### 88 characters per line — The Black Ritualist 🖤
566
+
567
+ You are nostalgic for the comforting embrace of Black, where every formatting decision has already been made for you. You don't want to risk the chaos of choosing your own line length because what if you accidentally become different? 😨 You trust the collective wisdom of the formatter, the community, and the thousands of developers who came before you. You are not afraid of change exactly; you are just very comfortable knowing that Black has already decided your fate. Peace through formatting consistency ✨.
568
+
569
+ ### 100 characters per line — The Peacekeeper 🤝
570
+
571
+ You chose 100 characters because you wanted everyone to be happy, including the people who review your code and the people who have to read it on a laptop. You looked at 80 characters and thought it felt a little cramped, but you looked at 120 and felt society was moving too fast 😅. You carefully live in the middle, avoiding formatting wars and unnecessary debates. Nobody writes angry comments about your line length, nobody praises it either, and that quiet neutrality is exactly the comfortable existence you wanted.
572
+
573
+ ### 240 characters per line — The Ultrawide Warrior 🖥️
574
+
575
+ You claim you increased your line length because it improves readability, but everyone knows the real reason: you want people to notice your enormous ultrawide monitor. You paid for every pixel, and you refuse to leave any of them unemployed 😎. Your docstrings stretch from one side of the screen to the other just to prove your setup is bigger than everyone else's. Whenever someone questions your line length, you casually mention your monitor resolution, refresh rate, and somehow the GPU model.
576
+
577
+ ### 500 characters per line — The Horizon Programmer 🌌
578
+
579
+ You don't wrap text because wrapping is a skill issue. You let photons travel uninterrupted across the entire display because your docstrings deserve the full cinematic experience 🎬. You combine programming with neck exercises, allowing you to safely skip neck day at the gym. You don't use a monitor anymore; you rent a cinema, sit in the back row, and program using a telescope pointed at your code 🔭. Your Git diffs require geological surveys, satellite mapping, and a team of explorers. NASA can read your docstrings from orbit 🚀.
580
+
581
+ ## Resources
582
+
583
+ ### Core resources
584
+
585
+ | <div style="width:70px">Resource</div> | <div style="width:100px">Description</div> | <div style="width:130px">Link</div>
586
+ |---|---|---|
587
+ | PEP 257 - Docstring Conventions | Documents the semantics and conventions associated with Python docstrings. | [Link](https://peps.python.org/pep-0257/) |
588
+ | 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) |
589
+ | 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) |
590
+ | Types of indentation | Wikipedia article that defines different kinds of indentation | [Link](https://en.wikipedia.org/wiki/Indentation_(typesetting)) |
591
+
592
+ ### Additional resources
593
+
594
+ | <div style="width:70px">Resource</div> | <div style="width:100px">Description</div> | <div style="width:130px">Link</div>
595
+ |---|---|---|
596
+ | PEP 8 - Style Guide for Python Code | Gives coding conventions for the Python code comprising the standard library in the main Python distribution. | [Link](https://peps.python.org/pep-0008/) |
597
+
598
+
599
+ ## Release Notes
600
+
601
+ | <div style="width:70px">Version</div> | <div style="width:100px">Release date</div> | <div style="width:130px">Type</div> | Details |
602
+ |---|---|---|---|
603
+ | `0.1.0` | 2026-05-31 | Initial release | First public release of `docstring-tailor`. Includes <ul><li>Automatic docstring wrapping for module, class and function docstrings, for both one line and multi line docstrings, with a configurable `line-length` parameter.</li><li>Paragraph-aware formatting, differentiating between 'Args', 'Examples' or normal text sections.</li> <li> Docstring support for the Google `style` (Numpy, Sphinx, Epydoc not yet supported). </li><li>TOML-based configuration support.</li><li> Test coverage: 52% </ul> |
604
+ | `0.1.1` | 2026-05-31 | Documentation update | Updated the `README.md` file with the 'Installation' and 'Quick Start' section. |
605
+ | `0.2.0` | 2026-06-07 | Feature update | <ul><li>Implemented the `detect-lists` parameter, adding support for unordered and ordered (numbered) lists in docstrings. When enabled, list structures are detected automatically and each list item is formatted onto its own line.</li><li>Introduced a declarative golden-file test framework for formatter validation. Test cases are now generated from parametrized templates using Cartesian-product expansion, significantly reducing boilerplate and improving scalability for configuration coverage.</li><li>Expanded this `README.md` with the 'API Overview', 'Release Notes', 'Example docstrings' and 'Roadmap' sections.</li><li>Test coverage: 75%</li></ul> |
606
+ | `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> |
607
+ | `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> |
608
+
609
+ ## Roadmap
610
+
611
+ ### Must have
612
+ - Parsing-time validation: the parser enforces structural rules on a docstring
613
+ (e.g. well-formed sections, consistent indentation, correctly closed lists and
614
+ code blocks) and rejects it if those rules aren't met. Formatting, rendering, and
615
+ conversion must never run on a docstring that fails this check — malformed input
616
+ should be refused at the parsing stage, not silently passed through. A dedicated
617
+ lint/check command exposes these errors directly to the user, with clear and
618
+ actionable messages, rather than only blocking format/convert silently.
619
+ - Parsing module for the remaining docstring formats (Sphinx, Epydoc), extending
620
+ the existing style-agnostic intermediate representation (IR) to cover all four
621
+ major styles.
622
+ - Formatting module for the remaining docstring formats (Sphinx, Epydoc), driven
623
+ entirely by the same IR already used for Google and NumPy.
624
+
625
+ ### Nice to have
626
+ - Make sure the package can be used as a pre-commit hook.
627
+ - LSP (Language Server Protocol) support, enabling real-time feedback on malformed
628
+ docstrings directly in editors like VS Code, PyCharm and Neovim. Built on top of
629
+ the validation layer and the existing parser, with `pygls` handling the protocol.
630
+ Requires position tracking in the IR (line/column numbers per node) as a
631
+ prerequisite, for precise diagnostics and editor highlighting.
632
+ - Make the package available as a VSCode extension.
633
+
634
+ ### Maybe later
635
+ - Parameter that allows the user to format module, class and function docstrings
636
+ independently.