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.
Files changed (112) hide show
  1. {format_docstring-0.2.2 → format_docstring-0.2.4}/.pre-commit-config.yaml +21 -13
  2. {format_docstring-0.2.2 → format_docstring-0.2.4}/CHANGELOG.md +23 -1
  3. {format_docstring-0.2.2 → format_docstring-0.2.4}/PKG-INFO +11 -11
  4. {format_docstring-0.2.2 → format_docstring-0.2.4}/README.md +5 -5
  5. {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring/base_fixer.py +1 -1
  6. {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring/config.py +5 -4
  7. {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring/docstring_rewriter.py +37 -26
  8. format_docstring-0.2.4/format_docstring/line_wrap_google.py +15 -0
  9. {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring/line_wrap_numpy.py +66 -36
  10. {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring/line_wrap_utils.py +21 -19
  11. {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring/main_jupyter.py +6 -6
  12. {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring/main_py.py +5 -3
  13. {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring.egg-info/PKG-INFO +11 -11
  14. {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring.egg-info/SOURCES.txt +1 -0
  15. format_docstring-0.2.4/muff.toml +116 -0
  16. format_docstring-0.2.4/pyproject.toml +58 -0
  17. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/helpers.py +5 -2
  18. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_base_fixer.py +4 -3
  19. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_config.py +3 -3
  20. format_docstring-0.2.4/tests/test_data/end_to_end/numpy/arg_name_is_default.txt +44 -0
  21. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_docstring_rewriter.py +24 -14
  22. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_line_wrap_numpy.py +30 -13
  23. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_line_wrap_utils.py +12 -10
  24. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_main_jupyter.py +4 -4
  25. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_main_py.py +4 -4
  26. {format_docstring-0.2.2 → format_docstring-0.2.4}/tox.ini +0 -7
  27. format_docstring-0.2.2/format_docstring/line_wrap_google.py +0 -14
  28. format_docstring-0.2.2/muff.toml +0 -48
  29. format_docstring-0.2.2/pyproject.toml +0 -60
  30. {format_docstring-0.2.2 → format_docstring-0.2.4}/.github/workflows/python-package.yml +0 -0
  31. {format_docstring-0.2.2 → format_docstring-0.2.4}/.github/workflows/python-publish.yml +0 -0
  32. {format_docstring-0.2.2 → format_docstring-0.2.4}/.gitignore +0 -0
  33. {format_docstring-0.2.2 → format_docstring-0.2.4}/.pre-commit-hooks.yaml +0 -0
  34. {format_docstring-0.2.2 → format_docstring-0.2.4}/AGENTS.md +0 -0
  35. {format_docstring-0.2.2 → format_docstring-0.2.4}/LICENSE +0 -0
  36. {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring/__init__.py +0 -0
  37. {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring.egg-info/dependency_links.txt +0 -0
  38. {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring.egg-info/entry_points.txt +0 -0
  39. {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring.egg-info/requires.txt +1 -1
  40. {format_docstring-0.2.2 → format_docstring-0.2.4}/format_docstring.egg-info/top_level.txt +0 -0
  41. {format_docstring-0.2.2 → format_docstring-0.2.4}/requirements.dev +0 -0
  42. {format_docstring-0.2.2 → format_docstring-0.2.4}/setup.cfg +0 -0
  43. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/__init__.py +0 -0
  44. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/README.md +0 -0
  45. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/colon_spacing_fix.txt +0 -0
  46. {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
  47. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/default_value_standardization.txt +0 -0
  48. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/empty_lines_are_respected.txt +0 -0
  49. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/examples_section.txt +0 -0
  50. {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
  51. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/fix_rst_backticks.txt +0 -0
  52. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/four_level_nested_classes.txt +0 -0
  53. {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
  54. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/indent_misaligned_all.txt +0 -0
  55. {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
  56. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/line_length_2.txt +0 -0
  57. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/mismatched_underlines.txt +0 -0
  58. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/mismatched_underlines_one_dash.txt +0 -0
  59. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/mismatched_underlines_two_dashes.txt +0 -0
  60. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/module_level_docstring.txt +0 -0
  61. {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
  62. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/no_format_docstring_comment.txt +0 -0
  63. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/param_signature_without_type.txt +0 -0
  64. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/parameters_returns_raises_wrapping.txt +0 -0
  65. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/returns_signature_and_description.txt +0 -0
  66. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/section_headings_with_colons.txt +0 -0
  67. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/section_title_fixed.txt +0 -0
  68. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/sections_notes_examples.txt +0 -0
  69. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/signature_dont_sync_raises.txt +0 -0
  70. {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
  71. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/signature_sync_class_docstrings.txt +0 -0
  72. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/signature_sync_parameters.txt +0 -0
  73. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/signature_sync_returns.txt +0 -0
  74. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/signature_sync_yields.txt +0 -0
  75. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/single_line_docstring.txt +0 -0
  76. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/texts_are_rewrapped.txt +0 -0
  77. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/end_to_end/numpy/very_long_unbreakable_word.txt +0 -0
  78. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/integration_test/numpy/after.ipynb +0 -0
  79. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/integration_test/numpy/after.py +0 -0
  80. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/integration_test/numpy/after_50.ipynb +0 -0
  81. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/integration_test/numpy/after_50.py +0 -0
  82. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/integration_test/numpy/before.ipynb +0 -0
  83. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/integration_test/numpy/before.py +0 -0
  84. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/jupyter/before.ipynb +0 -0
  85. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/jupyter/verbose_before.ipynb +0 -0
  86. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/README.md +0 -0
  87. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/colon_spacing_fix.txt +0 -0
  88. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/contents_that_are_not_wrapped.txt +0 -0
  89. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/default_value_standardization.txt +0 -0
  90. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/empty_lines_are_respected.txt +0 -0
  91. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/examples_section.txt +0 -0
  92. {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
  93. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/fix_rst_backticks.txt +0 -0
  94. {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
  95. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/indent_two_levels_8_spaces.txt +0 -0
  96. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/line_length_2.txt +0 -0
  97. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/mismatched_underlines.txt +0 -0
  98. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/mismatched_underlines_one_dash.txt +0 -0
  99. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/mismatched_underlines_two_dashes.txt +0 -0
  100. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/module_level_docstring.txt +0 -0
  101. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/param_signature_without_type.txt +0 -0
  102. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/parameters_returns_raises_wrapping.txt +0 -0
  103. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/returns_signature_and_description.txt +0 -0
  104. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/section_headings_with_colons.txt +0 -0
  105. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/section_title_fixed.txt +0 -0
  106. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/sections_notes_examples.txt +0 -0
  107. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/signature_line_is_not_wrapped.txt +0 -0
  108. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/texts_are_rewrapped.txt +0 -0
  109. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/line_wrap/numpy/very_long_unbreakable_word.txt +0 -0
  110. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_data/playground.py +0 -0
  111. {format_docstring-0.2.2 → format_docstring-0.2.4}/tests/test_line_wrap_google.py +0 -0
  112. {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/test_data/|^tests/helpers\.py$
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, '79', --number]
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.11
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.2] - 2024-10-20
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.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
- Keywords: formatter,code-style,docstring,python
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
- **Table of Contents:**
39
+ ______________________________________________________________________
40
40
 
41
- <!--TOC-->
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
- **Table of Contents:**
7
+ ______________________________________________________________________
8
8
 
9
- <!--TOC-->
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 _print_diff(self, filename: str, before: str, after: str) -> None:
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
- import click
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(config_file, 'rb') as fp:
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
- from format_docstring.line_wrap_utils import ParameterMetadata
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
- for tok in tokenize.generate_tokens(
95
- io.StringIO(normalized).readline
96
- ):
97
- if tok.type == tokenize.STRING:
98
- original_strings.append(tok.string)
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
- tok = tok._replace(string=replacement)
120
- except Exception:
126
+ current_tok = tok._replace(string=replacement)
127
+ except Exception: # noqa: BLE001
121
128
  pass
122
129
 
123
- rebuilt_tokens.append(tok)
124
- if tok.type == tokenize.ENDMARKER:
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
- replacements.sort(key=lambda x: x[0], reverse=True)
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] # noqa: LN002
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
- # Track section state
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
- SECTION_ATTRIBUTES = {
71
+ section_attributes = {
72
72
  'attributes',
73
73
  'attributes:',
74
74
  'attribute', # tolerate typo
75
75
  'attribute:',
76
76
  }
77
- SECTION_SIGNABLE = SECTION_PARAMS | SECTION_ATTRIBUTES
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
- SECTION_RAISES = {
87
+ section_raises = {
89
88
  'raises',
90
89
  'raises:',
91
90
  'raise', # tolerate typo
92
91
  'raise:',
93
92
  }
94
- SECTION_EXAMPLES = {
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 SECTION_EXAMPLES
142
- temp_out.append(line)
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 SECTION_SIGNABLE:
159
+ if section_lower_case in section_params | section_attributes:
164
160
  metadata_for_section = parameter_metadata
165
- if section_lower_case in SECTION_ATTRIBUTES:
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 SECTION_RETURNS_YIELDS:
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 SECTION_RAISES:
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
- return len(t) >= 2 and set(t) <= {'-'}
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(line)
477
+ match = _DEFAULT_COLON_RE.match(after_colon)
472
478
  if match:
473
- before = match.group(1).rstrip()
479
+ before = match.group(1)
480
+ if before.strip() == '':
481
+ return line
482
+
474
483
  default_value = match.group(2).strip()
475
- return f'{before}, default={default_value}'
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(line)
488
+ match = _DEFAULT_SPACE_RE.match(after_colon)
479
489
  if match:
480
- before = match.group(1).rstrip()
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
- return f'{before}, default={default_value}'
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
- if ', default=' in existing_annotation_text:
565
- existing_annotation_text = existing_annotation_text.split(
566
- ', default=', 1
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
- if len(slice_node.elts) < 2:
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('>>> ') or 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)