format-docstring 0.1.6__tar.gz → 0.1.8__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 (97) hide show
  1. {format_docstring-0.1.6 → format_docstring-0.1.8}/CHANGELOG.md +31 -0
  2. {format_docstring-0.1.6 → format_docstring-0.1.8}/PKG-INFO +1 -1
  3. {format_docstring-0.1.6 → format_docstring-0.1.8}/format_docstring/line_wrap_numpy.py +132 -31
  4. {format_docstring-0.1.6 → format_docstring-0.1.8}/format_docstring.egg-info/PKG-INFO +1 -1
  5. {format_docstring-0.1.6 → format_docstring-0.1.8}/pyproject.toml +1 -1
  6. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/fix_rst_backticks.txt +21 -3
  7. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/fix_rst_backticks.txt +26 -2
  8. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_line_wrap_numpy.py +78 -2
  9. {format_docstring-0.1.6 → format_docstring-0.1.8}/.github/workflows/python-package.yml +0 -0
  10. {format_docstring-0.1.6 → format_docstring-0.1.8}/.github/workflows/python-publish.yml +0 -0
  11. {format_docstring-0.1.6 → format_docstring-0.1.8}/.gitignore +0 -0
  12. {format_docstring-0.1.6 → format_docstring-0.1.8}/.pre-commit-config.yaml +0 -0
  13. {format_docstring-0.1.6 → format_docstring-0.1.8}/.pre-commit-hooks.yaml +0 -0
  14. {format_docstring-0.1.6 → format_docstring-0.1.8}/CLAUDE.md +0 -0
  15. {format_docstring-0.1.6 → format_docstring-0.1.8}/LICENSE +0 -0
  16. {format_docstring-0.1.6 → format_docstring-0.1.8}/README.md +0 -0
  17. {format_docstring-0.1.6 → format_docstring-0.1.8}/format_docstring/__init__.py +0 -0
  18. {format_docstring-0.1.6 → format_docstring-0.1.8}/format_docstring/base_fixer.py +0 -0
  19. {format_docstring-0.1.6 → format_docstring-0.1.8}/format_docstring/config.py +0 -0
  20. {format_docstring-0.1.6 → format_docstring-0.1.8}/format_docstring/docstring_rewriter.py +0 -0
  21. {format_docstring-0.1.6 → format_docstring-0.1.8}/format_docstring/line_wrap_google.py +0 -0
  22. {format_docstring-0.1.6 → format_docstring-0.1.8}/format_docstring/line_wrap_utils.py +0 -0
  23. {format_docstring-0.1.6 → format_docstring-0.1.8}/format_docstring/main_jupyter.py +0 -0
  24. {format_docstring-0.1.6 → format_docstring-0.1.8}/format_docstring/main_py.py +0 -0
  25. {format_docstring-0.1.6 → format_docstring-0.1.8}/format_docstring.egg-info/SOURCES.txt +0 -0
  26. {format_docstring-0.1.6 → format_docstring-0.1.8}/format_docstring.egg-info/dependency_links.txt +0 -0
  27. {format_docstring-0.1.6 → format_docstring-0.1.8}/format_docstring.egg-info/entry_points.txt +0 -0
  28. {format_docstring-0.1.6 → format_docstring-0.1.8}/format_docstring.egg-info/requires.txt +0 -0
  29. {format_docstring-0.1.6 → format_docstring-0.1.8}/format_docstring.egg-info/top_level.txt +0 -0
  30. {format_docstring-0.1.6 → format_docstring-0.1.8}/muff.toml +0 -0
  31. {format_docstring-0.1.6 → format_docstring-0.1.8}/requirements.dev +0 -0
  32. {format_docstring-0.1.6 → format_docstring-0.1.8}/setup.cfg +0 -0
  33. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/__init__.py +0 -0
  34. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/helpers.py +0 -0
  35. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_config.py +0 -0
  36. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/README.md +0 -0
  37. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/colon_spacing_fix.txt +0 -0
  38. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/contents_that_are_not_wrapped.txt +0 -0
  39. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/default_value_standardization.txt +0 -0
  40. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/empty_lines_are_respected.txt +0 -0
  41. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/examples_section.txt +0 -0
  42. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/existing_linebreaks_should_not_be_respected.txt +0 -0
  43. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/four_level_nested_classes.txt +0 -0
  44. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/indent_four_levels_16_spaces_width_10.txt +0 -0
  45. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/indent_misaligned_all.txt +0 -0
  46. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/indent_two_levels_8_spaces.txt +0 -0
  47. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/line_length_2.txt +0 -0
  48. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/mismatched_underlines.txt +0 -0
  49. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/mismatched_underlines_one_dash.txt +0 -0
  50. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/mismatched_underlines_two_dashes.txt +0 -0
  51. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/module_level_docstring.txt +0 -0
  52. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/new_lines_before_and_after.txt +0 -0
  53. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/param_signature_without_type.txt +0 -0
  54. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/parameters_returns_raises_wrapping.txt +0 -0
  55. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/returns_signature_and_description.txt +0 -0
  56. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/section_title_fixed.txt +0 -0
  57. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/sections_notes_examples.txt +0 -0
  58. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/signature_line_is_not_wrapped.txt +0 -0
  59. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/single_line_docstring.txt +0 -0
  60. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/texts_are_rewrapped.txt +0 -0
  61. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/end_to_end/numpy/very_long_unbreakable_word.txt +0 -0
  62. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/integration_test/numpy/after.ipynb +0 -0
  63. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/integration_test/numpy/after.py +0 -0
  64. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/integration_test/numpy/after_50.ipynb +0 -0
  65. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/integration_test/numpy/after_50.py +0 -0
  66. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/integration_test/numpy/before.ipynb +0 -0
  67. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/integration_test/numpy/before.py +0 -0
  68. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/README.md +0 -0
  69. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/colon_spacing_fix.txt +0 -0
  70. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/contents_that_are_not_wrapped.txt +0 -0
  71. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/default_value_standardization.txt +0 -0
  72. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/empty_lines_are_respected.txt +0 -0
  73. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/examples_section.txt +0 -0
  74. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/existing_linebreaks_should_not_be_respected.txt +0 -0
  75. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/indent_four_levels_16_spaces_width_10.txt +0 -0
  76. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/indent_two_levels_8_spaces.txt +0 -0
  77. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/line_length_2.txt +0 -0
  78. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/mismatched_underlines.txt +0 -0
  79. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/mismatched_underlines_one_dash.txt +0 -0
  80. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/mismatched_underlines_two_dashes.txt +0 -0
  81. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/module_level_docstring.txt +0 -0
  82. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/param_signature_without_type.txt +0 -0
  83. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/parameters_returns_raises_wrapping.txt +0 -0
  84. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/returns_signature_and_description.txt +0 -0
  85. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/section_title_fixed.txt +0 -0
  86. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/sections_notes_examples.txt +0 -0
  87. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/signature_line_is_not_wrapped.txt +0 -0
  88. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/texts_are_rewrapped.txt +0 -0
  89. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/line_wrap/numpy/very_long_unbreakable_word.txt +0 -0
  90. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_data/playground.py +0 -0
  91. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_docstring_rewriter.py +0 -0
  92. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_line_wrap_google.py +0 -0
  93. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_line_wrap_utils.py +0 -0
  94. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_main_jupyter.py +0 -0
  95. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_main_py.py +0 -0
  96. {format_docstring-0.1.6 → format_docstring-0.1.8}/tests/test_playground.py +0 -0
  97. {format_docstring-0.1.6 → format_docstring-0.1.8}/tox.ini +0 -0
