format-docstring 0.2.3__tar.gz → 0.2.5__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.
- {format_docstring-0.2.3 → format_docstring-0.2.5}/.gitignore +3 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/.pre-commit-config.yaml +3 -4
- {format_docstring-0.2.3 → format_docstring-0.2.5}/CHANGELOG.md +18 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/PKG-INFO +1 -1
- {format_docstring-0.2.3 → format_docstring-0.2.5}/format_docstring/line_wrap_numpy.py +105 -21
- {format_docstring-0.2.3 → format_docstring-0.2.5}/format_docstring.egg-info/PKG-INFO +1 -1
- {format_docstring-0.2.3 → format_docstring-0.2.5}/format_docstring.egg-info/SOURCES.txt +1 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/pyproject.toml +1 -1
- format_docstring-0.2.5/tests/test_data/end_to_end/numpy/arg_name_is_default.txt +44 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/signature_sync_yields.txt +49 -1
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_line_wrap_numpy.py +10 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/.github/workflows/python-package.yml +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/.github/workflows/python-publish.yml +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/.pre-commit-hooks.yaml +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/AGENTS.md +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/LICENSE +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/README.md +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/format_docstring/__init__.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/format_docstring/base_fixer.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/format_docstring/config.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/format_docstring/docstring_rewriter.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/format_docstring/line_wrap_google.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/format_docstring/line_wrap_utils.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/format_docstring/main_jupyter.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/format_docstring/main_py.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/format_docstring.egg-info/dependency_links.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/format_docstring.egg-info/entry_points.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/format_docstring.egg-info/requires.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/format_docstring.egg-info/top_level.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/muff.toml +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/requirements.dev +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/setup.cfg +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/__init__.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/helpers.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_base_fixer.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_config.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/README.md +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/colon_spacing_fix.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/contents_that_are_not_wrapped.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/default_value_standardization.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/empty_lines_are_respected.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/examples_section.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/existing_linebreaks_should_not_be_respected.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/fix_rst_backticks.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/four_level_nested_classes.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/indent_four_levels_16_spaces_width_10.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/indent_misaligned_all.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/indent_two_levels_8_spaces.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/line_length_2.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/mismatched_underlines.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/mismatched_underlines_one_dash.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/mismatched_underlines_two_dashes.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/module_level_docstring.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/new_lines_before_and_after.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/no_format_docstring_comment.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/param_signature_without_type.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/parameters_returns_raises_wrapping.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/returns_signature_and_description.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/section_headings_with_colons.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/section_title_fixed.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/sections_notes_examples.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/signature_dont_sync_raises.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/signature_line_is_not_wrapped.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/signature_sync_class_docstrings.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/signature_sync_parameters.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/signature_sync_returns.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/single_line_docstring.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/texts_are_rewrapped.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/very_long_unbreakable_word.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/integration_test/numpy/after.ipynb +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/integration_test/numpy/after.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/integration_test/numpy/after_50.ipynb +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/integration_test/numpy/after_50.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/integration_test/numpy/before.ipynb +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/integration_test/numpy/before.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/jupyter/before.ipynb +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/jupyter/verbose_before.ipynb +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/README.md +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/colon_spacing_fix.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/contents_that_are_not_wrapped.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/default_value_standardization.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/empty_lines_are_respected.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/examples_section.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/existing_linebreaks_should_not_be_respected.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/fix_rst_backticks.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/indent_four_levels_16_spaces_width_10.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/indent_two_levels_8_spaces.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/line_length_2.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/mismatched_underlines.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/mismatched_underlines_one_dash.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/mismatched_underlines_two_dashes.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/module_level_docstring.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/param_signature_without_type.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/parameters_returns_raises_wrapping.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/returns_signature_and_description.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/section_headings_with_colons.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/section_title_fixed.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/sections_notes_examples.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/signature_line_is_not_wrapped.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/texts_are_rewrapped.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/very_long_unbreakable_word.txt +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/playground.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_docstring_rewriter.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_line_wrap_google.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_line_wrap_utils.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_main_jupyter.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_main_py.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_playground.py +0 -0
- {format_docstring-0.2.3 → format_docstring-0.2.5}/tox.ini +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
+
exclude: ^tests/test_data/
|
|
2
3
|
repos:
|
|
3
4
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
4
5
|
rev: v6.0.0
|
|
@@ -13,7 +14,7 @@ repos:
|
|
|
13
14
|
- id: check-yaml
|
|
14
15
|
- id: name-tests-test
|
|
15
16
|
args: [--pytest-test-first]
|
|
16
|
-
exclude: ^tests/
|
|
17
|
+
exclude: ^tests/helpers\.py$
|
|
17
18
|
- id: check-merge-conflict
|
|
18
19
|
- repo: https://github.com/jsh9/muff-pre-commit
|
|
19
20
|
rev: 0.13.2
|
|
@@ -65,7 +66,7 @@ repos:
|
|
|
65
66
|
rev: 0.1.0
|
|
66
67
|
hooks:
|
|
67
68
|
- id: markdown-toc-creator
|
|
68
|
-
exclude: ^
|
|
69
|
+
exclude: ^AGENTS\.md$|^CHANGELOG\.md$
|
|
69
70
|
- repo: https://github.com/jsh9/markdown-heading-numbering
|
|
70
71
|
rev: 0.1.0
|
|
71
72
|
hooks:
|
|
@@ -79,7 +80,6 @@ repos:
|
|
|
79
80
|
args: [-m, format_docstring.main_py, --verbose, diff]
|
|
80
81
|
language: python
|
|
81
82
|
types: [python]
|
|
82
|
-
exclude: ^tests/test_data/
|
|
83
83
|
additional_dependencies:
|
|
84
84
|
- click>=8.0
|
|
85
85
|
- jupyter-notebook-parser>=0.1.4
|
|
@@ -90,7 +90,6 @@ repos:
|
|
|
90
90
|
args: [-m, format_docstring.main_jupyter, --verbose, diff]
|
|
91
91
|
language: python
|
|
92
92
|
files: ^.*\.ipynb$
|
|
93
|
-
exclude: ^tests/test_data/
|
|
94
93
|
additional_dependencies:
|
|
95
94
|
- click>=8.0
|
|
96
95
|
- jupyter-notebook-parser>=0.1.4
|
|
@@ -6,6 +6,24 @@ The format is based on
|
|
|
6
6
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
7
7
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## [0.2.5] - 2025-11-20
|
|
10
|
+
|
|
11
|
+
- Fixed
|
|
12
|
+
- A bug where `Generator[XXX, YYY, ZZZ]` in the return type annotation is not
|
|
13
|
+
parsed correctly (the Yields section should have been XXX)
|
|
14
|
+
- Full diff
|
|
15
|
+
- https://github.com/jsh9/format-docstring/compare/0.2.4...0.2.5
|
|
16
|
+
|
|
17
|
+
## [0.2.4] - 2025-10-27
|
|
18
|
+
|
|
19
|
+
- Fixed
|
|
20
|
+
- A bug where 2nd pair of backticks can't be added for dunder names (such as
|
|
21
|
+
`__init__`)
|
|
22
|
+
- A bug where input args named `default` would get treated incorrectly (the
|
|
23
|
+
tool would confuse it with the default values)
|
|
24
|
+
- Full diff
|
|
25
|
+
- https://github.com/jsh9/format-docstring/compare/0.2.3...0.2.4
|
|
26
|
+
|
|
9
27
|
## [0.2.3] - 2025-10-22
|
|
10
28
|
|
|
11
29
|
- Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: format-docstring
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary: A Python formatter to wrap/adjust docstring lines
|
|
5
5
|
Author-email: jsh9 <25124332+jsh9@users.noreply.github.com>
|
|
6
6
|
Maintainer-email: jsh9 <25124332+jsh9@users.noreply.github.com>
|
|
@@ -197,6 +197,7 @@ def wrap_docstring_numpy( # noqa: C901, PLR0915, TODO: https://github.com/jsh9/
|
|
|
197
197
|
|
|
198
198
|
# Treat top-level lines as signatures
|
|
199
199
|
if leading_indent is None or indent_length <= leading_indent:
|
|
200
|
+
is_yields_section = section_lower_case.startswith('yield')
|
|
200
201
|
if not return_signature_style_determined:
|
|
201
202
|
return_use_multiple_signatures = (
|
|
202
203
|
_detect_multiple_return_signatures(
|
|
@@ -223,6 +224,12 @@ def wrap_docstring_numpy( # noqa: C901, PLR0915, TODO: https://github.com/jsh9/
|
|
|
223
224
|
# Fallback to last component when docstring expects more
|
|
224
225
|
desired_annotation = return_components[-1]
|
|
225
226
|
|
|
227
|
+
if is_yields_section:
|
|
228
|
+
desired_annotation = (
|
|
229
|
+
_unwrap_generator_annotation(desired_annotation)
|
|
230
|
+
or desired_annotation
|
|
231
|
+
)
|
|
232
|
+
|
|
226
233
|
if desired_annotation is None:
|
|
227
234
|
temp_out.append(line)
|
|
228
235
|
i += 1
|
|
@@ -464,19 +471,39 @@ def _standardize_default_value(line: str) -> str:
|
|
|
464
471
|
>>> _standardize_default_value('arg : bool, default: True')
|
|
465
472
|
'arg : bool, default=True'
|
|
466
473
|
"""
|
|
474
|
+
colon_idx = line.find(':')
|
|
475
|
+
if colon_idx == -1:
|
|
476
|
+
return line
|
|
477
|
+
|
|
478
|
+
# `prefix` is everything before the 1st colon (param identifier portion).
|
|
479
|
+
# We leave `prefix` untouched so arg names like `default` aren't rewritten.
|
|
480
|
+
prefix = line[: colon_idx + 1]
|
|
481
|
+
after_colon = line[colon_idx + 1 :]
|
|
482
|
+
|
|
467
483
|
# Check colon format first to avoid matching colons in space-based pattern
|
|
468
|
-
match = _DEFAULT_COLON_RE.match(
|
|
484
|
+
match = _DEFAULT_COLON_RE.match(after_colon)
|
|
469
485
|
if match:
|
|
470
|
-
before = match.group(1)
|
|
486
|
+
before = match.group(1)
|
|
487
|
+
if before.strip() == '':
|
|
488
|
+
return line
|
|
489
|
+
|
|
471
490
|
default_value = match.group(2).strip()
|
|
472
|
-
|
|
491
|
+
rebuilt_suffix = f'{before.rstrip()}, default={default_value}'
|
|
492
|
+
return f'{prefix}{rebuilt_suffix}'
|
|
473
493
|
|
|
474
494
|
# Try space-separated format with optional "is"
|
|
475
|
-
match = _DEFAULT_SPACE_RE.match(
|
|
495
|
+
match = _DEFAULT_SPACE_RE.match(after_colon)
|
|
476
496
|
if match:
|
|
477
|
-
before = match.group(1)
|
|
497
|
+
before = match.group(1)
|
|
498
|
+
if before.strip() == '':
|
|
499
|
+
return line
|
|
500
|
+
|
|
501
|
+
# ``before`` still contains any annotation text; tightening the spacing
|
|
502
|
+
# here standardizes the ``", default=..."`` suffix while preserving
|
|
503
|
+
# whatever appeared to the left.
|
|
478
504
|
default_value = match.group(2).strip()
|
|
479
|
-
|
|
505
|
+
rebuilt_suffix = f'{before.rstrip()}, default={default_value}'
|
|
506
|
+
return f'{prefix}{rebuilt_suffix}'
|
|
480
507
|
|
|
481
508
|
return line
|
|
482
509
|
|
|
@@ -598,19 +625,6 @@ def _split_tuple_annotation(annotation: str | None) -> list[str] | None:
|
|
|
598
625
|
except (SyntaxError, ValueError):
|
|
599
626
|
return None
|
|
600
627
|
|
|
601
|
-
def _name_of(node: ast.AST) -> str | None:
|
|
602
|
-
if isinstance(node, ast.Name):
|
|
603
|
-
return node.id
|
|
604
|
-
|
|
605
|
-
if isinstance(node, ast.Attribute):
|
|
606
|
-
base = _name_of(node.value)
|
|
607
|
-
if base is None:
|
|
608
|
-
return None
|
|
609
|
-
|
|
610
|
-
return f'{base}.{node.attr}'
|
|
611
|
-
|
|
612
|
-
return None
|
|
613
|
-
|
|
614
628
|
if isinstance(expr, ast.Subscript):
|
|
615
629
|
base_name = _name_of(expr.value)
|
|
616
630
|
if base_name not in {'tuple', 'Tuple'}:
|
|
@@ -637,6 +651,63 @@ def _split_tuple_annotation(annotation: str | None) -> list[str] | None:
|
|
|
637
651
|
return None
|
|
638
652
|
|
|
639
653
|
|
|
654
|
+
def _name_of(node: ast.AST) -> str | None:
|
|
655
|
+
"""
|
|
656
|
+
Return the dotted name represented by ``node`` if possible.
|
|
657
|
+
"""
|
|
658
|
+
if isinstance(node, ast.Name):
|
|
659
|
+
return node.id
|
|
660
|
+
|
|
661
|
+
if isinstance(node, ast.Attribute):
|
|
662
|
+
base = _name_of(node.value)
|
|
663
|
+
if base is None:
|
|
664
|
+
return None
|
|
665
|
+
|
|
666
|
+
return f'{base}.{node.attr}'
|
|
667
|
+
|
|
668
|
+
return None
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
def _unwrap_generator_annotation(annotation: str | None) -> str | None:
|
|
672
|
+
"""
|
|
673
|
+
Return the first yield type when ``annotation`` is a Generator or
|
|
674
|
+
AsyncGenerator.
|
|
675
|
+
|
|
676
|
+
This is a small helper to keep ``Yields`` sections intuitive; Python
|
|
677
|
+
signatures often annotate generator functions as ``Generator[T, None,
|
|
678
|
+
None]`` but docstrings should spell out the yielded type ``T`` instead of
|
|
679
|
+
the whole container.
|
|
680
|
+
"""
|
|
681
|
+
if annotation is None:
|
|
682
|
+
return None
|
|
683
|
+
|
|
684
|
+
try:
|
|
685
|
+
expr = ast.parse(annotation, mode='eval').body
|
|
686
|
+
except (SyntaxError, ValueError):
|
|
687
|
+
return None
|
|
688
|
+
|
|
689
|
+
if not isinstance(expr, ast.Subscript):
|
|
690
|
+
return None
|
|
691
|
+
|
|
692
|
+
base_name = _name_of(expr.value)
|
|
693
|
+
if base_name is None or base_name.split('.')[-1] not in {
|
|
694
|
+
'Generator',
|
|
695
|
+
'AsyncGenerator',
|
|
696
|
+
}:
|
|
697
|
+
return None
|
|
698
|
+
|
|
699
|
+
slice_node = expr.slice
|
|
700
|
+
if not isinstance(slice_node, ast.Tuple) or not slice_node.elts:
|
|
701
|
+
return None
|
|
702
|
+
|
|
703
|
+
first = slice_node.elts[0]
|
|
704
|
+
segment = ast.get_source_segment(annotation, first)
|
|
705
|
+
if segment is None:
|
|
706
|
+
segment = ast.unparse(first)
|
|
707
|
+
|
|
708
|
+
return segment.strip()
|
|
709
|
+
|
|
710
|
+
|
|
640
711
|
def _detect_multiple_return_signatures(
|
|
641
712
|
lines: list[str],
|
|
642
713
|
start_idx: int,
|
|
@@ -730,12 +801,20 @@ def handle_single_line_docstring(
|
|
|
730
801
|
# or certain punctuation (like > and . for `>>> ` and `... ` literals)
|
|
731
802
|
# Note: We match [^`]+ (anything except backticks) and then check in the
|
|
732
803
|
# replacement function whether it's an external link (contains < followed by >)
|
|
733
|
-
# The opening backtick must not be immediately followed by _ or __ (to avoid
|
|
734
|
-
# matching the trailing backtick of cross-references like `text`_ or `text`__)
|
|
735
804
|
_RST_BACKTICK_PATTERN = re.compile(
|
|
736
805
|
r'(?:^|(?<=\s)|(?<=\()|(?<=[>.]))(?::[\w-]+:)?`(?!_)([^`]+)`(?!`)(?!__)(?!_)'
|
|
737
806
|
)
|
|
738
807
|
|
|
808
|
+
# 2nd-stage fixer for ``__dunder__`` names that slipped past the main pattern
|
|
809
|
+
# because the literal starts with an underscore. Negative lookbehinds/aheads
|
|
810
|
+
# ensure we only touch isolated single-backtick literals and leave
|
|
811
|
+
# cross-references (`name`_ / `name`__) alone.
|
|
812
|
+
_DUNDER_LITERAL_PATTERN = re.compile(
|
|
813
|
+
r'(?<!`)`(__[A-Za-z0-9_]+__)`(?!`)(?!_)(?!__)'
|
|
814
|
+
)
|
|
815
|
+
# Replacement wraps the captured dunder name (group 1) with double backticks.
|
|
816
|
+
_DUNDER_LITERAL_REPLACEMENT = r'``\1``'
|
|
817
|
+
|
|
739
818
|
|
|
740
819
|
def _fix_rst_backticks(docstring: str) -> str:
|
|
741
820
|
"""
|
|
@@ -857,6 +936,11 @@ def _fix_rst_backticks(docstring: str) -> str:
|
|
|
857
936
|
# Process the entire docstring (with REPL lines protected)
|
|
858
937
|
protected_docstring = ''.join(protected_lines)
|
|
859
938
|
processed = _RST_BACKTICK_PATTERN.sub(replace_func, protected_docstring)
|
|
939
|
+
# Upgrade remaining single-backtick ``__dunder__`` literals to double
|
|
940
|
+
# backticks; they are safe literals (not targets or refs) after the guards.
|
|
941
|
+
processed = _DUNDER_LITERAL_PATTERN.sub(
|
|
942
|
+
_DUNDER_LITERAL_REPLACEMENT, processed
|
|
943
|
+
)
|
|
860
944
|
|
|
861
945
|
# Restore REPL lines
|
|
862
946
|
result_lines = processed.splitlines(keepends=True)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: format-docstring
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary: A Python formatter to wrap/adjust docstring lines
|
|
5
5
|
Author-email: jsh9 <25124332+jsh9@users.noreply.github.com>
|
|
6
6
|
Maintainer-email: jsh9 <25124332+jsh9@users.noreply.github.com>
|
|
@@ -39,6 +39,7 @@ tests/test_main_py.py
|
|
|
39
39
|
tests/test_playground.py
|
|
40
40
|
tests/test_data/playground.py
|
|
41
41
|
tests/test_data/end_to_end/numpy/README.md
|
|
42
|
+
tests/test_data/end_to_end/numpy/arg_name_is_default.txt
|
|
42
43
|
tests/test_data/end_to_end/numpy/colon_spacing_fix.txt
|
|
43
44
|
tests/test_data/end_to_end/numpy/contents_that_are_not_wrapped.txt
|
|
44
45
|
tests/test_data/end_to_end/numpy/default_value_standardization.txt
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
LINE_LENGTH: 79
|
|
2
|
+
|
|
3
|
+
**********
|
|
4
|
+
def func1(arg1: str, default: int = 2, _default: str = "value", default_: list[int] | None = None) -> None:
|
|
5
|
+
"""
|
|
6
|
+
Do something
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
----------
|
|
10
|
+
arg1 : str
|
|
11
|
+
Very very very very very very very very very very very very very very very very
|
|
12
|
+
very very very very very very very very very very very very very very very very
|
|
13
|
+
very very very very very very very
|
|
14
|
+
default : int
|
|
15
|
+
Quite quite quite quite quite quite quite quite quite quite quite quite quite quite quite quite
|
|
16
|
+
quite quite quite quite quite quite quite quite quite quite quite quite quite quite quite quite long
|
|
17
|
+
_default : str default is "value"
|
|
18
|
+
Placeholder description for _default parameter.
|
|
19
|
+
default_ : list[int] | None default None
|
|
20
|
+
Placeholder description for default_ parameter.
|
|
21
|
+
"""
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
**********
|
|
25
|
+
def func1(arg1: str, default: int = 2, _default: str = "value", default_: list[int] | None = None) -> None:
|
|
26
|
+
"""
|
|
27
|
+
Do something
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
arg1 : str
|
|
32
|
+
Very very very very very very very very very very very very very very
|
|
33
|
+
very very very very very very very very very very very very very very
|
|
34
|
+
very very very very very very very very very very very
|
|
35
|
+
default : int, default=2
|
|
36
|
+
Quite quite quite quite quite quite quite quite quite quite quite quite
|
|
37
|
+
quite quite quite quite quite quite quite quite quite quite quite quite
|
|
38
|
+
quite quite quite quite quite quite quite quite long
|
|
39
|
+
_default : str, default="value"
|
|
40
|
+
Placeholder description for _default parameter.
|
|
41
|
+
default_ : list[int] | None, default=None
|
|
42
|
+
Placeholder description for default_ parameter.
|
|
43
|
+
"""
|
|
44
|
+
pass
|
|
@@ -51,6 +51,30 @@ def yield_custom_type_2() -> Iterator[tuple['MyType1', "MyType2"]]:
|
|
|
51
51
|
yield 'value'
|
|
52
52
|
yield 1
|
|
53
53
|
|
|
54
|
+
|
|
55
|
+
def yield_middle_non_none() -> Generator[int, str, None]:
|
|
56
|
+
"""
|
|
57
|
+
Yield with send type.
|
|
58
|
+
|
|
59
|
+
Yields
|
|
60
|
+
------
|
|
61
|
+
str
|
|
62
|
+
Should match first annotation element, not send type.
|
|
63
|
+
"""
|
|
64
|
+
yield 1
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def yield_last_non_none() -> Generator[int, None, str]:
|
|
68
|
+
"""
|
|
69
|
+
Yield with return type.
|
|
70
|
+
|
|
71
|
+
Yields
|
|
72
|
+
------
|
|
73
|
+
str
|
|
74
|
+
Should match first annotation element, not return type.
|
|
75
|
+
"""
|
|
76
|
+
yield 1
|
|
77
|
+
|
|
54
78
|
**********
|
|
55
79
|
from collections.abc import Generator, Iterator
|
|
56
80
|
|
|
@@ -73,7 +97,7 @@ def yield_named() -> Generator[int, None, None]:
|
|
|
73
97
|
|
|
74
98
|
Yields
|
|
75
99
|
------
|
|
76
|
-
result :
|
|
100
|
+
result : int
|
|
77
101
|
Should match annotation.
|
|
78
102
|
"""
|
|
79
103
|
yield 1
|
|
@@ -101,3 +125,27 @@ def yield_custom_type_2() -> Iterator[tuple['MyType1', "MyType2"]]:
|
|
|
101
125
|
"""
|
|
102
126
|
yield 'value'
|
|
103
127
|
yield 1
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def yield_middle_non_none() -> Generator[int, str, None]:
|
|
131
|
+
"""
|
|
132
|
+
Yield with send type.
|
|
133
|
+
|
|
134
|
+
Yields
|
|
135
|
+
------
|
|
136
|
+
int
|
|
137
|
+
Should match first annotation element, not send type.
|
|
138
|
+
"""
|
|
139
|
+
yield 1
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def yield_last_non_none() -> Generator[int, None, str]:
|
|
143
|
+
"""
|
|
144
|
+
Yield with return type.
|
|
145
|
+
|
|
146
|
+
Yields
|
|
147
|
+
------
|
|
148
|
+
int
|
|
149
|
+
Should match first annotation element, not return type.
|
|
150
|
+
"""
|
|
151
|
+
yield 1
|
|
@@ -238,6 +238,8 @@ def test_fix_colon_spacing(line: str, expected: str) -> None:
|
|
|
238
238
|
('arg : int', 'arg : int'),
|
|
239
239
|
('arg : str', 'arg : str'),
|
|
240
240
|
(' arg : bool', ' arg : bool'),
|
|
241
|
+
('default : int', 'default : int'),
|
|
242
|
+
(' default : int', ' default : int'),
|
|
241
243
|
# Case insensitive "default"
|
|
242
244
|
('arg : int Default 10', 'arg : int, default=10'),
|
|
243
245
|
('arg : int DEFAULT 10', 'arg : int, default=10'),
|
|
@@ -262,6 +264,14 @@ def test_standardize_default_value(line: str, expected: str) -> None:
|
|
|
262
264
|
'Underscores inside literal are fine: `foo_bar`.',
|
|
263
265
|
'Underscores inside literal are fine: ``foo_bar``.',
|
|
264
266
|
),
|
|
267
|
+
(
|
|
268
|
+
'Dunder names should be wrapped: `__init__`',
|
|
269
|
+
'Dunder names should be wrapped: ``__init__``',
|
|
270
|
+
),
|
|
271
|
+
(
|
|
272
|
+
'Dunder names should be wrapped: `__init123__`',
|
|
273
|
+
'Dunder names should be wrapped: ``__init123__``',
|
|
274
|
+
),
|
|
265
275
|
(
|
|
266
276
|
'Adjacent to parentheses: (`call_me`) and `ok`',
|
|
267
277
|
'Adjacent to parentheses: (``call_me``) and ``ok``',
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{format_docstring-0.2.3 → format_docstring-0.2.5}/format_docstring.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{format_docstring-0.2.3 → format_docstring-0.2.5}/format_docstring.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/end_to_end/numpy/line_length_2.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/integration_test/numpy/after.ipynb
RENAMED
|
File without changes
|
{format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/integration_test/numpy/after.py
RENAMED
|
File without changes
|
|
File without changes
|
{format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/integration_test/numpy/after_50.py
RENAMED
|
File without changes
|
|
File without changes
|
{format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/integration_test/numpy/before.py
RENAMED
|
File without changes
|
|
File without changes
|
{format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/jupyter/verbose_before.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{format_docstring-0.2.3 → format_docstring-0.2.5}/tests/test_data/line_wrap/numpy/line_length_2.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|