format-docstring 0.2.0__py3-none-any.whl → 0.2.2__py3-none-any.whl

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.
@@ -15,7 +15,7 @@ class BaseFixer:
15
15
  ----------
16
16
  path : str
17
17
  Target file or directory to process.
18
- exclude_pattern : str, default='\.git|\.tox|\.pytest_cache'
18
+ exclude_pattern : str, default=r'\.git|\.tox|\.pytest_cache'
19
19
  Regular expression describing paths to skip.
20
20
  verbose : str, default='default'
21
21
  Verbosity mode; ``'diff'`` prints unified diffs for rewritten files.
@@ -221,6 +221,53 @@ def _collect_param_metadata(
221
221
  return metadata
222
222
 
223
223
 
224
+ def _collect_class_metadata(
225
+ node: ast.ClassDef,
226
+ source_code: str,
227
+ ) -> tuple[ParameterMetadata, ParameterMetadata]:
228
+ """
229
+ Build metadata for class docstrings using ``__init__`` and class attrs.
230
+ """
231
+ init_metadata: ParameterMetadata = {}
232
+ attribute_metadata: ParameterMetadata = {}
233
+
234
+ init_method: ast.FunctionDef | None = None
235
+ for stmt in node.body:
236
+ if isinstance(stmt, ast.FunctionDef) and stmt.name == '__init__':
237
+ init_method = stmt
238
+ break
239
+
240
+ if init_method is not None:
241
+ init_metadata = _collect_param_metadata(init_method, source_code)
242
+ # ``self``/``cls`` rarely appear in docstrings; drop to avoid noise.
243
+ init_metadata.pop('self', None)
244
+ init_metadata.pop('cls', None)
245
+
246
+ for stmt in node.body:
247
+ if isinstance(stmt, ast.AnnAssign):
248
+ target = stmt.target
249
+ if not isinstance(target, ast.Name):
250
+ continue
251
+
252
+ annotation = _render_signature_piece(stmt.annotation, source_code)
253
+ default = _render_signature_piece(stmt.value, source_code)
254
+ attribute_metadata[target.id] = (annotation, default)
255
+ continue
256
+
257
+ if isinstance(stmt, ast.Assign):
258
+ if len(stmt.targets) != 1:
259
+ continue
260
+
261
+ assign_target = stmt.targets[0]
262
+ if not isinstance(assign_target, ast.Name):
263
+ continue
264
+
265
+ # Record that this attribute explicitly has no annotation/default.
266
+ attribute_metadata[assign_target.id] = ('', None)
267
+
268
+ return init_metadata, attribute_metadata
269
+
270
+
224
271
  def fix_src(
225
272
  source_code: str,
226
273
  *,
@@ -255,7 +302,7 @@ def fix_src(
255
302
  spans directly in the original text to preserve non-docstring formatting
256
303
  and comments.
257
304
  """
258
- tree: ast.Module = ast.parse(source_code)
305
+ tree: ast.Module = ast.parse(source_code, type_comments=True)
259
306
  line_starts: list[int] = calc_line_starts(source_code)
260
307
 
261
308
  replacements: list[tuple[int, int, str]] = []
@@ -387,10 +434,20 @@ def build_replacement_docstring(
387
434
  )
388
435
 
389
436
  param_metadata: ParameterMetadata | None = None
437
+ attribute_metadata: ParameterMetadata | None = None
390
438
  return_annotation: str | None = None
391
439
  if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
392
440
  param_metadata = _collect_param_metadata(node, source_code)
393
441
  return_annotation = _render_signature_piece(node.returns, source_code)
442
+ elif isinstance(node, ast.ClassDef):
443
+ init_metadata, class_attr_metadata = _collect_class_metadata(
444
+ node, source_code
445
+ )
446
+ if init_metadata:
447
+ param_metadata = init_metadata
448
+
449
+ if class_attr_metadata:
450
+ attribute_metadata = class_attr_metadata
394
451
 
395
452
  wrapped: str = wrap_docstring(
396
453
  doc,
@@ -400,6 +457,7 @@ def build_replacement_docstring(
400
457
  fix_rst_backticks=fix_rst_backticks,
401
458
  function_param_metadata=param_metadata,
402
459
  function_return_annotation=return_annotation,
460
+ class_attribute_metadata=attribute_metadata,
403
461
  )
404
462
 
405
463
  new_literal: str | None = rebuild_literal(original_literal, wrapped)
@@ -521,6 +579,7 @@ def wrap_docstring(
521
579
  fix_rst_backticks: bool = True,
522
580
  function_param_metadata: ParameterMetadata | None = None,
523
581
  function_return_annotation: str | None = None,
582
+ class_attribute_metadata: ParameterMetadata | None = None,
524
583
  ) -> str:
525
584
  """
526
585
  Wrap a docstring to the given line length (stub).
@@ -544,6 +603,9 @@ def wrap_docstring(
544
603
  function_return_annotation : str | None, default=None
545
604
  The function's return annotation text (normalized), used to keep
546
605
  ``Returns``/``Yields`` signature lines synchronized.
606
+ class_attribute_metadata : ParameterMetadata | None, default=None
607
+ Attribute metadata for class docstrings (names mapped to annotations
608
+ and default values) collected from class-level assignments.
547
609
 
548
610
  Returns
549
611
  -------
@@ -565,6 +627,7 @@ def wrap_docstring(
565
627
  fix_rst_backticks=fix_rst_backticks,
566
628
  parameter_metadata=function_param_metadata,
567
629
  return_annotation=function_return_annotation,
630
+ attribute_metadata=class_attribute_metadata,
568
631
  )
569
632
  # Default to NumPy-style for unknown/unspecified styles to be permissive.
570
633
  return wrap_docstring_numpy(
@@ -573,5 +636,6 @@ def wrap_docstring(
573
636
  leading_indent=leading_indent,
574
637
  fix_rst_backticks=fix_rst_backticks,
575
638
  parameter_metadata=function_param_metadata,
639
+ attribute_metadata=class_attribute_metadata,
576
640
  return_annotation=function_return_annotation,
577
641
  )
@@ -8,6 +8,7 @@ def wrap_docstring_google(
8
8
  fix_rst_backticks: bool = True,
9
9
  parameter_metadata: ParameterMetadata | None = None,
10
10
  return_annotation: str | None = None,
11
+ attribute_metadata: ParameterMetadata | None = None,
11
12
  ) -> str:
12
13
  """A placeholder for now.""" # noqa: D401
13
14
  return ''
@@ -19,6 +19,7 @@ def wrap_docstring_numpy(
19
19
  leading_indent: int | None = None,
20
20
  fix_rst_backticks: bool = False,
21
21
  parameter_metadata: ParameterMetadata | None = None,
22
+ attribute_metadata: ParameterMetadata | None = None,
22
23
  return_annotation: str | None = None,
23
24
  ) -> str:
24
25
  """
@@ -66,12 +67,15 @@ def wrap_docstring_numpy(
66
67
  'other parameters:',
67
68
  'other parameter', # tolerate typo
68
69
  'other parameter:',
70
+ }
71
+ SECTION_ATTRIBUTES = {
69
72
  'attributes',
70
73
  'attributes:',
71
74
  'attribute', # tolerate typo
72
75
  'attribute:',
73
76
  }
74
- SECTION_RETURNS = {
77
+ SECTION_SIGNABLE = SECTION_PARAMS | SECTION_ATTRIBUTES
78
+ SECTION_RETURNS_YIELDS = {
75
79
  'returns',
76
80
  'returns:',
77
81
  'return', # tolerate typo
@@ -80,6 +84,8 @@ def wrap_docstring_numpy(
80
84
  'yields:',
81
85
  'yield', # tolerate typo
82
86
  'yield:',
87
+ }
88
+ SECTION_RAISES = {
83
89
  'raises',
84
90
  'raises:',
85
91
  'raise', # tolerate typo
@@ -154,7 +160,11 @@ def wrap_docstring_numpy(
154
160
 
155
161
  # Parameters-like sections
156
162
  section_lower_case: str = current_section.lower()
157
- if section_lower_case in SECTION_PARAMS:
163
+ if section_lower_case in SECTION_SIGNABLE:
164
+ metadata_for_section = parameter_metadata
165
+ if section_lower_case in SECTION_ATTRIBUTES:
166
+ metadata_for_section = attribute_metadata or parameter_metadata
167
+
158
168
  if line.strip() == '':
159
169
  temp_out.append(line)
160
170
  i += 1
@@ -170,7 +180,7 @@ def wrap_docstring_numpy(
170
180
  fixed_line = _fix_colon_spacing(line)
171
181
  fixed_line = _standardize_default_value(fixed_line)
172
182
  fixed_line = _rewrite_parameter_signature(
173
- fixed_line, parameter_metadata
183
+ fixed_line, metadata_for_section
174
184
  )
175
185
  fixed_line = _standardize_default_value(fixed_line)
176
186
  temp_out.append(fixed_line)
@@ -183,7 +193,7 @@ def wrap_docstring_numpy(
183
193
  continue
184
194
 
185
195
  # Returns/Yields sections
186
- if section_lower_case in SECTION_RETURNS:
196
+ if section_lower_case in SECTION_RETURNS_YIELDS:
187
197
  if line.strip() == '':
188
198
  temp_out.append(line)
189
199
  i += 1
@@ -234,6 +244,19 @@ def wrap_docstring_numpy(
234
244
  i += 1
235
245
  continue
236
246
 
247
+ # Raises section
248
+ if section_lower_case in SECTION_RAISES:
249
+ if line.strip() == '':
250
+ temp_out.append(line)
251
+ i += 1
252
+ continue
253
+
254
+ # Treat top-level lines as signatures
255
+ if indent_length <= leading_indent: # type: ignore[operator]
256
+ temp_out.append(line)
257
+ i += 1
258
+ continue
259
+
237
260
  # Examples or any other section
238
261
  collect_to_temp_output(temp_out, line)
239
262
  i += 1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: format-docstring
3
- Version: 0.2.0
3
+ Version: 0.2.2
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>
@@ -0,0 +1,15 @@
1
+ format_docstring/__init__.py,sha256=3bPK0B7mVsgqXVtcJYGKBO8JAM5gYWmdQPaRrI2tpsI,146
2
+ format_docstring/base_fixer.py,sha256=PtTn-bMqlaQAJsNCzMbAxBC5_mjTl1pmC16iLZf_v2w,3118
3
+ format_docstring/config.py,sha256=2VvOL1AaYM-ODCZf-U3R3ptePR_DMZXnOVItbpp6yns,5528
4
+ format_docstring/docstring_rewriter.py,sha256=17tjHWqgptWeJZlFrJN7jqV3326IUgjEB4F3RKHmb-E,20848
5
+ format_docstring/line_wrap_google.py,sha256=PiWeEIJnbyEoxt3nxpzvOyxOMmBaQvcDKEPLbuyB4FM,464
6
+ format_docstring/line_wrap_numpy.py,sha256=8SBSSU47FR1enKxLSBX2tyX09f_cdDfg7FABKR49JeA,28932
7
+ format_docstring/line_wrap_utils.py,sha256=cXHTFMLefUFVZgUzC-UU6FrNCzh4kAOH7E_AMwm-bz0,27988
8
+ format_docstring/main_jupyter.py,sha256=rkNjk0XwCukxCrWsr8PNDCywxYH8Jpj5XCxql_3GPOI,6309
9
+ format_docstring/main_py.py,sha256=aYVOEtH15UpXugqkBdiYFzyR9QCnP7FXZ6PFt7fwvdk,4562
10
+ format_docstring-0.2.2.dist-info/licenses/LICENSE,sha256=UNm_-hqU-1dAB3bLytP6wvGtUeitoJde2xzb5wgVYn0,1061
11
+ format_docstring-0.2.2.dist-info/METADATA,sha256=6ff2qlTJdvJnMRWddv9fJimTyjKljCFqqoay-tT7bNI,15454
12
+ format_docstring-0.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
+ format_docstring-0.2.2.dist-info/entry_points.txt,sha256=Re2iHTzfgKJgeXDga5Z4bGNPtWGmxupOdzTolwn52sk,129
14
+ format_docstring-0.2.2.dist-info/top_level.txt,sha256=NXPwfHm_1YwwGuetwtK3pJv0jXwXelqPTNCFpm5LQyE,17
15
+ format_docstring-0.2.2.dist-info/RECORD,,
@@ -1,15 +0,0 @@
1
- format_docstring/__init__.py,sha256=3bPK0B7mVsgqXVtcJYGKBO8JAM5gYWmdQPaRrI2tpsI,146
2
- format_docstring/base_fixer.py,sha256=O-8FYKRvKg1BQJFcT23Ju85o-m3341PFulbeBM-UJss,3117
3
- format_docstring/config.py,sha256=2VvOL1AaYM-ODCZf-U3R3ptePR_DMZXnOVItbpp6yns,5528
4
- format_docstring/docstring_rewriter.py,sha256=-WFPBCfOw08Xd1utrTG8gVPi0P-Cn5ookCzK-nSUj7A,18407
5
- format_docstring/line_wrap_google.py,sha256=ECfXjKMOADXClqXStvtdvvYi_C3LcUZ5rrigNvyOxUo,403
6
- format_docstring/line_wrap_numpy.py,sha256=8J5_AWY2bPuilyk6OEKPqgbT0Zv2p70SG2wYXJ2gABs,28133
7
- format_docstring/line_wrap_utils.py,sha256=cXHTFMLefUFVZgUzC-UU6FrNCzh4kAOH7E_AMwm-bz0,27988
8
- format_docstring/main_jupyter.py,sha256=rkNjk0XwCukxCrWsr8PNDCywxYH8Jpj5XCxql_3GPOI,6309
9
- format_docstring/main_py.py,sha256=aYVOEtH15UpXugqkBdiYFzyR9QCnP7FXZ6PFt7fwvdk,4562
10
- format_docstring-0.2.0.dist-info/licenses/LICENSE,sha256=UNm_-hqU-1dAB3bLytP6wvGtUeitoJde2xzb5wgVYn0,1061
11
- format_docstring-0.2.0.dist-info/METADATA,sha256=p5wNL7yKCipSr-WhZ2yZFQxMk4sxeJsjcHEQ4EuP8F4,15454
12
- format_docstring-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
- format_docstring-0.2.0.dist-info/entry_points.txt,sha256=Re2iHTzfgKJgeXDga5Z4bGNPtWGmxupOdzTolwn52sk,129
14
- format_docstring-0.2.0.dist-info/top_level.txt,sha256=NXPwfHm_1YwwGuetwtK3pJv0jXwXelqPTNCFpm5LQyE,17
15
- format_docstring-0.2.0.dist-info/RECORD,,