@@ -6,6 +6,37 @@ 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.1.8] - 2025-10-14
10
+
11
+ - Fixed
12
+ - Bug in `_fix_rst_backticks()` where backtick pairs spanning multiple lines
13
+ (e.g., multi-line external links) were incorrectly processed
14
+ - Added `(?!_)` lookahead to regex pattern to prevent matching trailing
15
+ backticks from cross-references (e.g., `` `text`_ ``)
16
+ - Changed
17
+ - Moved backtick fixing from line-by-line processing to whole-docstring
18
+ processing to correctly handle multi-line constructs
19
+ - REPL lines (starting with `>>> ` or `... `) are now protected with
20
+ placeholders during backtick fixing to preserve backticks in Python
21
+ examples
22
+ - Added
23
+ - Test cases for multi-line external links in
24
+ `test_fix_rst_backticks_cases()`
25
+ - Test cases for REPL lines with backticks in
26
+ `test_fix_rst_backticks_cases()`
27
+ - Full diff
28
+ - https://github.com/jsh9/format-docstring/compare/0.1.7...0.1.8
29
+
30
+ ## [0.1.7] - 2025-10-14
31
+
32
+ - Fixed
33
+ - Backtick fixing logic to properly distinguish between inline literals and
34
+ external links (e.g., `` `Python <https://example.org>`_ ``)
35
+ - Refactored `_fix_rst_backticks()` to use pre-compiled regex pattern for
36
+ better performance
37
+ - Full diff
38
+ - https://github.com/jsh9/format-docstring/compare/0.1.6...0.1.7
39
+
9
40
  ## [0.1.6] - 2025-10-13
