format-docstring 0.1.7__tar.gz → 0.1.9__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.1.7 → format_docstring-0.1.9}/.pre-commit-config.yaml +24 -0
- format_docstring-0.1.9/AGENTS.md +93 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/CHANGELOG.md +35 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/PKG-INFO +22 -2
- {format_docstring-0.1.7 → format_docstring-0.1.9}/README.md +21 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/format_docstring/base_fixer.py +26 -2
- {format_docstring-0.1.7 → format_docstring-0.1.9}/format_docstring/docstring_rewriter.py +71 -29
- {format_docstring-0.1.7 → format_docstring-0.1.9}/format_docstring/line_wrap_numpy.py +98 -38
- {format_docstring-0.1.7 → format_docstring-0.1.9}/format_docstring/line_wrap_utils.py +62 -32
- {format_docstring-0.1.7 → format_docstring-0.1.9}/format_docstring/main_jupyter.py +21 -6
- {format_docstring-0.1.7 → format_docstring-0.1.9}/format_docstring/main_py.py +16 -1
- {format_docstring-0.1.7 → format_docstring-0.1.9}/format_docstring.egg-info/PKG-INFO +22 -2
- {format_docstring-0.1.7 → format_docstring-0.1.9}/format_docstring.egg-info/SOURCES.txt +7 -1
- {format_docstring-0.1.7 → format_docstring-0.1.9}/format_docstring.egg-info/requires.txt +0 -1
- {format_docstring-0.1.7 → format_docstring-0.1.9}/muff.toml +1 -1
- {format_docstring-0.1.7 → format_docstring-0.1.9}/pyproject.toml +1 -2
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/helpers.py +2 -1
- format_docstring-0.1.9/tests/test_base_fixer.py +47 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_config.py +2 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/fix_rst_backticks.txt +11 -3
- format_docstring-0.1.9/tests/test_data/end_to_end/numpy/no_format_docstring_comment.txt +54 -0
- format_docstring-0.1.9/tests/test_data/end_to_end/numpy/section_headings_with_colons.txt +68 -0
- format_docstring-0.1.9/tests/test_data/jupyter/before.ipynb +28 -0
- format_docstring-0.1.9/tests/test_data/jupyter/verbose_before.ipynb +28 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/fix_rst_backticks.txt +10 -2
- format_docstring-0.1.9/tests/test_data/line_wrap/numpy/section_headings_with_colons.txt +60 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_docstring_rewriter.py +5 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_line_wrap_numpy.py +23 -1
- format_docstring-0.1.9/tests/test_main_jupyter.py +90 -0
- format_docstring-0.1.9/tests/test_main_py.py +88 -0
- format_docstring-0.1.7/CLAUDE.md +0 -151
- format_docstring-0.1.7/tests/test_main_jupyter.py +0 -45
- format_docstring-0.1.7/tests/test_main_py.py +0 -42
- {format_docstring-0.1.7 → format_docstring-0.1.9}/.github/workflows/python-package.yml +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/.github/workflows/python-publish.yml +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/.gitignore +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/.pre-commit-hooks.yaml +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/LICENSE +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/format_docstring/__init__.py +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/format_docstring/config.py +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/format_docstring/line_wrap_google.py +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/format_docstring.egg-info/dependency_links.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/format_docstring.egg-info/entry_points.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/format_docstring.egg-info/top_level.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/requirements.dev +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/setup.cfg +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/__init__.py +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/README.md +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/colon_spacing_fix.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/contents_that_are_not_wrapped.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/default_value_standardization.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/empty_lines_are_respected.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/examples_section.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/existing_linebreaks_should_not_be_respected.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/four_level_nested_classes.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/indent_four_levels_16_spaces_width_10.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/indent_misaligned_all.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/indent_two_levels_8_spaces.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/line_length_2.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/mismatched_underlines.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/mismatched_underlines_one_dash.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/mismatched_underlines_two_dashes.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/module_level_docstring.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/new_lines_before_and_after.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/param_signature_without_type.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/parameters_returns_raises_wrapping.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/returns_signature_and_description.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/section_title_fixed.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/sections_notes_examples.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/signature_line_is_not_wrapped.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/single_line_docstring.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/texts_are_rewrapped.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/end_to_end/numpy/very_long_unbreakable_word.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/integration_test/numpy/after.ipynb +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/integration_test/numpy/after.py +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/integration_test/numpy/after_50.ipynb +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/integration_test/numpy/after_50.py +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/integration_test/numpy/before.ipynb +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/integration_test/numpy/before.py +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/README.md +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/colon_spacing_fix.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/contents_that_are_not_wrapped.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/default_value_standardization.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/empty_lines_are_respected.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/examples_section.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/existing_linebreaks_should_not_be_respected.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/indent_four_levels_16_spaces_width_10.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/indent_two_levels_8_spaces.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/line_length_2.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/mismatched_underlines.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/mismatched_underlines_one_dash.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/mismatched_underlines_two_dashes.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/module_level_docstring.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/param_signature_without_type.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/parameters_returns_raises_wrapping.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/returns_signature_and_description.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/section_title_fixed.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/sections_notes_examples.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/signature_line_is_not_wrapped.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/texts_are_rewrapped.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/line_wrap/numpy/very_long_unbreakable_word.txt +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_data/playground.py +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_line_wrap_google.py +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_line_wrap_utils.py +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tests/test_playground.py +0 -0
- {format_docstring-0.1.7 → format_docstring-0.1.9}/tox.ini +0 -0
|
@@ -40,3 +40,27 @@ repos:
|
|
|
40
40
|
rev: 0.0.11
|
|
41
41
|
hooks:
|
|
42
42
|
- id: markdown-toc-creator
|
|
43
|
+
- repo: local
|
|
44
|
+
hooks:
|
|
45
|
+
- id: format-docstring
|
|
46
|
+
name: Format self (python docstrings)
|
|
47
|
+
entry: python
|
|
48
|
+
args: [-m, format_docstring.main_py, --verbose, diff]
|
|
49
|
+
language: python
|
|
50
|
+
types: [python]
|
|
51
|
+
exclude: ^tests/test_data/
|
|
52
|
+
additional_dependencies:
|
|
53
|
+
- click>=8.0
|
|
54
|
+
- jupyter-notebook-parser>=0.1.4
|
|
55
|
+
- tomli>=1.1.0
|
|
56
|
+
- id: format-docstring-jupyter
|
|
57
|
+
name: Format self (docstrings in Jupyter notebooks)
|
|
58
|
+
entry: python
|
|
59
|
+
args: [-m, format_docstring.main_jupyter, --verbose, diff]
|
|
60
|
+
language: python
|
|
61
|
+
files: ^.*\.ipynb$
|
|
62
|
+
exclude: ^tests/test_data/
|
|
63
|
+
additional_dependencies:
|
|
64
|
+
- click>=8.0
|
|
65
|
+
- jupyter-notebook-parser>=0.1.4
|
|
66
|
+
- tomli>=1.1.0
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
This guide briefs coding agents working on `format-docstring`. Use it to get
|
|
4
|
+
oriented before making changes.
|
|
5
|
+
|
|
6
|
+
## Quick Snapshot
|
|
7
|
+
|
|
8
|
+
- Formats NumPy-style docstrings (with experimental Google support) in `.py`
|
|
9
|
+
files and Jupyter notebooks while preserving surrounding code.
|
|
10
|
+
- Distributed as a CLI (`format-docstring`, `format-docstring-jupyter`) with
|
|
11
|
+
minimum Python 3.10, packaged via `setuptools`.
|
|
12
|
+
- Core dependencies: `click`, `docstring_parser_fork`,
|
|
13
|
+
`jupyter-notebook-parser`, and `tomli/tomllib` for configuration loading.
|
|
14
|
+
- Version is sourced dynamically in `format_docstring/__init__.py`.
|
|
15
|
+
|
|
16
|
+
## Repository Layout
|
|
17
|
+
|
|
18
|
+
- `format_docstring/main_py.py` – Click CLI for Python files; validates input
|
|
19
|
+
and delegates to `PythonFileFixer`.
|
|
20
|
+
- `format_docstring/main_jupyter.py` – CLI for `.ipynb` files built on
|
|
21
|
+
`JupyterNotebookParser`/`JupyterNotebookRewriter`.
|
|
22
|
+
- `format_docstring/base_fixer.py` – Shared directory traversal, exclusion
|
|
23
|
+
regex handling, and `fix_one_directory_or_one_file` orchestration.
|
|
24
|
+
- `format_docstring/docstring_rewriter.py` – AST-based docstring extraction and
|
|
25
|
+
replacement that leaves non-docstring text untouched.
|
|
26
|
+
- `format_docstring/line_wrap_numpy.py` / `line_wrap_google.py` –
|
|
27
|
+
Style-specific wrapping helpers; NumPy path is the production code path,
|
|
28
|
+
Google is partial.
|
|
29
|
+
- `format_docstring/line_wrap_utils.py` – Shared utilities for wrapping (indent
|
|
30
|
+
management, bullet handling, preserving literal blocks, etc.).
|
|
31
|
+
- `format_docstring/config.py` – `pyproject.toml` discovery, parsing, and Click
|
|
32
|
+
default injection.
|
|
33
|
+
- `tests/` – Pytest suite with fixture-driven cases in `tests/test_data/`; see
|
|
34
|
+
`tests/helpers.py` for fixture loading helpers.
|
|
35
|
+
|
|
36
|
+
## Implementation Notes
|
|
37
|
+
|
|
38
|
+
- `docstring_rewriter.fix_src` parses with `ast.parse`, collects docstring
|
|
39
|
+
literals, and rewrites source slices using absolute offsets from
|
|
40
|
+
`calc_line_starts`; this avoids `ast.unparse` and keeps comments/spacing.
|
|
41
|
+
- Wrapping honors NumPy section heuristics, rST constructs, code fences,
|
|
42
|
+
`Examples` prompts, and literal blocks introduced by `::`.
|
|
43
|
+
- CLI exposes `--docstring-style`, but the Python entry-point currently raises
|
|
44
|
+
if a non-NumPy style is requested; Jupyter flow passes style through
|
|
45
|
+
unchanged.
|
|
46
|
+
- `BaseFixer` subclasses return `1` when any file changed so callers can
|
|
47
|
+
surface non-zero exit codes.
|
|
48
|
+
- Notebook fixer round-trips JSON via `json.dump(..., indent=1)` and rewrites
|
|
49
|
+
cells only when content changes, preserving magics with `reconstruct_source`.
|
|
50
|
+
|
|
51
|
+
## Configuration
|
|
52
|
+
|
|
53
|
+
- User-facing configuration lives under `[tool.format_docstring]` inside
|
|
54
|
+
`pyproject.toml` and supports `line_length`, `docstring_style`, `exclude`,
|
|
55
|
+
`fix_rst_backticks`, and `verbose` (`default` or `diff` to print unified
|
|
56
|
+
diffs on rewrites).
|
|
57
|
+
- `config.inject_config_from_file` auto-discovers the nearest `pyproject.toml`
|
|
58
|
+
(walking up from targets) and merges values into Click’s `default_map`.
|
|
59
|
+
- Default exclude pattern is `\.git|\.tox|\.pytest_cache`; tests tweak it as
|
|
60
|
+
needed.
|
|
61
|
+
|
|
62
|
+
## Development Workflow
|
|
63
|
+
|
|
64
|
+
- Install: `pip install -e .` for the project,
|
|
65
|
+
`pip install -r requirements.dev` for tooling.
|
|
66
|
+
- Tests: `pytest --tb=long`, or target modules such as
|
|
67
|
+
`pytest tests/test_docstring_rewriter.py`.
|
|
68
|
+
- Lint/format: `muff check --fix --config=muff.toml format_docstring tests`,
|
|
69
|
+
`muff format --diff --config=muff.toml format_docstring tests`.
|
|
70
|
+
- Type checking: `mypy format_docstring/`.
|
|
71
|
+
- Tox: `tox` for the full matrix (py310–py313, mypy, lint), or focused runs
|
|
72
|
+
like `tox -e py311`, `tox -e mypy`, `tox -e muff-format`.
|
|
73
|
+
- CLI smoke tests: `format-docstring --help`,
|
|
74
|
+
`format-docstring-jupyter --help`.
|
|
75
|
+
- Pre-commit: `pre-commit run -a`.
|
|
76
|
+
|
|
77
|
+
## Testing Notes
|
|
78
|
+
|
|
79
|
+
- Fixture files under `tests/test_data/line_wrap` and
|
|
80
|
+
`tests/test_data/end_to_end` use `LINE_LENGTH: <int>` headers followed by
|
|
81
|
+
`BEFORE`/`AFTER` sections split by `**********`.
|
|
82
|
+
- `tests/test_playground.py` focuses on regression snippets;
|
|
83
|
+
`tests/test_config.py` exercises config discovery and CLI overrides.
|
|
84
|
+
- When modifying wrapping rules, update both the helper (`line_wrap_utils.py`)
|
|
85
|
+
and the corresponding expectation files in `tests/test_data/`.
|
|
86
|
+
|
|
87
|
+
## Style Guidance
|
|
88
|
+
|
|
89
|
+
- Formatting rules mirror `muff.toml` (line length 79, single quotes, NumPy
|
|
90
|
+
docstring convention). Respect these when adding code.
|
|
91
|
+
- Keep docstring style tests conservative: avoid mutating non-docstring
|
|
92
|
+
content, and add regression cases whenever handling around literal sections
|
|
93
|
+
or tables changes.
|
|
@@ -6,6 +6,39 @@ 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.9] - 2025-10-16
|
|
10
|
+
|
|
11
|
+
- Added
|
|
12
|
+
- A "`# no-format-docstring`" directive to ignore certain docstring
|
|
13
|
+
- Verbose diff output via `--verbose diff` and
|
|
14
|
+
`[tool.format_docstring] verbose`
|
|
15
|
+
- Normalize NumPy section headings that include trailing colons (e.g.,
|
|
16
|
+
`Parameters:`); also, fix Google-style "Arg" header into "Parameters"
|
|
17
|
+
- Changed
|
|
18
|
+
- Added "self format" pre-commit hook to format docstrings within this repo
|
|
19
|
+
with its own formatting logic
|
|
20
|
+
|
|
21
|
+
## [0.1.8] - 2025-10-14
|
|
22
|
+
|
|
23
|
+
- Fixed
|
|
24
|
+
- Bug in `_fix_rst_backticks()` where backtick pairs spanning multiple lines
|
|
25
|
+
(e.g., multi-line external links) were incorrectly processed
|
|
26
|
+
- Added `(?!_)` lookahead to regex pattern to prevent matching trailing
|
|
27
|
+
backticks from cross-references (e.g., `` `text`_ ``)
|
|
28
|
+
- Changed
|
|
29
|
+
- Moved backtick fixing from line-by-line processing to whole-docstring
|
|
30
|
+
processing to correctly handle multi-line constructs
|
|
31
|
+
- REPL lines (starting with `>>> ` or `... `) are now protected with
|
|
32
|
+
placeholders during backtick fixing to preserve backticks in Python
|
|
33
|
+
examples
|
|
34
|
+
- Added
|
|
35
|
+
- Test cases for multi-line external links in
|
|
36
|
+
`test_fix_rst_backticks_cases()`
|
|
37
|
+
- Test cases for REPL lines with backticks in
|
|
38
|
+
`test_fix_rst_backticks_cases()`
|
|
39
|
+
- Full diff
|
|
40
|
+
- https://github.com/jsh9/format-docstring/compare/0.1.7...0.1.8
|
|
41
|
+
|
|
9
42
|
## [0.1.7] - 2025-10-14
|
|
10
43
|
|
|
11
44
|
- Fixed
|
|
@@ -13,6 +46,8 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
13
46
|
external links (e.g., `` `Python <https://example.org>`_ ``)
|
|
14
47
|
- Refactored `_fix_rst_backticks()` to use pre-compiled regex pattern for
|
|
15
48
|
better performance
|
|
49
|
+
- Full diff
|
|
50
|
+
- https://github.com/jsh9/format-docstring/compare/0.1.6...0.1.7
|
|
16
51
|
|
|
17
52
|
## [0.1.6] - 2025-10-13
|
|
18
53
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: format-docstring
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
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>
|
|
@@ -27,7 +27,6 @@ Description-Content-Type: text/markdown
|
|
|
27
27
|
License-File: LICENSE
|
|
28
28
|
Requires-Dist: jupyter-notebook-parser>=0.1.4
|
|
29
29
|
Requires-Dist: click>=8.0
|
|
30
|
-
Requires-Dist: docstring_parser_fork>=0.0.14
|
|
31
30
|
Requires-Dist: tomli>=1.1.0; python_version < "3.11"
|
|
32
31
|
Dynamic: license-file
|
|
33
32
|
|
|
@@ -50,6 +49,7 @@ A Python formatter to automatically format numpy-style docstrings.
|
|
|
50
49
|
- [4. Usage](#4-usage)
|
|
51
50
|
- [4.1. Command Line Interface](#41-command-line-interface)
|
|
52
51
|
- [4.2. Pre-commit Hook](#42-pre-commit-hook)
|
|
52
|
+
- [4.3. Opting Out of Formatting](#43-opting-out-of-formatting)
|
|
53
53
|
- [5. Configuration](#5-configuration)
|
|
54
54
|
- [5.1. Command-Line Options](#51-command-line-options)
|
|
55
55
|
- [5.2. Usage Examples](#52-usage-examples)
|
|
@@ -281,6 +281,19 @@ Then install the pre-commit hook:
|
|
|
281
281
|
pre-commit install
|
|
282
282
|
```
|
|
283
283
|
|
|
284
|
+
### 4.3. Opting Out of Formatting
|
|
285
|
+
|
|
286
|
+
Add a comment containing `no-format-docstring` on the same line as the closing
|
|
287
|
+
triple quotes to prevent the formatter from touching that docstring:
|
|
288
|
+
`""" ... """ # no-format-docstring`.
|
|
289
|
+
|
|
290
|
+
You can combine this "no-format-docstring" with other directives like "noqa".
|
|
291
|
+
|
|
292
|
+
Tip: If you only want to keep specific formatter changes inside a docstring,
|
|
293
|
+
first run `format-docstring`, accept the parts you like, revert the edits you
|
|
294
|
+
dislike, and then add an inline `# no-format-docstring` comment so future runs
|
|
295
|
+
leave that docstring untouched.
|
|
296
|
+
|
|
284
297
|
## 5. Configuration
|
|
285
298
|
|
|
286
299
|
### 5.1. Command-Line Options
|
|
@@ -291,6 +304,8 @@ pre-commit install
|
|
|
291
304
|
default: `numpy`). Note: Currently only `numpy` style is fully supported.
|
|
292
305
|
- `--fix-rst-backticks BOOL`: Automatically fix single backticks to double
|
|
293
306
|
backticks per rST syntax (default: True)
|
|
307
|
+
- `--verbose CHOICE`: Logging detail level (`default` keeps the existing
|
|
308
|
+
behaviour, `diff` prints unified diffs when rewrites happen)
|
|
294
309
|
- `--exclude TEXT`: Regex pattern to exclude files/directories (default:
|
|
295
310
|
`\.git|\.tox|\.pytest_cache`)
|
|
296
311
|
- `--config PATH`: Path to a `pyproject.toml` config file. If not specified,
|
|
@@ -311,6 +326,9 @@ format-docstring --line-length 72 src/
|
|
|
311
326
|
# Format Jupyter notebooks excluding certain directories
|
|
312
327
|
format-docstring-jupyter --exclude "\.git|\.venv|__pycache__" notebooks/
|
|
313
328
|
|
|
329
|
+
# Preview changes with unified diffs
|
|
330
|
+
format-docstring --verbose diff src/
|
|
331
|
+
|
|
314
332
|
# Use a specific config file
|
|
315
333
|
format-docstring --config path/to/pyproject.toml src/
|
|
316
334
|
|
|
@@ -332,6 +350,7 @@ line_length = 79
|
|
|
332
350
|
docstring_style = "numpy"
|
|
333
351
|
fix_rst_backticks = true
|
|
334
352
|
exclude = "\\.git|\\.venv|__pycache__"
|
|
353
|
+
verbose = "default" # or "diff" to print unified diffs
|
|
335
354
|
```
|
|
336
355
|
|
|
337
356
|
**Available options:**
|
|
@@ -344,6 +363,7 @@ exclude = "\\.git|\\.venv|__pycache__"
|
|
|
344
363
|
backticks per rST syntax (default: `true`)
|
|
345
364
|
- `exclude` (str): Regex pattern to exclude files/directories (default:
|
|
346
365
|
`"\\.git|\\.tox|\\.pytest_cache"`)
|
|
366
|
+
- `verbose` (str): Logging detail level (`"default"` or `"diff"`)
|
|
347
367
|
|
|
348
368
|
The tool searches for `pyproject.toml` starting from the target file/directory
|
|
349
369
|
and walking up the parent directories until one is found.
|
|
@@ -17,6 +17,7 @@ A Python formatter to automatically format numpy-style docstrings.
|
|
|
17
17
|
- [4. Usage](#4-usage)
|
|
18
18
|
- [4.1. Command Line Interface](#41-command-line-interface)
|
|
19
19
|
- [4.2. Pre-commit Hook](#42-pre-commit-hook)
|
|
20
|
+
- [4.3. Opting Out of Formatting](#43-opting-out-of-formatting)
|
|
20
21
|
- [5. Configuration](#5-configuration)
|
|
21
22
|
- [5.1. Command-Line Options](#51-command-line-options)
|
|
22
23
|
- [5.2. Usage Examples](#52-usage-examples)
|
|
@@ -248,6 +249,19 @@ Then install the pre-commit hook:
|
|
|
248
249
|
pre-commit install
|
|
249
250
|
```
|
|
250
251
|
|
|
252
|
+
### 4.3. Opting Out of Formatting
|
|
253
|
+
|
|
254
|
+
Add a comment containing `no-format-docstring` on the same line as the closing
|
|
255
|
+
triple quotes to prevent the formatter from touching that docstring:
|
|
256
|
+
`""" ... """ # no-format-docstring`.
|
|
257
|
+
|
|
258
|
+
You can combine this "no-format-docstring" with other directives like "noqa".
|
|
259
|
+
|
|
260
|
+
Tip: If you only want to keep specific formatter changes inside a docstring,
|
|
261
|
+
first run `format-docstring`, accept the parts you like, revert the edits you
|
|
262
|
+
dislike, and then add an inline `# no-format-docstring` comment so future runs
|
|
263
|
+
leave that docstring untouched.
|
|
264
|
+
|
|
251
265
|
## 5. Configuration
|
|
252
266
|
|
|
253
267
|
### 5.1. Command-Line Options
|
|
@@ -258,6 +272,8 @@ pre-commit install
|
|
|
258
272
|
default: `numpy`). Note: Currently only `numpy` style is fully supported.
|
|
259
273
|
- `--fix-rst-backticks BOOL`: Automatically fix single backticks to double
|
|
260
274
|
backticks per rST syntax (default: True)
|
|
275
|
+
- `--verbose CHOICE`: Logging detail level (`default` keeps the existing
|
|
276
|
+
behaviour, `diff` prints unified diffs when rewrites happen)
|
|
261
277
|
- `--exclude TEXT`: Regex pattern to exclude files/directories (default:
|
|
262
278
|
`\.git|\.tox|\.pytest_cache`)
|
|
263
279
|
- `--config PATH`: Path to a `pyproject.toml` config file. If not specified,
|
|
@@ -278,6 +294,9 @@ format-docstring --line-length 72 src/
|
|
|
278
294
|
# Format Jupyter notebooks excluding certain directories
|
|
279
295
|
format-docstring-jupyter --exclude "\.git|\.venv|__pycache__" notebooks/
|
|
280
296
|
|
|
297
|
+
# Preview changes with unified diffs
|
|
298
|
+
format-docstring --verbose diff src/
|
|
299
|
+
|
|
281
300
|
# Use a specific config file
|
|
282
301
|
format-docstring --config path/to/pyproject.toml src/
|
|
283
302
|
|
|
@@ -299,6 +318,7 @@ line_length = 79
|
|
|
299
318
|
docstring_style = "numpy"
|
|
300
319
|
fix_rst_backticks = true
|
|
301
320
|
exclude = "\\.git|\\.venv|__pycache__"
|
|
321
|
+
verbose = "default" # or "diff" to print unified diffs
|
|
302
322
|
```
|
|
303
323
|
|
|
304
324
|
**Available options:**
|
|
@@ -311,6 +331,7 @@ exclude = "\\.git|\\.venv|__pycache__"
|
|
|
311
331
|
backticks per rST syntax (default: `true`)
|
|
312
332
|
- `exclude` (str): Regex pattern to exclude files/directories (default:
|
|
313
333
|
`"\\.git|\\.tox|\\.pytest_cache"`)
|
|
334
|
+
- `verbose` (str): Logging detail level (`"default"` or `"diff"`)
|
|
314
335
|
|
|
315
336
|
The tool searches for `pyproject.toml` starting from the target file/directory
|
|
316
337
|
and walking up the parent directories until one is found.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import difflib
|
|
3
4
|
import re
|
|
5
|
+
import sys
|
|
4
6
|
from pathlib import Path
|
|
5
7
|
from typing import Any
|
|
6
8
|
|
|
@@ -12,10 +14,12 @@ class BaseFixer:
|
|
|
12
14
|
self,
|
|
13
15
|
path: str,
|
|
14
16
|
exclude_pattern: str = r'\.git|\.tox|\.pytest_cache',
|
|
17
|
+
verbose: str = 'default',
|
|
15
18
|
) -> None:
|
|
16
19
|
"""Initialize the fixer with a path and optional exclude pattern."""
|
|
17
20
|
self.path = path
|
|
18
21
|
self.exclude_pattern = exclude_pattern
|
|
22
|
+
self.verbose = verbose
|
|
19
23
|
|
|
20
24
|
def _get_files_to_process(
|
|
21
25
|
self, directory: Path, pattern: str
|
|
@@ -28,6 +32,25 @@ class BaseFixer:
|
|
|
28
32
|
if not should_exclude_file(f, self.exclude_pattern)
|
|
29
33
|
]
|
|
30
34
|
|
|
35
|
+
def _print_diff(self, filename: str, before: str, after: str) -> None:
|
|
36
|
+
"""Print a unified diff when verbose mode is enabled."""
|
|
37
|
+
if self.verbose != 'diff':
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
diff = difflib.unified_diff(
|
|
41
|
+
before.splitlines(keepends=True),
|
|
42
|
+
after.splitlines(keepends=True),
|
|
43
|
+
fromfile=f'{filename} (before)',
|
|
44
|
+
tofile=f'{filename} (after)',
|
|
45
|
+
lineterm='',
|
|
46
|
+
)
|
|
47
|
+
diff_text = ''.join(diff)
|
|
48
|
+
if diff_text:
|
|
49
|
+
if not diff_text.endswith('\n'):
|
|
50
|
+
diff_text += '\n'
|
|
51
|
+
|
|
52
|
+
print(diff_text, file=sys.stderr, end='')
|
|
53
|
+
|
|
31
54
|
def fix_one_directory_or_one_file(self) -> int:
|
|
32
55
|
"""
|
|
33
56
|
Fix formatting in a single file or all Python files in a directory.
|
|
@@ -55,9 +78,10 @@ class BaseFixer:
|
|
|
55
78
|
|
|
56
79
|
|
|
57
80
|
def should_exclude_file(file_path: Path, exclude_pattern: str) -> bool:
|
|
58
|
-
"""
|
|
81
|
+
"""
|
|
82
|
+
Return True if ``file_path`` matches the provided exclude regex.
|
|
59
83
|
|
|
60
|
-
If
|
|
84
|
+
If ``exclude_pattern`` is empty or invalid, no files are excluded.
|
|
61
85
|
"""
|
|
62
86
|
if not exclude_pattern:
|
|
63
87
|
return False
|
|
@@ -12,6 +12,40 @@ ModuleClassOrFunc = (
|
|
|
12
12
|
ast.Module | ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef
|
|
13
13
|
)
|
|
14
14
|
|
|
15
|
+
NO_FORMAT_DOCSTRING_MARKER = 'no-format-docstring'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _determine_newline(text: str) -> str:
|
|
19
|
+
r"""
|
|
20
|
+
Return the dominant newline style detected in ``text``.
|
|
21
|
+
|
|
22
|
+
Defaults to ``\n`` when no Windows/Mac classic newlines are present.
|
|
23
|
+
"""
|
|
24
|
+
if '\r\n' in text:
|
|
25
|
+
return '\r\n'
|
|
26
|
+
|
|
27
|
+
if '\r' in text:
|
|
28
|
+
return '\r'
|
|
29
|
+
|
|
30
|
+
return '\n'
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _has_inline_no_format_comment(source_code: str, end_pos: int) -> bool:
|
|
34
|
+
"""
|
|
35
|
+
Return True if the closing quotes share a line with the sentinel comment.
|
|
36
|
+
"""
|
|
37
|
+
source_len = len(source_code)
|
|
38
|
+
line_end = source_code.find('\n', end_pos)
|
|
39
|
+
if line_end == -1:
|
|
40
|
+
line_end = source_len
|
|
41
|
+
|
|
42
|
+
same_line_segment = source_code[end_pos:line_end].lower()
|
|
43
|
+
hash_index = same_line_segment.find('#')
|
|
44
|
+
if hash_index == -1:
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
return NO_FORMAT_DOCSTRING_MARKER in same_line_segment[hash_index:]
|
|
48
|
+
|
|
15
49
|
|
|
16
50
|
def fix_src(
|
|
17
51
|
source_code: str,
|
|
@@ -20,7 +54,8 @@ def fix_src(
|
|
|
20
54
|
docstring_style: str = 'numpy',
|
|
21
55
|
fix_rst_backticks: bool = True,
|
|
22
56
|
) -> str:
|
|
23
|
-
"""
|
|
57
|
+
"""
|
|
58
|
+
Return code with only docstrings updated to wrapped content.
|
|
24
59
|
|
|
25
60
|
Parameters
|
|
26
61
|
----------
|
|
@@ -31,21 +66,20 @@ def fix_src(
|
|
|
31
66
|
docstring_style : str, default='numpy'
|
|
32
67
|
The docstring style to target ('numpy' or 'google').
|
|
33
68
|
fix_rst_backticks : bool, default=True
|
|
34
|
-
If True, automatically fix single backticks to double backticks per
|
|
35
|
-
|
|
69
|
+
If True, automatically fix single backticks to double backticks per rST
|
|
70
|
+
syntax.
|
|
36
71
|
|
|
37
72
|
Returns
|
|
38
73
|
-------
|
|
39
74
|
str
|
|
40
|
-
The updated source code. Only docstring literals are changed; all
|
|
41
|
-
|
|
75
|
+
The updated source code. Only docstring literals are changed; all other
|
|
76
|
+
formatting is preserved.
|
|
42
77
|
|
|
43
78
|
Notes
|
|
44
79
|
-----
|
|
45
|
-
This function avoids ``ast.unparse`` and instead replaces docstring
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
80
|
+
This function avoids ``ast.unparse`` and instead replaces docstring literal
|
|
81
|
+
spans directly in the original text to preserve non-docstring formatting
|
|
82
|
+
and comments.
|
|
49
83
|
"""
|
|
50
84
|
tree: ast.Module = ast.parse(source_code)
|
|
51
85
|
line_starts: list[int] = calc_line_starts(source_code)
|
|
@@ -93,7 +127,8 @@ def fix_src(
|
|
|
93
127
|
|
|
94
128
|
|
|
95
129
|
def calc_line_starts(source_code: str) -> list[int]:
|
|
96
|
-
"""
|
|
130
|
+
"""
|
|
131
|
+
Return starting offsets for each line in the source string.
|
|
97
132
|
|
|
98
133
|
Parameters
|
|
99
134
|
----------
|
|
@@ -104,7 +139,6 @@ def calc_line_starts(source_code: str) -> list[int]:
|
|
|
104
139
|
-------
|
|
105
140
|
list[int]
|
|
106
141
|
A list of absolute indices for the start of each line.
|
|
107
|
-
|
|
108
142
|
"""
|
|
109
143
|
starts: list[int] = [0]
|
|
110
144
|
for i, ch in enumerate(source_code):
|
|
@@ -122,7 +156,8 @@ def build_replacement_docstring(
|
|
|
122
156
|
docstring_style: str = 'numpy',
|
|
123
157
|
fix_rst_backticks: bool = True,
|
|
124
158
|
) -> tuple[int, int, str] | None:
|
|
125
|
-
"""
|
|
159
|
+
"""
|
|
160
|
+
Compute a single docstring replacement for the given node.
|
|
126
161
|
|
|
127
162
|
Parameters
|
|
128
163
|
----------
|
|
@@ -137,15 +172,14 @@ def build_replacement_docstring(
|
|
|
137
172
|
docstring_style : str, default='numpy'
|
|
138
173
|
The docstring style to target ('numpy' or 'google').
|
|
139
174
|
fix_rst_backticks : bool, default=True
|
|
140
|
-
If True, automatically fix single backticks to double backticks per
|
|
141
|
-
|
|
175
|
+
If True, automatically fix single backticks to double backticks per rST
|
|
176
|
+
syntax.
|
|
142
177
|
|
|
143
178
|
Returns
|
|
144
179
|
-------
|
|
145
180
|
tuple[int, int, str] or None
|
|
146
|
-
A tuple ``(start, end, new_literal)`` indicating the replacement
|
|
147
|
-
|
|
148
|
-
|
|
181
|
+
A tuple ``(start, end, new_literal)`` indicating the replacement range
|
|
182
|
+
and text, or ``None`` if no change is needed.
|
|
149
183
|
"""
|
|
150
184
|
docstring_obj: ast.Expr | None = find_docstring(node)
|
|
151
185
|
if docstring_obj is None:
|
|
@@ -159,6 +193,9 @@ def build_replacement_docstring(
|
|
|
159
193
|
end: int = calc_abs_pos(line_starts, val.end_lineno, val.end_col_offset) # type: ignore[arg-type] # noqa: LN002
|
|
160
194
|
original_literal = source_code[start:end]
|
|
161
195
|
|
|
196
|
+
if _has_inline_no_format_comment(source_code, end):
|
|
197
|
+
return None
|
|
198
|
+
|
|
162
199
|
doc: str | None = ast.get_docstring(node, clean=False)
|
|
163
200
|
if doc is None:
|
|
164
201
|
return None
|
|
@@ -200,7 +237,8 @@ def build_replacement_docstring(
|
|
|
200
237
|
|
|
201
238
|
|
|
202
239
|
def find_docstring(node: ModuleClassOrFunc) -> ast.Expr | None:
|
|
203
|
-
"""
|
|
240
|
+
"""
|
|
241
|
+
Return the first statement if it is a string-literal docstring.
|
|
204
242
|
|
|
205
243
|
Parameters
|
|
206
244
|
----------
|
|
@@ -213,7 +251,6 @@ def find_docstring(node: ModuleClassOrFunc) -> ast.Expr | None:
|
|
|
213
251
|
ast.Expr or None
|
|
214
252
|
The ``ast.Expr`` node that holds the docstring literal, if present;
|
|
215
253
|
otherwise ``None``.
|
|
216
|
-
|
|
217
254
|
"""
|
|
218
255
|
body: list[ast.stmt] | None = getattr(node, 'body', None)
|
|
219
256
|
if not body:
|
|
@@ -231,7 +268,8 @@ def find_docstring(node: ModuleClassOrFunc) -> ast.Expr | None:
|
|
|
231
268
|
|
|
232
269
|
|
|
233
270
|
def calc_abs_pos(line_starts: list[int], lineno: int, col: int) -> int:
|
|
234
|
-
"""
|
|
271
|
+
"""
|
|
272
|
+
Convert a (lineno, col) pair to an absolute index.
|
|
235
273
|
|
|
236
274
|
Parameters
|
|
237
275
|
----------
|
|
@@ -246,19 +284,19 @@ def calc_abs_pos(line_starts: list[int], lineno: int, col: int) -> int:
|
|
|
246
284
|
-------
|
|
247
285
|
int
|
|
248
286
|
The absolute character index into the source string.
|
|
249
|
-
|
|
250
287
|
"""
|
|
251
288
|
return line_starts[lineno - 1] + col
|
|
252
289
|
|
|
253
290
|
|
|
254
291
|
def rebuild_literal(original_literal: str, content: str) -> str | None:
|
|
255
|
-
"""
|
|
292
|
+
"""
|
|
293
|
+
Rebuild a string literal preserving prefix and quote style.
|
|
256
294
|
|
|
257
295
|
Parameters
|
|
258
296
|
----------
|
|
259
297
|
original_literal : str
|
|
260
|
-
The exact text of the original string literal including any prefix
|
|
261
|
-
|
|
298
|
+
The exact text of the original string literal including any prefix and
|
|
299
|
+
surrounding quotes.
|
|
262
300
|
content : str
|
|
263
301
|
The new inner content (without surrounding quotes).
|
|
264
302
|
|
|
@@ -267,7 +305,6 @@ def rebuild_literal(original_literal: str, content: str) -> str | None:
|
|
|
267
305
|
str or None
|
|
268
306
|
A new literal string with the same prefix and quotes and the new
|
|
269
307
|
content. Returns ``None`` if the original cannot be parsed.
|
|
270
|
-
|
|
271
308
|
"""
|
|
272
309
|
i = 0
|
|
273
310
|
n = len(original_literal)
|
|
@@ -286,6 +323,11 @@ def rebuild_literal(original_literal: str, content: str) -> str | None:
|
|
|
286
323
|
else:
|
|
287
324
|
return None
|
|
288
325
|
|
|
326
|
+
newline: str = _determine_newline(original_literal)
|
|
327
|
+
if newline != '\n':
|
|
328
|
+
normalized_content = content.replace('\r\n', '\n').replace('\r', '\n')
|
|
329
|
+
content = newline.join(normalized_content.split('\n'))
|
|
330
|
+
|
|
289
331
|
return f'{prefix}{delim}{content}{delim}'
|
|
290
332
|
|
|
291
333
|
|
|
@@ -296,7 +338,8 @@ def wrap_docstring(
|
|
|
296
338
|
leading_indent: int = 0,
|
|
297
339
|
fix_rst_backticks: bool = True,
|
|
298
340
|
) -> str:
|
|
299
|
-
"""
|
|
341
|
+
"""
|
|
342
|
+
Wrap a docstring to the given line length (stub).
|
|
300
343
|
|
|
301
344
|
Parameters
|
|
302
345
|
----------
|
|
@@ -309,8 +352,8 @@ def wrap_docstring(
|
|
|
309
352
|
leading_indent : int, default=0
|
|
310
353
|
The number of indentation spaces of this docstring.
|
|
311
354
|
fix_rst_backticks : bool, default=True
|
|
312
|
-
If True, automatically fix single backticks to double backticks per
|
|
313
|
-
|
|
355
|
+
If True, automatically fix single backticks to double backticks per rST
|
|
356
|
+
syntax.
|
|
314
357
|
|
|
315
358
|
Returns
|
|
316
359
|
-------
|
|
@@ -322,7 +365,6 @@ def wrap_docstring(
|
|
|
322
365
|
This function dispatches to style-specific implementations:
|
|
323
366
|
- 'numpy' -> wrap_docstring_numpy
|
|
324
367
|
- 'google' -> wrap_docstring_google
|
|
325
|
-
|
|
326
368
|
"""
|
|
327
369
|
style = (docstring_style or '').strip().lower()
|
|
328
370
|
if style == 'google':
|