format-docstring 0.2.2__tar.gz → 0.2.4__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.2 → format_docstring-0.2.4}/.pre-commit-config.yaml +21 -13
- {format_docstring-0.2.2 → format_docstring-0.2.4}/CHANGELOG.md +23 -1
- {format_docstring-0.2.2 → format_docstring-0.2.4}/PKG-INFO +11 -11
- {format_docstring-0.2.2 → format_docstring-0.2.4}/README.md +5 -5
- {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring/base_fixer.py +1 -1
- {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring/config.py +5 -4
- {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring/docstring_rewriter.py +37 -26
- format_docstring-0.2.4/format_docstring/line_wrap_google.py +15 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring/line_wrap_numpy.py +66 -36
- {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring/line_wrap_utils.py +21 -19
- {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring/main_jupyter.py +6 -6
- {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring/main_py.py +5 -3
- {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring.egg-info/PKG-INFO +11 -11
- {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring.egg-info/SOURCES.txt +1 -0
- format_docstring-0.2.4/muff.toml +116 -0
- format_docstring-0.2.4/pyproject.toml +58 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/helpers.py +5 -2
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_base_fixer.py +4 -3
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_config.py +3 -3
- format_docstring-0.2.4/tests/test_data/end_to_end/numpy/arg_name_is_default.txt +44 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_docstring_rewriter.py +24 -14
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_line_wrap_numpy.py +30 -13
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_line_wrap_utils.py +12 -10
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_main_jupyter.py +4 -4
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_main_py.py +4 -4
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tox.ini +0 -7
- format_docstring-0.2.2/format_docstring/line_wrap_google.py +0 -14
- format_docstring-0.2.2/muff.toml +0 -48
- format_docstring-0.2.2/pyproject.toml +0 -60
- {format_docstring-0.2.2 → format_docstring-0.2.4}/.github/workflows/python-package.yml +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/.github/workflows/python-publish.yml +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/.gitignore +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/.pre-commit-hooks.yaml +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/AGENTS.md +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/LICENSE +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring/__init__.py +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring.egg-info/dependency_links.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring.egg-info/entry_points.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring.egg-info/requires.txt +1 -1
- {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring.egg-info/top_level.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/requirements.dev +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/setup.cfg +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/__init__.py +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/README.md +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/colon_spacing_fix.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/contents_that_are_not_wrapped.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/default_value_standardization.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/empty_lines_are_respected.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/examples_section.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/existing_linebreaks_should_not_be_respected.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/fix_rst_backticks.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/four_level_nested_classes.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/indent_four_levels_16_spaces_width_10.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/indent_misaligned_all.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/indent_two_levels_8_spaces.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/line_length_2.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/mismatched_underlines.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/mismatched_underlines_one_dash.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/mismatched_underlines_two_dashes.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/module_level_docstring.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/new_lines_before_and_after.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/no_format_docstring_comment.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/param_signature_without_type.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/parameters_returns_raises_wrapping.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/returns_signature_and_description.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/section_headings_with_colons.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/section_title_fixed.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/sections_notes_examples.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/signature_dont_sync_raises.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/signature_line_is_not_wrapped.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/signature_sync_class_docstrings.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/signature_sync_parameters.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/signature_sync_returns.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/signature_sync_yields.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/single_line_docstring.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/texts_are_rewrapped.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/very_long_unbreakable_word.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/integration_test/numpy/after.ipynb +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/integration_test/numpy/after.py +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/integration_test/numpy/after_50.ipynb +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/integration_test/numpy/after_50.py +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/integration_test/numpy/before.ipynb +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/integration_test/numpy/before.py +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/jupyter/before.ipynb +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/jupyter/verbose_before.ipynb +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/README.md +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/colon_spacing_fix.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/contents_that_are_not_wrapped.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/default_value_standardization.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/empty_lines_are_respected.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/examples_section.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/existing_linebreaks_should_not_be_respected.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/fix_rst_backticks.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/indent_four_levels_16_spaces_width_10.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/indent_two_levels_8_spaces.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/line_length_2.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/mismatched_underlines.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/mismatched_underlines_one_dash.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/mismatched_underlines_two_dashes.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/module_level_docstring.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/param_signature_without_type.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/parameters_returns_raises_wrapping.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/returns_signature_and_description.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/section_headings_with_colons.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/section_title_fixed.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/sections_notes_examples.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/signature_line_is_not_wrapped.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/texts_are_rewrapped.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/very_long_unbreakable_word.txt +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/playground.py +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_line_wrap_google.py +0 -0
- {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_playground.py +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
|
|
@@ -6,23 +7,15 @@ repos:
|
|
|
6
7
|
- id: trailing-whitespace
|
|
7
8
|
- id: end-of-file-fixer
|
|
8
9
|
exclude: \.json$|\.ipynb$
|
|
9
|
-
- id: debug-statements
|
|
10
|
-
- id: double-quote-string-fixer
|
|
11
10
|
- id: requirements-txt-fixer
|
|
12
|
-
exclude: \.ipynb$
|
|
13
11
|
- id: check-added-large-files
|
|
14
12
|
args: [--maxkb=1000]
|
|
15
13
|
- id: check-toml
|
|
16
14
|
- id: check-yaml
|
|
17
15
|
- id: name-tests-test
|
|
18
16
|
args: [--pytest-test-first]
|
|
19
|
-
exclude: ^tests/
|
|
17
|
+
exclude: ^tests/helpers\.py$
|
|
20
18
|
- id: check-merge-conflict
|
|
21
|
-
- repo: https://github.com/asottile/pyupgrade
|
|
22
|
-
rev: v3.20.0
|
|
23
|
-
hooks:
|
|
24
|
-
- id: pyupgrade
|
|
25
|
-
args: [--py310-plus]
|
|
26
19
|
- repo: https://github.com/jsh9/muff-pre-commit
|
|
27
20
|
rev: 0.13.2
|
|
28
21
|
hooks:
|
|
@@ -37,11 +30,27 @@ repos:
|
|
|
37
30
|
rev: 1.18.0
|
|
38
31
|
hooks:
|
|
39
32
|
- id: yamlfix
|
|
33
|
+
- repo: https://github.com/pappasam/toml-sort
|
|
34
|
+
rev: v0.24.2
|
|
35
|
+
hooks:
|
|
36
|
+
- id: toml-sort-fix
|
|
37
|
+
args:
|
|
38
|
+
- --in-place
|
|
39
|
+
- --sort-table-keys
|
|
40
|
+
- --sort-inline-tables
|
|
41
|
+
- --sort-first=name,version,dependencies,requires-python,authors,maintainers
|
|
42
|
+
- --trailing-comma-inline-array
|
|
43
|
+
- --sort-inline-arrays
|
|
44
|
+
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
|
|
45
|
+
rev: v2.15.0
|
|
46
|
+
hooks:
|
|
47
|
+
- id: pretty-format-ini
|
|
48
|
+
args: [--autofix]
|
|
40
49
|
- repo: https://github.com/hukkin/mdformat
|
|
41
50
|
rev: 1.0.0
|
|
42
51
|
hooks:
|
|
43
52
|
- id: mdformat
|
|
44
|
-
args: [--wrap
|
|
53
|
+
args: [--wrap=79, --number]
|
|
45
54
|
additional_dependencies: [mdformat-gfm]
|
|
46
55
|
- repo: https://github.com/jsh9/format-json
|
|
47
56
|
rev: 0.1.2
|
|
@@ -54,9 +63,10 @@ repos:
|
|
|
54
63
|
- --no-sort-keys
|
|
55
64
|
- --no-eof-newline
|
|
56
65
|
- repo: https://github.com/jsh9/markdown-toc-creator
|
|
57
|
-
rev: 0.0
|
|
66
|
+
rev: 0.1.0
|
|
58
67
|
hooks:
|
|
59
68
|
- id: markdown-toc-creator
|
|
69
|
+
exclude: ^AGENTS\.md$|^CHANGELOG\.md$
|
|
60
70
|
- repo: https://github.com/jsh9/markdown-heading-numbering
|
|
61
71
|
rev: 0.1.0
|
|
62
72
|
hooks:
|
|
@@ -70,7 +80,6 @@ repos:
|
|
|
70
80
|
args: [-m, format_docstring.main_py, --verbose, diff]
|
|
71
81
|
language: python
|
|
72
82
|
types: [python]
|
|
73
|
-
exclude: ^tests/test_data/
|
|
74
83
|
additional_dependencies:
|
|
75
84
|
- click>=8.0
|
|
76
85
|
- jupyter-notebook-parser>=0.1.4
|
|
@@ -81,7 +90,6 @@ repos:
|
|
|
81
90
|
args: [-m, format_docstring.main_jupyter, --verbose, diff]
|
|
82
91
|
language: python
|
|
83
92
|
files: ^.*\.ipynb$
|
|
84
|
-
exclude: ^tests/test_data/
|
|
85
93
|
additional_dependencies:
|
|
86
94
|
- click>=8.0
|
|
87
95
|
- jupyter-notebook-parser>=0.1.4
|
|
@@ -6,7 +6,29 @@ 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.
|
|
9
|
+
## [0.2.4] - 2025-10-27
|
|
10
|
+
|
|
11
|
+
- Fixed
|
|
12
|
+
- A bug where 2nd pair of backticks can't be added for dunder names (such as
|
|
13
|
+
`__init__`)
|
|
14
|
+
- A bug where input args named `default` would get treated incorrectly (the
|
|
15
|
+
tool would confuse it with the default values)
|
|
16
|
+
- Full diff
|
|
17
|
+
- https://github.com/jsh9/format-docstring/compare/0.2.3...0.2.4
|
|
18
|
+
|
|
19
|
+
## [0.2.3] - 2025-10-22
|
|
20
|
+
|
|
21
|
+
- Added
|
|
22
|
+
- A lot more linters
|
|
23
|
+
- Formatters for TOML and INI config files
|
|
24
|
+
- Changed
|
|
25
|
+
- A lot of code changes to pass the linter checks
|
|
26
|
+
- Removed
|
|
27
|
+
- Unnecessary pre-commit hooks
|
|
28
|
+
- Full diff
|
|
29
|
+
- https://github.com/jsh9/format-docstring/compare/0.2.2...0.2.3
|
|
30
|
+
|
|
31
|
+
## [0.2.2] - 2025-10-20
|
|
10
32
|
|
|
11
33
|
- Added
|
|
12
34
|
- Formatting support for type hints and default values in class docstrings
|
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: format-docstring
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
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>
|
|
7
7
|
License: MIT
|
|
8
8
|
Project-URL: Homepage, https://github.com/your/repo
|
|
9
|
-
Project-URL: Repository, https://github.com/your/repo.git
|
|
10
9
|
Project-URL: Issues, https://github.com/your/repo/issues
|
|
11
|
-
|
|
10
|
+
Project-URL: Repository, https://github.com/your/repo.git
|
|
11
|
+
Keywords: code-style,docstring,formatter,python
|
|
12
12
|
Classifier: Development Status :: 3 - Alpha
|
|
13
13
|
Classifier: Environment :: Console
|
|
14
14
|
Classifier: Intended Audience :: Developers
|
|
15
15
|
Classifier: License :: OSI Approved :: MIT License
|
|
16
16
|
Classifier: Operating System :: OS Independent
|
|
17
|
-
Classifier: Programming Language :: Python
|
|
18
|
-
Classifier: Programming Language :: Python :: 3
|
|
19
17
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python
|
|
23
23
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
24
|
Classifier: Topic :: Software Development :: Quality Assurance
|
|
25
25
|
Requires-Python: >=3.10
|
|
26
26
|
Description-Content-Type: text/markdown
|
|
27
27
|
License-File: LICENSE
|
|
28
|
-
Requires-Dist: jupyter-notebook-parser>=0.1.4
|
|
29
28
|
Requires-Dist: click>=8.0
|
|
29
|
+
Requires-Dist: jupyter-notebook-parser>=0.1.4
|
|
30
30
|
Requires-Dist: tomli>=1.1.0; python_version < "3.11"
|
|
31
31
|
Dynamic: license-file
|
|
32
32
|
|
|
@@ -34,11 +34,11 @@ Dynamic: license-file
|
|
|
34
34
|
|
|
35
35
|
A Python formatter to automatically format numpy-style docstrings.
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
<!--TOC-->
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
______________________________________________________________________
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
**Table of Contents**
|
|
42
42
|
|
|
43
43
|
- [1. Overview](#1-overview)
|
|
44
44
|
- [2. Before vs After Examples](#2-before-vs-after-examples)
|
|
@@ -59,10 +59,10 @@ ______________________________________________________________________
|
|
|
59
59
|
- [5.3. `pyproject.toml` Configuration](#53-pyprojecttoml-configuration)
|
|
60
60
|
- [6. Caveat](#6-caveat)
|
|
61
61
|
|
|
62
|
-
<!--TOC-->
|
|
63
|
-
|
|
64
62
|
______________________________________________________________________
|
|
65
63
|
|
|
64
|
+
<!--TOC-->
|
|
65
|
+
|
|
66
66
|
## 1. Overview
|
|
67
67
|
|
|
68
68
|
`format-docstring` is a tool that automatically formats and wraps docstring
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
A Python formatter to automatically format numpy-style docstrings.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
<!--TOC-->
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
______________________________________________________________________
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
**Table of Contents**
|
|
10
10
|
|
|
11
11
|
- [1. Overview](#1-overview)
|
|
12
12
|
- [2. Before vs After Examples](#2-before-vs-after-examples)
|
|
@@ -27,10 +27,10 @@ ______________________________________________________________________
|
|
|
27
27
|
- [5.3. `pyproject.toml` Configuration](#53-pyprojecttoml-configuration)
|
|
28
28
|
- [6. Caveat](#6-caveat)
|
|
29
29
|
|
|
30
|
-
<!--TOC-->
|
|
31
|
-
|
|
32
30
|
______________________________________________________________________
|
|
33
31
|
|
|
32
|
+
<!--TOC-->
|
|
33
|
+
|
|
34
34
|
## 1. Overview
|
|
35
35
|
|
|
36
36
|
`format-docstring` is a tool that automatically formats and wraps docstring
|
|
@@ -42,7 +42,7 @@ class BaseFixer:
|
|
|
42
42
|
if not should_exclude_file(f, self.exclude_pattern)
|
|
43
43
|
]
|
|
44
44
|
|
|
45
|
-
def
|
|
45
|
+
def print_diff(self, filename: str, before: str, after: str) -> None:
|
|
46
46
|
"""Print a unified diff when verbose mode is enabled."""
|
|
47
47
|
if self.verbose != 'diff':
|
|
48
48
|
return
|
|
@@ -4,9 +4,10 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import sys
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Any
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
import click
|
|
10
11
|
|
|
11
12
|
if sys.version_info >= (3, 11):
|
|
12
13
|
import tomllib
|
|
@@ -132,7 +133,7 @@ def load_config_from_file(config_file: Path) -> dict[str, Any]:
|
|
|
132
133
|
return {}
|
|
133
134
|
|
|
134
135
|
try:
|
|
135
|
-
with open(
|
|
136
|
+
with Path(config_file).open('rb') as fp:
|
|
136
137
|
raw_config = tomllib.load(fp)
|
|
137
138
|
|
|
138
139
|
# Extract [tool.format_docstring] section
|
|
@@ -144,7 +145,7 @@ def load_config_from_file(config_file: Path) -> dict[str, Any]:
|
|
|
144
145
|
return {
|
|
145
146
|
k.replace('-', '_'): v for k, v in format_docstring_section.items()
|
|
146
147
|
}
|
|
147
|
-
except Exception:
|
|
148
|
+
except Exception: # noqa: BLE001
|
|
148
149
|
# If there's any error reading/parsing the file, return empty config
|
|
149
150
|
return {}
|
|
150
151
|
|
|
@@ -2,14 +2,18 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import ast
|
|
4
4
|
import io
|
|
5
|
+
import operator
|
|
5
6
|
import tokenize
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
6
8
|
|
|
7
9
|
from format_docstring.line_wrap_google import wrap_docstring_google
|
|
8
10
|
from format_docstring.line_wrap_numpy import (
|
|
9
11
|
handle_single_line_docstring,
|
|
10
12
|
wrap_docstring_numpy,
|
|
11
13
|
)
|
|
12
|
-
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from format_docstring.line_wrap_utils import ParameterMetadata
|
|
13
17
|
|
|
14
18
|
ModuleClassOrFunc = (
|
|
15
19
|
ast.Module | ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef
|
|
@@ -91,11 +95,13 @@ def _normalize_signature_segment(segment: str | None) -> str | None:
|
|
|
91
95
|
# iterator order mirrors the unparse traversal so we can reapply them.
|
|
92
96
|
original_strings: list[str] = []
|
|
93
97
|
try:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
original_strings.extend(
|
|
99
|
+
tok.string
|
|
100
|
+
for tok in tokenize.generate_tokens(
|
|
101
|
+
io.StringIO(normalized).readline
|
|
102
|
+
)
|
|
103
|
+
if tok.type == tokenize.STRING
|
|
104
|
+
)
|
|
99
105
|
except tokenize.TokenError:
|
|
100
106
|
original_strings = []
|
|
101
107
|
|
|
@@ -105,6 +111,7 @@ def _normalize_signature_segment(segment: str | None) -> str | None:
|
|
|
105
111
|
for tok in tokenize.generate_tokens(
|
|
106
112
|
io.StringIO(canonical).readline
|
|
107
113
|
):
|
|
114
|
+
current_tok: tokenize.TokenInfo = tok
|
|
108
115
|
if tok.type == tokenize.STRING:
|
|
109
116
|
replacement = next(string_iter, None)
|
|
110
117
|
if replacement is not None:
|
|
@@ -116,12 +123,12 @@ def _normalize_signature_segment(segment: str | None) -> str | None:
|
|
|
116
123
|
if ast.literal_eval(
|
|
117
124
|
replacement
|
|
118
125
|
) == ast.literal_eval(tok.string):
|
|
119
|
-
|
|
120
|
-
except Exception:
|
|
126
|
+
current_tok = tok._replace(string=replacement)
|
|
127
|
+
except Exception: # noqa: BLE001
|
|
121
128
|
pass
|
|
122
129
|
|
|
123
|
-
rebuilt_tokens.append(
|
|
124
|
-
if
|
|
130
|
+
rebuilt_tokens.append(current_tok)
|
|
131
|
+
if current_tok.type == tokenize.ENDMARKER:
|
|
125
132
|
break
|
|
126
133
|
|
|
127
134
|
# Untokenize the rebuilt stream while trimming the leading/trailing
|
|
@@ -305,16 +312,17 @@ def fix_src(
|
|
|
305
312
|
tree: ast.Module = ast.parse(source_code, type_comments=True)
|
|
306
313
|
line_starts: list[int] = calc_line_starts(source_code)
|
|
307
314
|
|
|
315
|
+
# Store (start, end, replacement text) tuples
|
|
308
316
|
replacements: list[tuple[int, int, str]] = []
|
|
309
317
|
|
|
310
318
|
# Module-level docstring
|
|
311
319
|
replacement = build_replacement_docstring(
|
|
312
320
|
tree,
|
|
313
|
-
source_code,
|
|
314
|
-
line_starts,
|
|
315
|
-
line_length,
|
|
316
|
-
docstring_style,
|
|
317
|
-
fix_rst_backticks,
|
|
321
|
+
source_code=source_code,
|
|
322
|
+
line_starts=line_starts,
|
|
323
|
+
line_length=line_length,
|
|
324
|
+
docstring_style=docstring_style,
|
|
325
|
+
fix_rst_backticks=fix_rst_backticks,
|
|
318
326
|
)
|
|
319
327
|
if replacement is not None:
|
|
320
328
|
replacements.append(replacement)
|
|
@@ -326,11 +334,11 @@ def fix_src(
|
|
|
326
334
|
):
|
|
327
335
|
replacement = build_replacement_docstring(
|
|
328
336
|
node,
|
|
329
|
-
source_code,
|
|
330
|
-
line_starts,
|
|
331
|
-
line_length,
|
|
332
|
-
docstring_style,
|
|
333
|
-
fix_rst_backticks,
|
|
337
|
+
source_code=source_code,
|
|
338
|
+
line_starts=line_starts,
|
|
339
|
+
line_length=line_length,
|
|
340
|
+
docstring_style=docstring_style,
|
|
341
|
+
fix_rst_backticks=fix_rst_backticks,
|
|
334
342
|
)
|
|
335
343
|
if replacement is not None:
|
|
336
344
|
replacements.append(replacement)
|
|
@@ -339,7 +347,8 @@ def fix_src(
|
|
|
339
347
|
if not replacements:
|
|
340
348
|
return source_code
|
|
341
349
|
|
|
342
|
-
|
|
350
|
+
# Sort by starting index descending
|
|
351
|
+
replacements.sort(key=operator.itemgetter(0), reverse=True)
|
|
343
352
|
new_src = source_code
|
|
344
353
|
for start, end, text in replacements:
|
|
345
354
|
new_src = new_src[:start] + text + new_src[end:]
|
|
@@ -371,6 +380,7 @@ def calc_line_starts(source_code: str) -> list[int]:
|
|
|
371
380
|
|
|
372
381
|
def build_replacement_docstring(
|
|
373
382
|
node: ModuleClassOrFunc,
|
|
383
|
+
*,
|
|
374
384
|
source_code: str,
|
|
375
385
|
line_starts: list[int],
|
|
376
386
|
line_length: int,
|
|
@@ -411,7 +421,7 @@ def build_replacement_docstring(
|
|
|
411
421
|
return None
|
|
412
422
|
|
|
413
423
|
start: int = calc_abs_pos(line_starts, val.lineno, val.col_offset)
|
|
414
|
-
end: int = calc_abs_pos(line_starts, val.end_lineno, val.end_col_offset) # type: ignore[arg-type]
|
|
424
|
+
end: int = calc_abs_pos(line_starts, val.end_lineno, val.end_col_offset) # type: ignore[arg-type]
|
|
415
425
|
original_literal = source_code[start:end]
|
|
416
426
|
|
|
417
427
|
if _has_inline_no_format_comment(source_code, end):
|
|
@@ -554,10 +564,10 @@ def rebuild_literal(original_literal: str, content: str) -> str | None:
|
|
|
554
564
|
prefix = original_literal[:i]
|
|
555
565
|
|
|
556
566
|
delim = ''
|
|
557
|
-
if original_literal[i : i + 3] in
|
|
567
|
+
if original_literal[i : i + 3] in {'"""', "'''"}:
|
|
558
568
|
delim = original_literal[i : i + 3]
|
|
559
569
|
i += 3
|
|
560
|
-
elif i < n and original_literal[i] in
|
|
570
|
+
elif i < n and original_literal[i] in {'"', "'"}:
|
|
561
571
|
delim = original_literal[i]
|
|
562
572
|
i += 1
|
|
563
573
|
else:
|
|
@@ -576,6 +586,7 @@ def wrap_docstring(
|
|
|
576
586
|
line_length: int = 79,
|
|
577
587
|
docstring_style: str = 'numpy',
|
|
578
588
|
leading_indent: int = 0,
|
|
589
|
+
*,
|
|
579
590
|
fix_rst_backticks: bool = True,
|
|
580
591
|
function_param_metadata: ParameterMetadata | None = None,
|
|
581
592
|
function_return_annotation: str | None = None,
|
|
@@ -622,7 +633,7 @@ def wrap_docstring(
|
|
|
622
633
|
if style == 'google':
|
|
623
634
|
return wrap_docstring_google(
|
|
624
635
|
docstring,
|
|
625
|
-
line_length,
|
|
636
|
+
line_length=line_length,
|
|
626
637
|
leading_indent=leading_indent,
|
|
627
638
|
fix_rst_backticks=fix_rst_backticks,
|
|
628
639
|
parameter_metadata=function_param_metadata,
|
|
@@ -632,7 +643,7 @@ def wrap_docstring(
|
|
|
632
643
|
# Default to NumPy-style for unknown/unspecified styles to be permissive.
|
|
633
644
|
return wrap_docstring_numpy(
|
|
634
645
|
docstring,
|
|
635
|
-
line_length,
|
|
646
|
+
line_length=line_length,
|
|
636
647
|
leading_indent=leading_indent,
|
|
637
648
|
fix_rst_backticks=fix_rst_backticks,
|
|
638
649
|
parameter_metadata=function_param_metadata,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from format_docstring.line_wrap_utils import ParameterMetadata
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def wrap_docstring_google(
|
|
5
|
+
docstring: str, # noqa: ARG001
|
|
6
|
+
*,
|
|
7
|
+
line_length: int, # noqa: ARG001
|
|
8
|
+
leading_indent: int | None = None, # noqa: ARG001
|
|
9
|
+
fix_rst_backticks: bool = True, # noqa: ARG001
|
|
10
|
+
parameter_metadata: ParameterMetadata | None = None, # noqa: ARG001
|
|
11
|
+
return_annotation: str | None = None, # noqa: ARG001
|
|
12
|
+
attribute_metadata: ParameterMetadata | None = None, # noqa: ARG001
|
|
13
|
+
) -> str:
|
|
14
|
+
"""A placeholder for now.""" # noqa: D401
|
|
15
|
+
return ''
|
|
@@ -13,8 +13,9 @@ from format_docstring.line_wrap_utils import (
|
|
|
13
13
|
)
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
def wrap_docstring_numpy(
|
|
16
|
+
def wrap_docstring_numpy( # noqa: C901, PLR0915, TODO: https://github.com/jsh9/format-docstring/issues/17
|
|
17
17
|
docstring: str,
|
|
18
|
+
*,
|
|
18
19
|
line_length: int,
|
|
19
20
|
leading_indent: int | None = None,
|
|
20
21
|
fix_rst_backticks: bool = False,
|
|
@@ -53,8 +54,7 @@ def wrap_docstring_numpy(
|
|
|
53
54
|
if not lines:
|
|
54
55
|
return docstring_
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
SECTION_PARAMS = {
|
|
57
|
+
section_params = {
|
|
58
58
|
'parameters',
|
|
59
59
|
'parameters:',
|
|
60
60
|
'parameter', # tolerate typo
|
|
@@ -68,14 +68,13 @@ def wrap_docstring_numpy(
|
|
|
68
68
|
'other parameter', # tolerate typo
|
|
69
69
|
'other parameter:',
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
section_attributes = {
|
|
72
72
|
'attributes',
|
|
73
73
|
'attributes:',
|
|
74
74
|
'attribute', # tolerate typo
|
|
75
75
|
'attribute:',
|
|
76
76
|
}
|
|
77
|
-
|
|
78
|
-
SECTION_RETURNS_YIELDS = {
|
|
77
|
+
section_returns_yields = {
|
|
79
78
|
'returns',
|
|
80
79
|
'returns:',
|
|
81
80
|
'return', # tolerate typo
|
|
@@ -85,13 +84,13 @@ def wrap_docstring_numpy(
|
|
|
85
84
|
'yield', # tolerate typo
|
|
86
85
|
'yield:',
|
|
87
86
|
}
|
|
88
|
-
|
|
87
|
+
section_raises = {
|
|
89
88
|
'raises',
|
|
90
89
|
'raises:',
|
|
91
90
|
'raise', # tolerate typo
|
|
92
91
|
'raise:',
|
|
93
92
|
}
|
|
94
|
-
|
|
93
|
+
section_examples = {
|
|
95
94
|
'examples',
|
|
96
95
|
'examples:',
|
|
97
96
|
'example', # tolerate typo
|
|
@@ -138,9 +137,8 @@ def wrap_docstring_numpy(
|
|
|
138
137
|
heading: str | None = _get_section_heading_title(lines, i)
|
|
139
138
|
if heading:
|
|
140
139
|
current_section = heading
|
|
141
|
-
in_examples = heading in
|
|
142
|
-
temp_out.
|
|
143
|
-
temp_out.append(lines[i + 1])
|
|
140
|
+
in_examples = heading in section_examples
|
|
141
|
+
temp_out.extend((line, lines[i + 1]))
|
|
144
142
|
i += 2
|
|
145
143
|
continue
|
|
146
144
|
|
|
@@ -151,18 +149,16 @@ def wrap_docstring_numpy(
|
|
|
151
149
|
continue
|
|
152
150
|
|
|
153
151
|
# In Examples, skip wrapping and backtick fixing for REPL lines
|
|
154
|
-
if in_examples and (
|
|
155
|
-
stripped.startswith('>>> ') or stripped.startswith('... ')
|
|
156
|
-
):
|
|
152
|
+
if in_examples and stripped.startswith(('>>> ', '... ')):
|
|
157
153
|
temp_out.append(line)
|
|
158
154
|
i += 1
|
|
159
155
|
continue
|
|
160
156
|
|
|
161
157
|
# Parameters-like sections
|
|
162
158
|
section_lower_case: str = current_section.lower()
|
|
163
|
-
if section_lower_case in
|
|
159
|
+
if section_lower_case in section_params | section_attributes:
|
|
164
160
|
metadata_for_section = parameter_metadata
|
|
165
|
-
if section_lower_case in
|
|
161
|
+
if section_lower_case in section_attributes:
|
|
166
162
|
metadata_for_section = attribute_metadata or parameter_metadata
|
|
167
163
|
|
|
168
164
|
if line.strip() == '':
|
|
@@ -193,7 +189,7 @@ def wrap_docstring_numpy(
|
|
|
193
189
|
continue
|
|
194
190
|
|
|
195
191
|
# Returns/Yields sections
|
|
196
|
-
if section_lower_case in
|
|
192
|
+
if section_lower_case in section_returns_yields:
|
|
197
193
|
if line.strip() == '':
|
|
198
194
|
temp_out.append(line)
|
|
199
195
|
i += 1
|
|
@@ -245,7 +241,7 @@ def wrap_docstring_numpy(
|
|
|
245
241
|
continue
|
|
246
242
|
|
|
247
243
|
# Raises section
|
|
248
|
-
if section_lower_case in
|
|
244
|
+
if section_lower_case in section_raises:
|
|
249
245
|
if line.strip() == '':
|
|
250
246
|
temp_out.append(line)
|
|
251
247
|
i += 1
|
|
@@ -294,8 +290,9 @@ def _is_hyphen_underline(s: str) -> bool:
|
|
|
294
290
|
>>> _is_hyphen_underline(' - - ')
|
|
295
291
|
False
|
|
296
292
|
"""
|
|
297
|
-
t = s.strip()
|
|
298
|
-
|
|
293
|
+
t: str = s.strip()
|
|
294
|
+
min_hyphens_in_section_header: int = 2
|
|
295
|
+
return len(t) >= min_hyphens_in_section_header and set(t) <= {'-'}
|
|
299
296
|
|
|
300
297
|
|
|
301
298
|
def _get_section_heading_title(lines: list[str], idx: int) -> str | None:
|
|
@@ -467,19 +464,39 @@ def _standardize_default_value(line: str) -> str:
|
|
|
467
464
|
>>> _standardize_default_value('arg : bool, default: True')
|
|
468
465
|
'arg : bool, default=True'
|
|
469
466
|
"""
|
|
467
|
+
colon_idx = line.find(':')
|
|
468
|
+
if colon_idx == -1:
|
|
469
|
+
return line
|
|
470
|
+
|
|
471
|
+
# `prefix` is everything before the 1st colon (param identifier portion).
|
|
472
|
+
# We leave `prefix` untouched so arg names like `default` aren't rewritten.
|
|
473
|
+
prefix = line[: colon_idx + 1]
|
|
474
|
+
after_colon = line[colon_idx + 1 :]
|
|
475
|
+
|
|
470
476
|
# Check colon format first to avoid matching colons in space-based pattern
|
|
471
|
-
match = _DEFAULT_COLON_RE.match(
|
|
477
|
+
match = _DEFAULT_COLON_RE.match(after_colon)
|
|
472
478
|
if match:
|
|
473
|
-
before = match.group(1)
|
|
479
|
+
before = match.group(1)
|
|
480
|
+
if before.strip() == '':
|
|
481
|
+
return line
|
|
482
|
+
|
|
474
483
|
default_value = match.group(2).strip()
|
|
475
|
-
|
|
484
|
+
rebuilt_suffix = f'{before.rstrip()}, default={default_value}'
|
|
485
|
+
return f'{prefix}{rebuilt_suffix}'
|
|
476
486
|
|
|
477
487
|
# Try space-separated format with optional "is"
|
|
478
|
-
match = _DEFAULT_SPACE_RE.match(
|
|
488
|
+
match = _DEFAULT_SPACE_RE.match(after_colon)
|
|
479
489
|
if match:
|
|
480
|
-
before = match.group(1)
|
|
490
|
+
before = match.group(1)
|
|
491
|
+
if before.strip() == '':
|
|
492
|
+
return line
|
|
493
|
+
|
|
494
|
+
# ``before`` still contains any annotation text; tightening the spacing
|
|
495
|
+
# here standardizes the ``", default=..."`` suffix while preserving
|
|
496
|
+
# whatever appeared to the left.
|
|
481
497
|
default_value = match.group(2).strip()
|
|
482
|
-
|
|
498
|
+
rebuilt_suffix = f'{before.rstrip()}, default={default_value}'
|
|
499
|
+
return f'{prefix}{rebuilt_suffix}'
|
|
483
500
|
|
|
484
501
|
return line
|
|
485
502
|
|
|
@@ -560,11 +577,10 @@ def _rewrite_parameter_signature(
|
|
|
560
577
|
core, tail = _extract_signature_tail(line[colon_idx + 1 :])
|
|
561
578
|
|
|
562
579
|
existing_annotation_text = core.strip()
|
|
563
|
-
if existing_annotation_text:
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
)[0].rstrip(', ')
|
|
580
|
+
if existing_annotation_text and ', default=' in existing_annotation_text:
|
|
581
|
+
existing_annotation_text = existing_annotation_text.split(
|
|
582
|
+
', default=', 1
|
|
583
|
+
)[0].rstrip(', ')
|
|
568
584
|
|
|
569
585
|
existing_annotation_text = existing_annotation_text.strip()
|
|
570
586
|
|
|
@@ -624,7 +640,8 @@ def _split_tuple_annotation(annotation: str | None) -> list[str] | None:
|
|
|
624
640
|
if not isinstance(slice_node, ast.Tuple):
|
|
625
641
|
return None
|
|
626
642
|
|
|
627
|
-
|
|
643
|
+
min_elements_for_an_actual_tuple: int = 2
|
|
644
|
+
if len(slice_node.elts) < min_elements_for_an_actual_tuple:
|
|
628
645
|
return None
|
|
629
646
|
|
|
630
647
|
parts: list[str] = []
|
|
@@ -733,12 +750,20 @@ def handle_single_line_docstring(
|
|
|
733
750
|
# or certain punctuation (like > and . for `>>> ` and `... ` literals)
|
|
734
751
|
# Note: We match [^`]+ (anything except backticks) and then check in the
|
|
735
752
|
# replacement function whether it's an external link (contains < followed by >)
|
|
736
|
-
# The opening backtick must not be immediately followed by _ or __ (to avoid
|
|
737
|
-
# matching the trailing backtick of cross-references like `text`_ or `text`__)
|
|
738
753
|
_RST_BACKTICK_PATTERN = re.compile(
|
|
739
754
|
r'(?:^|(?<=\s)|(?<=\()|(?<=[>.]))(?::[\w-]+:)?`(?!_)([^`]+)`(?!`)(?!__)(?!_)'
|
|
740
755
|
)
|
|
741
756
|
|
|
757
|
+
# 2nd-stage fixer for ``__dunder__`` names that slipped past the main pattern
|
|
758
|
+
# because the literal starts with an underscore. Negative lookbehinds/aheads
|
|
759
|
+
# ensure we only touch isolated single-backtick literals and leave
|
|
760
|
+
# cross-references (`name`_ / `name`__) alone.
|
|
761
|
+
_DUNDER_LITERAL_PATTERN = re.compile(
|
|
762
|
+
r'(?<!`)`(__[A-Za-z0-9_]+__)`(?!`)(?!_)(?!__)'
|
|
763
|
+
)
|
|
764
|
+
# Replacement wraps the captured dunder name (group 1) with double backticks.
|
|
765
|
+
_DUNDER_LITERAL_REPLACEMENT = r'``\1``'
|
|
766
|
+
|
|
742
767
|
|
|
743
768
|
def _fix_rst_backticks(docstring: str) -> str:
|
|
744
769
|
"""
|
|
@@ -824,7 +849,7 @@ def _fix_rst_backticks(docstring: str) -> str:
|
|
|
824
849
|
|
|
825
850
|
# Check if this is an external link (contains <...> pattern)
|
|
826
851
|
# External links look like: `text <url>`_
|
|
827
|
-
if '<' in content and '>' in content:
|
|
852
|
+
if '<' in content and '>' in content: # noqa: SIM102
|
|
828
853
|
# Check if < comes before > (basic validation)
|
|
829
854
|
if content.index('<') < content.rindex('>'):
|
|
830
855
|
return full_match # Keep original (it's an external link)
|
|
@@ -846,7 +871,7 @@ def _fix_rst_backticks(docstring: str) -> str:
|
|
|
846
871
|
for i, line in enumerate(lines):
|
|
847
872
|
stripped = line.lstrip()
|
|
848
873
|
# Protect REPL lines (>>> or ...) - don't fix backticks in these
|
|
849
|
-
if stripped.startswith('>>> '
|
|
874
|
+
if stripped.startswith(('>>> ', '... ')):
|
|
850
875
|
repl_lines[i] = line
|
|
851
876
|
# Use a placeholder that won't be matched by the regex
|
|
852
877
|
protected_lines.append(
|
|
@@ -860,6 +885,11 @@ def _fix_rst_backticks(docstring: str) -> str:
|
|
|
860
885
|
# Process the entire docstring (with REPL lines protected)
|
|
861
886
|
protected_docstring = ''.join(protected_lines)
|
|
862
887
|
processed = _RST_BACKTICK_PATTERN.sub(replace_func, protected_docstring)
|
|
888
|
+
# Upgrade remaining single-backtick ``__dunder__`` literals to double
|
|
889
|
+
# backticks; they are safe literals (not targets or refs) after the guards.
|
|
890
|
+
processed = _DUNDER_LITERAL_PATTERN.sub(
|
|
891
|
+
_DUNDER_LITERAL_REPLACEMENT, processed
|
|
892
|
+
)
|
|
863
893
|
|
|
864
894
|
# Restore REPL lines
|
|
865
895
|
result_lines = processed.splitlines(keepends=True)
|