10
41
 
11
42
  - Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: format-docstring
3
- Version: 0.1.6
3
+ Version: 0.1.8
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>
@@ -37,6 +37,12 @@ def wrap_docstring_numpy(
37
37
  # This helps place the closing quotes on their own indented line later.
38
38
  docstring_: str = add_leading_indent(docstring, leading_indent)
39
39
 
40
+ # Apply backtick fixing to the entire docstring first, before line-by-line
41
+ # processing. This ensures that backtick pairs spanning multiple lines are
42
+ # handled correctly.
43
+ if fix_rst_backticks:
44
+ docstring_ = _fix_rst_backticks(docstring_)
45
+
40
46
  lines: list[str] = docstring_.splitlines()
41
47
  if not lines:
42
48
  return docstring_
@@ -132,10 +138,7 @@ def wrap_docstring_numpy(
132
138
  continue
133
139
 
134
140
  # Description lines (typically indented): wrap if too long
135
- line_to_process = (
136
- _fix_rst_backticks(line) if fix_rst_backticks else line
137
- )
138
- collect_to_temp_output(temp_out, line_to_process)
141
+ collect_to_temp_output(temp_out, line)
139
142
  i += 1
140
143
  continue
141
144
 
@@ -152,18 +155,12 @@ def wrap_docstring_numpy(
152
155
  i += 1
153
156
  continue
154
157
 
155
- line_to_process = (
156
- _fix_rst_backticks(line) if fix_rst_backticks else line
157
- )
158
- collect_to_temp_output(temp_out, line_to_process)
158
+ collect_to_temp_output(temp_out, line)
159
159
  i += 1
160
160
  continue
161
161
 
162
162
  # Examples or any other section
163
- line_to_process = (
164
- _fix_rst_backticks(line) if fix_rst_backticks else line
165
- )
166
- collect_to_temp_output(temp_out, line_to_process)
163
+ collect_to_temp_output(temp_out, line)
167
164
  i += 1
168
165
 
169
166
  out: list[str] = process_temp_output(temp_out, width=line_length)
@@ -415,15 +412,51 @@ def handle_single_line_docstring(
415
412
  return whole_docstring_literal
416
413
 
417
414
 
415
+ # Precompiled regex for fixing RST backticks.
416
+ # Pattern matches inline literals while avoiding roles, cross-references, and
417
+ # links. See the documentation of _fix_rst_backticks() for more details.
418
+ # The pattern allows backticks after: start of line, whitespace, parentheses,
419
+ # or certain punctuation (like > and . for `>>> ` and `... ` literals)
420
+ # Note: We match [^`]+ (anything except backticks) and then check in the
421
+ # replacement function whether it's an external link (contains < followed by >)
422
+ # The opening backtick must not be immediately followed by _ or __ (to avoid
423
+ # matching the trailing backtick of cross-references like `text`_ or `text`__)
424
+ _RST_BACKTICK_PATTERN = re.compile(
425
+ r'(?:^|(?<=\s)|(?<=\()|(?<=[>.]))(?::[\w-]+:)?`(?!_)([^`]+)`(?!`)(?!__)(?!_)'
426
+ )
427
+
428
+
418
429
  def _fix_rst_backticks(docstring: str) -> str:
419
430
  """
420
- Fix single backticks to double backticks per rST syntax.
421
-
422
- This function converts pairs of single backticks (`) to pairs of double
423
- backticks (``). It handles various edge cases:
424
- - Preserves existing double backticks
425
- - Handles nested backticks
426
- - Preserves code blocks and literal blocks
431
+ Fix inline-literal single backticks to double backticks per rST syntax.
432
+
433
+ This function converts pairs of single backticks (`` `...` ``) that
434
+ represent inline *literals* into pairs of double backticks (`` ``...`` ``).
435
+ It deliberately **does not** modify other rST constructs that require
436
+ single backticks.
437
+
438
+ What stays untouched
439
+ --------------------
440
+ - Existing double-backtick literals: ````code````.
441
+ - Roles: ``:role:`text``` (e.g., ``:emphasis:`word```).
442
+ - Cross-references: `` `text`_ `` and anonymous refs `` `text`__ ``.
443
+ - Inline external links: `` `text <https://example.com>`_ ``.
444
+ - Explicit hyperlink targets: ``.. _`Label`: https://example.com``.
445
+ - REPL lines: Lines starting with ``>>> `` or ``... `` (Python examples).
446
+
447
+ How it works (regex guards)
448
+ ---------------------------
449
+ The pattern only upgrades a match when **all** these are true:
450
+ - Opening backtick is not part of an existing ````...```` (``(?<!`)``).
451
+ - Opening backtick is not immediately preceded by ``:`` (to avoid roles).
452
+ - Opening backtick is not immediately preceded by ``_`` (to avoid
453
+ explicit targets like ``.. _`Label`: …``).
454
+ - The enclosed text contains **no** backticks and **no** ``<`` (to avoid
455
+ inline-link forms like `` `text <url>`_ ``).
456
+ - Closing backtick is not part of ````...```` (``(?!`)``).
457
+ - Closing backtick is not followed by ``__`` or ``_`` (to avoid
458
+ anonymous/named references).
459
+ - The line does not start with ``>>> `` or ``... `` (Python REPL).
427
460
 
428
461
  Parameters
429
462
  ----------
@@ -433,23 +466,91 @@ def _fix_rst_backticks(docstring: str) -> str:
433
466
  Returns
434
467
  -------
435
468
  str
436
- The docstring with single backticks converted to double backticks.
469
+ The docstring with only inline-literal backticks fixed.
437
470
 
438
471
  Examples
439
472
  --------
440
473
  >>> _fix_rst_backticks('Use `foo` to do something')
441
474
  'Use ``foo`` to do something'
475
+
476
+ >>> _fix_rst_backticks('Edge punctuation: `x`.')
477
+ 'Edge punctuation: ``x``.'
478
+
479
+ >>> _fix_rst_backticks(':emphasis:`word`')
480
+ ':emphasis:`word`'
481
+
482
+ >>> _fix_rst_backticks('See `Link`_ for details')
483
+ 'See `Link`_ for details'
484
+
485
+ >>> _fix_rst_backticks('`Python <https://www.python.org>`_')
486
+ '`Python <https://www.python.org>`_'
487
+
488
+ >>> _fix_rst_backticks('.. _`Special Target`: https://example.com/special')
489
+ '.. _`Special Target`: https://example.com/special'
490
+
442
491
  >>> _fix_rst_backticks('Already has ``foo`` double backticks')
443
492
  'Already has ``foo`` double backticks'
493
+
494
+ >>> _fix_rst_backticks('>>> `foo` in REPL')
495
+ '>>> `foo` in REPL'
444
496
  """
445
- # Pattern to match single backticks that are not already part of double
446
- # backticks. We look for:
447
- # - A backtick not preceded by a backtick: (?<!`)
448
- # - Followed by one or more non-backtick characters: ([^`]+)
449
- # - Followed by a backtick not followed by another backtick: `(?!`)
450
- #
451
- # This pattern avoids matching backticks that are already part of ``...``
452
- pattern = r'(?<!`)`([^`]+)`(?!`)'
453
-
454
- # Replace single backtick pairs with double backtick pairs
455
- return re.sub(pattern, r'``\1``', docstring)
497
+
498
+ def replace_func(match: re.Match[str]) -> str:
499
+ # match.group(0) is the full match
500
+ # match.group(1) is the content between backticks
501
+ full_match: str = match.group(0)
502
+ content: str = match.group(1)
503
+
504
+ # If the match includes a role prefix (like :emphasis:), don't replace
505
+ if ':' in full_match and full_match.index('`') > 0:
506
+ # Check if there's a role prefix before the backtick
507
+ before_backtick = full_match[: full_match.index('`')]
508
+ if ':' in before_backtick:
509
+ return full_match # Keep original (it's a role)
510
+
511
+ # Check if this is an external link (contains <...> pattern)
512
+ # External links look like: `text <url>`_
513
+ if '<' in content and '>' in content:
514
+ # Check if < comes before > (basic validation)
515
+ if content.index('<') < content.rindex('>'):
516
+ return full_match # Keep original (it's an external link)
517
+
518
+ # Otherwise, replace single backticks with double
519
+ # Keep any leading whitespace/parenthesis/punctuation
520
+ prefix = match.group(0)[: match.group(0).index('`')]
521
+ return f'{prefix}``{content}``'
522
+
523
+ # Protect REPL lines (>>> and ...) from backtick fixing by temporarily
524
+ # replacing them with placeholders, then restoring after processing.
525
+ # This allows multi-line backtick pairs (such as external links spanning
526
+ # lines) to be handled correctly while still preserving backticks in REPL
527
+ # comments.
528
+ lines = docstring.splitlines(keepends=True)
529
+ repl_lines: dict[int, str] = {}
530
+ protected_lines: list[str] = []
531
+
532
+ for i, line in enumerate(lines):
533
+ stripped = line.lstrip()
534
+ # Protect REPL lines (>>> or ...) - don't fix backticks in these
535
+ if stripped.startswith('>>> ') or stripped.startswith('... '):
536
+ repl_lines[i] = line
537
+ # Use a placeholder that won't be matched by the regex
538
+ protected_lines.append(
539
+ '\x00REPL_LINE\x00\n'
540
+ if line.endswith('\n')
541
+ else '\x00REPL_LINE\x00'
542
+ )
543
+ else:
544
+ protected_lines.append(line)
545
+
546
+ # Process the entire docstring (with REPL lines protected)
547
+ protected_docstring = ''.join(protected_lines)
548
+ processed = _RST_BACKTICK_PATTERN.sub(replace_func, protected_docstring)
549
+
550
+ # Restore REPL lines
551
+ result_lines = processed.splitlines(keepends=True)
552
+ for i, original_line in repl_lines.items():
553
+ if i < len(result_lines):
554
+ result_lines[i] = original_line
555
+
556
+ return ''.join(result_lines)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: format-docstring
3
- Version: 0.1.6
3
+ Version: 0.1.8
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>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "format-docstring"
7
- version = "0.1.6"
7
+ version = "0.1.8"
8
8
  description = "A Python formatter to wrap/adjust docstring lines"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -41,6 +41,15 @@ def process_data(data, config=None):
41
41
  When `mode` is set to `'advanced'`, additional processing steps are
42
42
  applied. The `threshold` parameter controls filtering behavior.
43
43
 
44
+ For rST roles like :emphasis:`important` and :sup:`2`, the single backticks
45
+ should remain. Cross-references like `Section`_ and anonymous refs like
46
+ `Link`__ should not be modified. External links such as `Python
47
+ <https://www.python.org>`_ must stay as-is.
48
+
49
+ Here's another example where long URLs extend to the next line `Here is the
50
+ Link <https://www.this-is-a-url-that-is-long.com>`_ and `Another One
51
+ <https://www.this-is-another-url-that-is-long.com>`_.
52
+
44
53
  Examples
45
54
  --------
46
55
  >>> data = [{'id': 1, 'value': 10}]
@@ -98,14 +107,14 @@ def process_data(data, config=None):
98
107
  config : dict, optional
99
108
  Configuration dictionary. Valid keys are ``mode``, ``threshold``, and
100
109
  ``output_format``. Default is ``None``.
101
- processor : `DataProcessor`
110
+ processor : ``DataProcessor``
102
111
  The ``processor`` instance to use for data transformation.
103
112
 
104
113
  Returns
105
114
  -------
106
115
  dict
107
116
  Processed data with keys ``results``, ``metadata``, and ``status``.
108
- `ProcessedData`
117
+ ``ProcessedData``
109
118
  Alternative format with ``data`` and ``errors`` fields.
110
119
 
111
120
  Raises
@@ -123,6 +132,15 @@ def process_data(data, config=None):
123
132
  When ``mode`` is set to ``'advanced'``, additional processing steps are
124
133
  applied. The ``threshold`` parameter controls filtering behavior.
125
134
 
135
+ For rST roles like :emphasis:`important` and :sup:`2`, the single backticks
136
+ should remain. Cross-references like `Section`_ and anonymous refs like
137
+ `Link`__ should not be modified. External links such as `Python
138
+ <https://www.python.org>`_ must stay as-is.
139
+
140
+ Here's another example where long URLs extend to the next line `Here is the
141
+ Link <https://www.this-is-a-url-that-is-long.com>`_ and `Another One
142
+ <https://www.this-is-another-url-that-is-long.com>`_.
143
+
126
144
  Examples
127
145
  --------
128
146
  >>> data = [{'id': 1, 'value': 10}]
@@ -149,7 +167,7 @@ class DataProcessor:
149
167
  ``'advanced'``.
150
168
  pipelines : list
151
169
  List of ``Pipeline`` objects to apply.
152
- config : `ConfigDict`
170
+ config : ``ConfigDict``
153
171
  Configuration for the ``processor`` with ``settings`` dictionary.
154
172
  """
155
173
 
@@ -29,6 +29,18 @@ Notes
29
29
  When using `special_value`, ensure that `config` is set properly. The
30
30
  `default_value` is `None` by default.
31
31
 
32
+ This section also contains rST roles like :emphasis:`word` and :strong:`bold`
33
+ that should remain unchanged. Cross-references like `Section`_ should not be
34
+ modified, nor should anonymous refs like `Docs`__. External links like `Python
35
+ <https://www.python.org>`_ must also stay as-is.
36
+
37
+ Explicit targets like shown in the directive ``.. _`Special Target`:
38
+ https://example.com/special`` should not be touched.
39
+
40
+ Here's another example where long URLs extend to the next line `Here is perhaps
41
+ the Link <https://www.this-is-a-url-that-is-long.com>`_ and `Another One
42
+ <https://www.this-is-another-url-that-is-long.com>`_.
43
+
32
44
  Examples
33
45
  --------
34
46
  >>> data = [{'id': 1, 'value': 10}]
@@ -47,14 +59,14 @@ arg1 : str
47
59
  Use ``method_name`` to access this value.
48
60
  arg2 : int
49
61
  The value should be between ``1`` and ``100``.
50
- config : `ConfigType`
62
+ config : ``ConfigType``
51
63
  A ``Config`` object for settings.
52
64
 
53
65
  Returns
54
66
  -------
55
67
  dict
56
68
  A dictionary with keys ``result`` and ``status``.
57
- `ResultDict`
69
+ ``ResultDict``
58
70
  An alternative return format with ``data`` field.
59
71
 
60
72
  See Also
@@ -67,6 +79,18 @@ Notes
67
79
  When using ``special_value``, ensure that ``config`` is set properly. The
68
80
  ``default_value`` is ``None`` by default.
69
81
 
82
+ This section also contains rST roles like :emphasis:`word` and :strong:`bold`
83
+ that should remain unchanged. Cross-references like `Section`_ should not be
84
+ modified, nor should anonymous refs like `Docs`__. External links like `Python
85
+ <https://www.python.org>`_ must also stay as-is.
86
+
87
+ Explicit targets like shown in the directive ``.. _`Special Target`:
88
+ https://example.com/special`` should not be touched.
89
+
90
+ Here's another example where long URLs extend to the next line `Here is perhaps
91
+ the Link <https://www.this-is-a-url-that-is-long.com>`_ and `Another One
92
+ <https://www.this-is-another-url-that-is-long.com>`_.
93
+
70
94
  Examples
71
95
  --------
72
96
  >>> data = [{'id': 1, 'value': 10}]
@@ -5,6 +5,7 @@ import pytest
5
5
  from format_docstring.docstring_rewriter import wrap_docstring
6
6
  from format_docstring.line_wrap_numpy import (
7
7
  _fix_colon_spacing,
8
+ _fix_rst_backticks,
8
9
  _get_section_heading_title,
9
10
  _is_hyphen_underline,
10
11
  _is_param_signature,
@@ -243,6 +244,81 @@ def test_standardize_default_value(line: str, expected: str) -> None:
243
244
  assert _standardize_default_value(line) == expected
244
245
 
245
246
 
247
+ @pytest.mark.parametrize(
248
+ 'src, expected',
249
+ [
250
+ # --- should fix (inline literals) ---
251
+ ('Use `foo` to do something', 'Use ``foo`` to do something'),
252
+ ('Edge punctuation: `x`.', 'Edge punctuation: ``x``.'),
253
+ (
254
+ 'Multiple inline literals: `a` and `b`.',
255
+ 'Multiple inline literals: ``a`` and ``b``.',
256
+ ),
257
+ (
258
+ 'Underscores inside literal are fine: `foo_bar`.',
259
+ 'Underscores inside literal are fine: ``foo_bar``.',
260
+ ),
261
+ (
262
+ 'Adjacent to parentheses: (`call_me`) and `ok`',
263
+ 'Adjacent to parentheses: (``call_me``) and ``ok``',
264
+ ),
265
+ # Edge cases: literals that contain special characters -> should fix
266
+ ('`>>> `', '``>>> ``'),
267
+ ('`... `', '``... ``'),
268
+ # --- should not fix (roles) ---
269
+ (':emphasis:`word`', ':emphasis:`word`'),
270
+ (':strong:`bold`', ':strong:`bold`'),
271
+ (':sup:`2`', ':sup:`2`'),
272
+ (
273
+ ':title-reference:`The Great Book`',
274
+ ':title-reference:`The Great Book`',
275
+ ),
276
+ # --- should not fix (cross-references & links) ---
277
+ ('See `Section`_ for details', 'See `Section`_ for details'),
278
+ (
279
+ 'See `Docs`__ for the anonymous reference',
280
+ 'See `Docs`__ for the anonymous reference',
281
+ ),
282
+ (
283
+ '`Python <https://www.python.org>`_',
284
+ '`Python <https://www.python.org>`_',
285
+ ),
286
+ # --- should not fix (already correct literals) ---
287
+ (
288
+ 'Already has ``double`` backticks',
289
+ 'Already has ``double`` backticks',
290
+ ),
291
+ # --- should not fix (explicit hyperlink target) ---
292
+ (
293
+ '.. _`Special Target`: https://example.com/special',
294
+ '.. _`Special Target`: https://example.com/special',
295
+ ),
296
+ # --- should not fix (multi-line external links) ---
297
+ (
298
+ "Here's another example where long URLs"
299
+ ' extend to the next line `Here is perhaps\n'
300
+ 'the Link <https://www.this-is-a-url-that-is-long.com>`_'
301
+ ' and `Another One\n'
302
+ '<https://www.this-is-another-url-that-is-long.com>`_.',
303
+ "Here's another example where long URLs extend to"
304
+ ' the next line `Here is perhaps\n'
305
+ 'the Link <https://www.this-is-a-url-that-is-long.com>`_'
306
+ ' and `Another One\n'
307
+ '<https://www.this-is-another-url-that-is-long.com>`_.',
308
+ ),
309
+ # --- should not fix (REPL lines with backticks) ---
310
+ (
311
+ '>>> # Use `config` parameter to customize `mode`\n'
312
+ '... # and set the `threshold` value',
313
+ '>>> # Use `config` parameter to customize `mode`\n'
314
+ '... # and set the `threshold` value',
315
+ ),
316
+ ],
317
+ )
318
+ def test_fix_rst_backticks_cases(src: str, expected: str) -> None:
319
+ assert _fix_rst_backticks(src) == expected
320
+
321
+
246
322
  @pytest.mark.parametrize(
247
323
  'fix_rst_backticks, input_docstring, expected_docstring',
248
324
  [
@@ -258,10 +334,10 @@ def test_standardize_default_value(line: str, expected: str) -> None:
258
334
  ),
259
335
  ],
260
336
  )
261
- def test_fix_rst_backticks(
337
+ def test_fix_rst_backticks_option_on_and_off(
262
338
  fix_rst_backticks: bool, input_docstring: str, expected_docstring: str
263
339
  ) -> None:
264
- """Test that backticks are fixed or preserved based on fix_rst_backticks"""
340
+ """Verify the `fix_rst_backticks` option can be correctly turned on/off"""
265
341
  result = wrap_docstring(
266
342
  input_docstring,
267
343
  line_length=79,