google-docstring-parser 0.0.9__tar.gz → 0.0.11__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.
- {google_docstring_parser-0.0.9 → google_docstring_parser-0.0.11}/PKG-INFO +8 -1
- {google_docstring_parser-0.0.9 → google_docstring_parser-0.0.11}/README.md +7 -0
- {google_docstring_parser-0.0.9 → google_docstring_parser-0.0.11}/google_docstring_parser/google_docstring_parser.py +12 -12
- {google_docstring_parser-0.0.9 → google_docstring_parser-0.0.11}/google_docstring_parser/type_validation.py +11 -11
- {google_docstring_parser-0.0.9 → google_docstring_parser-0.0.11}/google_docstring_parser.egg-info/PKG-INFO +8 -1
- {google_docstring_parser-0.0.9 → google_docstring_parser-0.0.11}/pyproject.toml +3 -1
- {google_docstring_parser-0.0.9 → google_docstring_parser-0.0.11}/tools/check_docstrings.py +381 -46
- {google_docstring_parser-0.0.9 → google_docstring_parser-0.0.11}/LICENSE +0 -0
- {google_docstring_parser-0.0.9 → google_docstring_parser-0.0.11}/google_docstring_parser/__init__.py +0 -0
- {google_docstring_parser-0.0.9 → google_docstring_parser-0.0.11}/google_docstring_parser/py.typed +0 -0
- {google_docstring_parser-0.0.9 → google_docstring_parser-0.0.11}/google_docstring_parser.egg-info/SOURCES.txt +0 -0
- {google_docstring_parser-0.0.9 → google_docstring_parser-0.0.11}/google_docstring_parser.egg-info/dependency_links.txt +0 -0
- {google_docstring_parser-0.0.9 → google_docstring_parser-0.0.11}/google_docstring_parser.egg-info/requires.txt +0 -0
- {google_docstring_parser-0.0.9 → google_docstring_parser-0.0.11}/google_docstring_parser.egg-info/top_level.txt +0 -0
- {google_docstring_parser-0.0.9 → google_docstring_parser-0.0.11}/setup.cfg +0 -0
- {google_docstring_parser-0.0.9 → google_docstring_parser-0.0.11}/tools/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: google-docstring-parser
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.11
|
|
4
4
|
Summary: A lightweight, efficient parser for Google-style Python docstrings that converts them into structured dictionaries.
|
|
5
5
|
Author: Vladimir Iglovikov
|
|
6
6
|
Maintainer: Vladimir Iglovikov
|
|
@@ -153,6 +153,10 @@ References:
|
|
|
153
153
|
|
|
154
154
|
Each reference is parsed into a dictionary with `description` and `source` keys. URLs in the source are properly handled, ensuring colons in URLs are not confused with the separator colon.
|
|
155
155
|
|
|
156
|
+
## Short Description (Meta Description)
|
|
157
|
+
|
|
158
|
+
The checker extracts the **short description** as the first paragraph (up to the first blank line). Multi-line first paragraphs are joined with spaces. This is useful for meta descriptions on documentation sites. SEO best practice: 120-160 characters. Use `min_short_description_length` and `max_short_description_length` to enforce bounds (0 to disable).
|
|
159
|
+
|
|
156
160
|
## Pre-commit Hook
|
|
157
161
|
|
|
158
162
|
This package includes a pre-commit hook that checks if Google-style docstrings in your codebase can be parsed correctly.
|
|
@@ -180,6 +184,9 @@ Add a `[tool.docstring_checker]` section to your pyproject.toml:
|
|
|
180
184
|
paths = ["src", "tests"] # Directories or files to scan
|
|
181
185
|
require_param_types = true # Require parameter types in docstrings
|
|
182
186
|
check_references = true # Check references for proper format
|
|
187
|
+
check_type_consistency = true # Compare docstring types with annotations
|
|
183
188
|
exclude_files = ["conftest.py", "__init__.py"] # Files to exclude from checks
|
|
189
|
+
min_short_description_length = 50 # Minimum short description length; 0 to disable
|
|
190
|
+
max_short_description_length = 160 # Maximum short description length; 0 to disable (SEO: 120-160)
|
|
184
191
|
verbose = false # Enable verbose output
|
|
185
192
|
```
|
|
@@ -119,6 +119,10 @@ References:
|
|
|
119
119
|
|
|
120
120
|
Each reference is parsed into a dictionary with `description` and `source` keys. URLs in the source are properly handled, ensuring colons in URLs are not confused with the separator colon.
|
|
121
121
|
|
|
122
|
+
## Short Description (Meta Description)
|
|
123
|
+
|
|
124
|
+
The checker extracts the **short description** as the first paragraph (up to the first blank line). Multi-line first paragraphs are joined with spaces. This is useful for meta descriptions on documentation sites. SEO best practice: 120-160 characters. Use `min_short_description_length` and `max_short_description_length` to enforce bounds (0 to disable).
|
|
125
|
+
|
|
122
126
|
## Pre-commit Hook
|
|
123
127
|
|
|
124
128
|
This package includes a pre-commit hook that checks if Google-style docstrings in your codebase can be parsed correctly.
|
|
@@ -146,6 +150,9 @@ Add a `[tool.docstring_checker]` section to your pyproject.toml:
|
|
|
146
150
|
paths = ["src", "tests"] # Directories or files to scan
|
|
147
151
|
require_param_types = true # Require parameter types in docstrings
|
|
148
152
|
check_references = true # Check references for proper format
|
|
153
|
+
check_type_consistency = true # Compare docstring types with annotations
|
|
149
154
|
exclude_files = ["conftest.py", "__init__.py"] # Files to exclude from checks
|
|
155
|
+
min_short_description_length = 50 # Minimum short description length; 0 to disable
|
|
156
|
+
max_short_description_length = 160 # Maximum short description length; 0 to disable (SEO: 120-160)
|
|
150
157
|
verbose = false # Enable verbose output
|
|
151
158
|
```
|
|
@@ -40,7 +40,7 @@ __all__ = [
|
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
class ReferenceFormatError(ValueError):
|
|
43
|
-
"""Error raised when a reference format is invalid.
|
|
43
|
+
"""Error raised when a reference format is invalid or malformed.
|
|
44
44
|
|
|
45
45
|
Args:
|
|
46
46
|
code (str): Error code identifying the specific format issue
|
|
@@ -88,7 +88,7 @@ class EmptyDescriptionError(ReferenceFormatError):
|
|
|
88
88
|
|
|
89
89
|
|
|
90
90
|
def _extract_sections(docstring: str) -> dict[str, str]:
|
|
91
|
-
"""Extract sections from a docstring.
|
|
91
|
+
"""Extract named sections from a Google-style docstring.
|
|
92
92
|
|
|
93
93
|
Args:
|
|
94
94
|
docstring (str): The docstring to extract sections from
|
|
@@ -170,7 +170,7 @@ def _find_separator_colon(content: str) -> int:
|
|
|
170
170
|
|
|
171
171
|
|
|
172
172
|
def _parse_reference_line(line: str, *, is_single: bool = False) -> dict[str, str]:
|
|
173
|
-
"""Parse a single reference line.
|
|
173
|
+
"""Parse a single reference line into description and source.
|
|
174
174
|
|
|
175
175
|
Args:
|
|
176
176
|
line (str): The line to parse
|
|
@@ -252,7 +252,7 @@ def _identify_main_reference_lines(lines: list[str]) -> list[str]:
|
|
|
252
252
|
|
|
253
253
|
|
|
254
254
|
def _process_single_reference(main_line: str, all_lines: list[str]) -> dict[str, str]:
|
|
255
|
-
"""Process a single reference entry.
|
|
255
|
+
"""Process a single reference entry from the References section.
|
|
256
256
|
|
|
257
257
|
Args:
|
|
258
258
|
main_line (str): The main reference line
|
|
@@ -285,7 +285,7 @@ def _process_single_reference(main_line: str, all_lines: list[str]) -> dict[str,
|
|
|
285
285
|
|
|
286
286
|
|
|
287
287
|
def _process_multiple_references(lines: list[str]) -> list[dict[str, str]]:
|
|
288
|
-
"""Process multiple reference entries.
|
|
288
|
+
"""Process multiple reference entries from the References section.
|
|
289
289
|
|
|
290
290
|
Args:
|
|
291
291
|
lines (list[str]): Lines containing multiple references
|
|
@@ -338,7 +338,7 @@ def _process_multiple_references(lines: list[str]) -> list[dict[str, str]]:
|
|
|
338
338
|
|
|
339
339
|
|
|
340
340
|
def _parse_references(reference_content: str) -> list[dict[str, str]]:
|
|
341
|
-
"""Parse references section content.
|
|
341
|
+
"""Parse references section content into structured reference entries.
|
|
342
342
|
|
|
343
343
|
Args:
|
|
344
344
|
reference_content (str): Content of the references section
|
|
@@ -373,7 +373,7 @@ def _parse_references(reference_content: str) -> list[dict[str, str]]:
|
|
|
373
373
|
|
|
374
374
|
|
|
375
375
|
def _validate_type_with_error_handling(type_str: str, result: dict[str, Any], collect_errors: bool) -> None:
|
|
376
|
-
"""Validate a type annotation and handle any errors.
|
|
376
|
+
"""Validate a type annotation and handle any validation errors.
|
|
377
377
|
|
|
378
378
|
This function validates type annotations and handles errors differently based on the collect_errors flag:
|
|
379
379
|
- When collect_errors is True: Errors are added to result["errors"] list instead of being raised
|
|
@@ -408,7 +408,7 @@ def _process_args_with_validation(
|
|
|
408
408
|
validate_types: bool,
|
|
409
409
|
collect_errors: bool,
|
|
410
410
|
) -> None:
|
|
411
|
-
"""Process the Args section with type validation.
|
|
411
|
+
"""Process the Args section with type validation and error collection.
|
|
412
412
|
|
|
413
413
|
Args:
|
|
414
414
|
sections (dict[str, str]): The sections dictionary
|
|
@@ -439,7 +439,7 @@ def _process_args_with_validation(
|
|
|
439
439
|
|
|
440
440
|
|
|
441
441
|
def _parse_returns_section(sections: dict[str, str], *, validate_types: bool) -> dict[str, str] | str:
|
|
442
|
-
"""Process the Returns section of a docstring.
|
|
442
|
+
"""Process the Returns section of a docstring into type and description.
|
|
443
443
|
|
|
444
444
|
Args:
|
|
445
445
|
sections (dict[str, str]): The sections dictionary
|
|
@@ -482,7 +482,7 @@ def _process_returns_with_validation(
|
|
|
482
482
|
validate_types: bool,
|
|
483
483
|
collect_errors: bool,
|
|
484
484
|
) -> None:
|
|
485
|
-
"""Process the Returns section with type validation.
|
|
485
|
+
"""Process the Returns section with type validation and error handling.
|
|
486
486
|
|
|
487
487
|
Args:
|
|
488
488
|
sections (dict[str, str]): The sections dictionary
|
|
@@ -506,7 +506,7 @@ def _process_returns_with_validation(
|
|
|
506
506
|
|
|
507
507
|
|
|
508
508
|
def _process_references_section(sections: dict[str, str], result: dict[str, Any]) -> None:
|
|
509
|
-
"""Process the References section.
|
|
509
|
+
"""Process the References section into structured reference entries.
|
|
510
510
|
|
|
511
511
|
Args:
|
|
512
512
|
sections (dict[str, str]): The sections dictionary
|
|
@@ -527,7 +527,7 @@ def parse_google_docstring(
|
|
|
527
527
|
validate_types: bool = True,
|
|
528
528
|
collect_errors: bool = True,
|
|
529
529
|
) -> dict[str, Any]:
|
|
530
|
-
"""Parse a Google-style docstring.
|
|
530
|
+
"""Parse a Google-style docstring into a structured dictionary.
|
|
531
531
|
|
|
532
532
|
Args:
|
|
533
533
|
docstring (str): The docstring to parse
|
|
@@ -89,7 +89,7 @@ NESTING_KEYWORD = "with"
|
|
|
89
89
|
|
|
90
90
|
|
|
91
91
|
class InvalidTypeAnnotationError(ValueError):
|
|
92
|
-
"""Error raised when a type annotation is invalid.
|
|
92
|
+
"""Error raised when a type annotation is invalid or malformed.
|
|
93
93
|
|
|
94
94
|
Args:
|
|
95
95
|
message (str): The error message.
|
|
@@ -100,7 +100,7 @@ class InvalidTypeAnnotationError(ValueError):
|
|
|
100
100
|
INVALID_NESTED_TYPE = "Invalid nested type: {}"
|
|
101
101
|
|
|
102
102
|
def __init__(self, message: str) -> None:
|
|
103
|
-
"""Initialize the error with a message.
|
|
103
|
+
"""Initialize the error instance with a descriptive message.
|
|
104
104
|
|
|
105
105
|
Args:
|
|
106
106
|
message (str): The error message.
|
|
@@ -125,7 +125,7 @@ class BracketValidationError(ValueError):
|
|
|
125
125
|
WRONG_BRACKET_TYPE = "Collection '{}' must use square brackets for type arguments, not '{}'"
|
|
126
126
|
|
|
127
127
|
def __init__(self, error_type: str) -> None:
|
|
128
|
-
"""Initialize with a specific
|
|
128
|
+
"""Initialize the error with a specific bracket validation type.
|
|
129
129
|
|
|
130
130
|
Args:
|
|
131
131
|
error_type (str): One of the predefined error types.
|
|
@@ -137,7 +137,7 @@ class BracketValidationError(ValueError):
|
|
|
137
137
|
|
|
138
138
|
|
|
139
139
|
def is_collection_type(type_name: str) -> bool:
|
|
140
|
-
"""Check if a type name is a known collection type.
|
|
140
|
+
"""Check if a type name is a known collection type (list, dict, etc).
|
|
141
141
|
|
|
142
142
|
Args:
|
|
143
143
|
type_name (str): The type name to check.
|
|
@@ -261,7 +261,7 @@ def _is_within_string_literal(text: str, position: int) -> bool:
|
|
|
261
261
|
|
|
262
262
|
|
|
263
263
|
def _looks_like_type_annotation(text: str) -> bool:
|
|
264
|
-
"""Check if text looks like a type annotation.
|
|
264
|
+
"""Check if text looks like a type annotation using heuristics.
|
|
265
265
|
|
|
266
266
|
Args:
|
|
267
267
|
text (str): The text to check
|
|
@@ -277,7 +277,7 @@ def _looks_like_type_annotation(text: str) -> bool:
|
|
|
277
277
|
|
|
278
278
|
|
|
279
279
|
def _process_string_literals(text: str) -> tuple[str, list[str]]:
|
|
280
|
-
"""Process string literals in text.
|
|
280
|
+
"""Process string literals in text by replacing them with placeholders.
|
|
281
281
|
|
|
282
282
|
Args:
|
|
283
283
|
text (str): The text to process
|
|
@@ -386,7 +386,7 @@ def _check_for_opening_bracket(
|
|
|
386
386
|
bracket_stack: list[str],
|
|
387
387
|
collection_stack: list[tuple[str, str]],
|
|
388
388
|
) -> None:
|
|
389
|
-
"""Check for opening bracket in type declaration.
|
|
389
|
+
"""Check for opening bracket in type declaration and update stacks.
|
|
390
390
|
|
|
391
391
|
Args:
|
|
392
392
|
tokens (list[str]): List of tokens
|
|
@@ -409,7 +409,7 @@ def _check_for_opening_bracket(
|
|
|
409
409
|
|
|
410
410
|
|
|
411
411
|
def _check_for_closing_bracket(token: str, bracket_stack: list[str], collection_stack: list[tuple[str, str]]) -> None:
|
|
412
|
-
"""Check for closing bracket in type declaration.
|
|
412
|
+
"""Check for closing bracket in type declaration and validate pairing.
|
|
413
413
|
|
|
414
414
|
Args:
|
|
415
415
|
token (str): Current token
|
|
@@ -440,7 +440,7 @@ def _check_for_closing_bracket(token: str, bracket_stack: list[str], collection_
|
|
|
440
440
|
|
|
441
441
|
|
|
442
442
|
def _check_for_bare_collection(tokens: list[str], i: int, token: str) -> None:
|
|
443
|
-
"""Check for bare collection type usage.
|
|
443
|
+
"""Check for bare collection type usage without type arguments.
|
|
444
444
|
|
|
445
445
|
Args:
|
|
446
446
|
tokens (list[str]): List of tokens
|
|
@@ -487,7 +487,7 @@ def _is_bare_collection_in_nested_type(token: str, tokens: list[str], i: int, br
|
|
|
487
487
|
|
|
488
488
|
|
|
489
489
|
def _check_tokens_for_collection_type_usage(tokens: list[str]) -> None:
|
|
490
|
-
"""Check tokens for proper collection type usage.
|
|
490
|
+
"""Check tokens for proper collection type usage and brackets.
|
|
491
491
|
|
|
492
492
|
Args:
|
|
493
493
|
tokens (list[str]): List of tokens to check
|
|
@@ -539,7 +539,7 @@ def _check_tokens_for_collection_type_usage(tokens: list[str]) -> None:
|
|
|
539
539
|
|
|
540
540
|
|
|
541
541
|
def _validate_type_declaration(declaration: str) -> None:
|
|
542
|
-
"""Validate a type declaration.
|
|
542
|
+
"""Validate a type declaration for syntax and collection usage.
|
|
543
543
|
|
|
544
544
|
Args:
|
|
545
545
|
declaration (str): The type declaration to validate
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: google-docstring-parser
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.11
|
|
4
4
|
Summary: A lightweight, efficient parser for Google-style Python docstrings that converts them into structured dictionaries.
|
|
5
5
|
Author: Vladimir Iglovikov
|
|
6
6
|
Maintainer: Vladimir Iglovikov
|
|
@@ -153,6 +153,10 @@ References:
|
|
|
153
153
|
|
|
154
154
|
Each reference is parsed into a dictionary with `description` and `source` keys. URLs in the source are properly handled, ensuring colons in URLs are not confused with the separator colon.
|
|
155
155
|
|
|
156
|
+
## Short Description (Meta Description)
|
|
157
|
+
|
|
158
|
+
The checker extracts the **short description** as the first paragraph (up to the first blank line). Multi-line first paragraphs are joined with spaces. This is useful for meta descriptions on documentation sites. SEO best practice: 120-160 characters. Use `min_short_description_length` and `max_short_description_length` to enforce bounds (0 to disable).
|
|
159
|
+
|
|
156
160
|
## Pre-commit Hook
|
|
157
161
|
|
|
158
162
|
This package includes a pre-commit hook that checks if Google-style docstrings in your codebase can be parsed correctly.
|
|
@@ -180,6 +184,9 @@ Add a `[tool.docstring_checker]` section to your pyproject.toml:
|
|
|
180
184
|
paths = ["src", "tests"] # Directories or files to scan
|
|
181
185
|
require_param_types = true # Require parameter types in docstrings
|
|
182
186
|
check_references = true # Check references for proper format
|
|
187
|
+
check_type_consistency = true # Compare docstring types with annotations
|
|
183
188
|
exclude_files = ["conftest.py", "__init__.py"] # Files to exclude from checks
|
|
189
|
+
min_short_description_length = 50 # Minimum short description length; 0 to disable
|
|
190
|
+
max_short_description_length = 160 # Maximum short description length; 0 to disable (SEO: 120-160)
|
|
184
191
|
verbose = false # Enable verbose output
|
|
185
192
|
```
|
|
@@ -5,7 +5,7 @@ requires = [ "setuptools>=45", "wheel" ]
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "google-docstring-parser"
|
|
8
|
-
version = "0.0.
|
|
8
|
+
version = "0.0.11"
|
|
9
9
|
|
|
10
10
|
description = "A lightweight, efficient parser for Google-style Python docstrings that converts them into structured dictionaries."
|
|
11
11
|
readme = "README.md"
|
|
@@ -119,6 +119,7 @@ lint.per-file-ignores = { "__init__.py" = [
|
|
|
119
119
|
"BLE001",
|
|
120
120
|
"FBT002",
|
|
121
121
|
"ANN201",
|
|
122
|
+
"PLR0913",
|
|
122
123
|
] }
|
|
123
124
|
|
|
124
125
|
lint.fixable = [ "ALL" ]
|
|
@@ -146,5 +147,6 @@ paths = [ "google_docstring_parser", "tools" ]
|
|
|
146
147
|
require_param_types = true
|
|
147
148
|
check_references = true
|
|
148
149
|
check_type_consistency = true
|
|
150
|
+
min_short_description_length = 50
|
|
149
151
|
exclude_files = [ "test_malformed_docstrings.py" ]
|
|
150
152
|
verbose = false
|
|
@@ -20,18 +20,24 @@ from google_docstring_parser.google_docstring_parser import (
|
|
|
20
20
|
parse_google_docstring,
|
|
21
21
|
)
|
|
22
22
|
|
|
23
|
+
# Preview length for short description error messages
|
|
24
|
+
SHORT_DESC_PREVIEW_LENGTH = 50
|
|
25
|
+
|
|
23
26
|
# Default configuration
|
|
24
27
|
DEFAULT_CONFIG = {
|
|
25
28
|
"paths": [], # Empty by default, so no directories are scanned unless explicitly specified
|
|
26
29
|
"require_param_types": False,
|
|
27
30
|
"check_references": True,
|
|
31
|
+
"check_type_consistency": False,
|
|
32
|
+
"min_short_description_length": 0,
|
|
33
|
+
"max_short_description_length": 0, # 160 recommended for SEO meta descriptions
|
|
28
34
|
"exclude_files": [],
|
|
29
35
|
"verbose": False,
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
|
|
33
39
|
class DocstringContext(NamedTuple):
|
|
34
|
-
"""Context for docstring processing.
|
|
40
|
+
"""Context for docstring processing, validation, and error reporting.
|
|
35
41
|
|
|
36
42
|
Args:
|
|
37
43
|
file_path (Path): Path to the file
|
|
@@ -40,6 +46,10 @@ class DocstringContext(NamedTuple):
|
|
|
40
46
|
verbose (bool): Whether to print verbose output
|
|
41
47
|
require_param_types (bool): Whether parameter types are required
|
|
42
48
|
check_references (bool): Whether to check references for errors
|
|
49
|
+
check_type_consistency (bool): Whether to compare docstring types with annotations
|
|
50
|
+
min_short_description_length (int): Minimum length for short description
|
|
51
|
+
max_short_description_length (int): Maximum length for short description (0 to disable)
|
|
52
|
+
node (ast.AST | None): AST node for the function or class
|
|
43
53
|
|
|
44
54
|
Returns:
|
|
45
55
|
DocstringContext: A named tuple containing docstring processing context
|
|
@@ -51,6 +61,35 @@ class DocstringContext(NamedTuple):
|
|
|
51
61
|
verbose: bool
|
|
52
62
|
require_param_types: bool = False
|
|
53
63
|
check_references: bool = True
|
|
64
|
+
check_type_consistency: bool = False
|
|
65
|
+
min_short_description_length: int = 0
|
|
66
|
+
max_short_description_length: int = 0
|
|
67
|
+
node: ast.AST | None = None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
_CONFIG_KEYS: dict[str, tuple[str, type]] = {
|
|
71
|
+
"paths": ("paths", list),
|
|
72
|
+
"require_param_types": ("require_param_types", bool),
|
|
73
|
+
"check_references": ("check_references", bool),
|
|
74
|
+
"check_type_consistency": ("check_type_consistency", bool),
|
|
75
|
+
"min_short_description_length": ("min_short_description_length", int),
|
|
76
|
+
"max_short_description_length": ("max_short_description_length", int),
|
|
77
|
+
"exclude_files": ("exclude_files", list),
|
|
78
|
+
"verbose": ("verbose", bool),
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _config_keys_match() -> bool:
|
|
83
|
+
"""Return True if DEFAULT_CONFIG and _CONFIG_KEYS have the same keys."""
|
|
84
|
+
return set(DEFAULT_CONFIG.keys()) == set(_CONFIG_KEYS.keys())
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _apply_tool_config(config: dict[str, Any], tool_config: dict[str, Any]) -> None:
|
|
88
|
+
"""Apply tool_config values to config. Modifies config in place."""
|
|
89
|
+
for key, (config_key, converter) in _CONFIG_KEYS.items():
|
|
90
|
+
if key in tool_config:
|
|
91
|
+
raw = tool_config[key]
|
|
92
|
+
config[config_key] = raw if converter is list else converter(raw)
|
|
54
93
|
|
|
55
94
|
|
|
56
95
|
def load_pyproject_config() -> dict[str, Any]:
|
|
@@ -60,8 +99,6 @@ def load_pyproject_config() -> dict[str, Any]:
|
|
|
60
99
|
dict[str, Any]: Dictionary with configuration values
|
|
61
100
|
"""
|
|
62
101
|
config = DEFAULT_CONFIG.copy()
|
|
63
|
-
|
|
64
|
-
# Look for pyproject.toml in the current directory
|
|
65
102
|
pyproject_path = Path("pyproject.toml")
|
|
66
103
|
if not pyproject_path.is_file():
|
|
67
104
|
return config
|
|
@@ -69,24 +106,10 @@ def load_pyproject_config() -> dict[str, Any]:
|
|
|
69
106
|
try:
|
|
70
107
|
with pyproject_path.open("rb") as f:
|
|
71
108
|
pyproject_data = tomli.load(f)
|
|
72
|
-
|
|
73
|
-
# Check if our tool is configured
|
|
74
109
|
tool_config = pyproject_data.get("tool", {}).get("docstring_checker", {})
|
|
75
110
|
if not tool_config:
|
|
76
111
|
return config
|
|
77
|
-
|
|
78
|
-
# Update config with values from pyproject.toml
|
|
79
|
-
if "paths" in tool_config:
|
|
80
|
-
config["paths"] = tool_config["paths"]
|
|
81
|
-
if "require_param_types" in tool_config:
|
|
82
|
-
config["require_param_types"] = bool(tool_config["require_param_types"])
|
|
83
|
-
if "check_references" in tool_config:
|
|
84
|
-
config["check_references"] = bool(tool_config["check_references"])
|
|
85
|
-
if "exclude_files" in tool_config:
|
|
86
|
-
config["exclude_files"] = tool_config["exclude_files"]
|
|
87
|
-
if "verbose" in tool_config:
|
|
88
|
-
config["verbose"] = bool(tool_config["verbose"])
|
|
89
|
-
|
|
112
|
+
_apply_tool_config(config, tool_config)
|
|
90
113
|
except Exception as e:
|
|
91
114
|
print(f"Warning: Failed to load configuration from pyproject.toml: {e}")
|
|
92
115
|
|
|
@@ -94,7 +117,7 @@ def load_pyproject_config() -> dict[str, Any]:
|
|
|
94
117
|
|
|
95
118
|
|
|
96
119
|
def get_docstrings(file_path: Path) -> list[tuple[str, int, str | None, ast.AST | None]]:
|
|
97
|
-
"""Extract docstrings from a Python file.
|
|
120
|
+
"""Extract docstrings from a Python file using AST parsing.
|
|
98
121
|
|
|
99
122
|
Args:
|
|
100
123
|
file_path (Path): Path to the Python file
|
|
@@ -128,7 +151,7 @@ def get_docstrings(file_path: Path) -> list[tuple[str, int, str | None, ast.AST
|
|
|
128
151
|
|
|
129
152
|
|
|
130
153
|
def check_param_types(docstring_dict: dict[str, Any], require_types: bool) -> list[str]:
|
|
131
|
-
"""Check if all parameters have types
|
|
154
|
+
"""Check if all parameters have types when types are required.
|
|
132
155
|
|
|
133
156
|
Args:
|
|
134
157
|
docstring_dict (dict[str, Any]): Parsed docstring dictionary
|
|
@@ -150,6 +173,111 @@ def check_param_types(docstring_dict: dict[str, Any], require_types: bool) -> li
|
|
|
150
173
|
return errors
|
|
151
174
|
|
|
152
175
|
|
|
176
|
+
def _normalize_type(type_str: str) -> str:
|
|
177
|
+
"""Normalize type string for comparison (quotes and whitespace only).
|
|
178
|
+
|
|
179
|
+
Python 3.10+ typing uses list, dict, tuple, X|Y - no List, Dict, Tuple, Union.
|
|
180
|
+
We do not normalize those; mismatches will be reported.
|
|
181
|
+
Internal whitespace differences (e.g., around commas, |, or brackets) are ignored.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
type_str (str): Type string to normalize
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
str: Normalized type string
|
|
188
|
+
"""
|
|
189
|
+
normalized = type_str.strip().strip("'\"")
|
|
190
|
+
return re.sub(r"\s+", "", normalized)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _annotation_to_str(annotation: ast.expr | None) -> str | None:
|
|
194
|
+
"""Extract the type string from an AST annotation node.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
annotation (ast.expr | None): AST annotation node
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
str | None: Type string or None if no annotation
|
|
201
|
+
"""
|
|
202
|
+
if annotation is None:
|
|
203
|
+
return None
|
|
204
|
+
if isinstance(annotation, ast.Constant) and isinstance(annotation.value, str):
|
|
205
|
+
return annotation.value
|
|
206
|
+
return ast.unparse(annotation)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _get_ast_param_types(node: ast.FunctionDef | ast.AsyncFunctionDef) -> dict[str, str]:
|
|
210
|
+
"""Extract parameter names and type strings from function AST.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
node (ast.FunctionDef | ast.AsyncFunctionDef): Function AST node
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
dict[str, str]: Map of param name to annotation string (skips self/cls)
|
|
217
|
+
"""
|
|
218
|
+
ast_params: dict[str, str] = {}
|
|
219
|
+
all_args: list[ast.arg] = []
|
|
220
|
+
all_args.extend(node.args.posonlyargs)
|
|
221
|
+
all_args.extend(node.args.args)
|
|
222
|
+
all_args.extend(node.args.kwonlyargs)
|
|
223
|
+
if node.args.vararg is not None:
|
|
224
|
+
all_args.append(node.args.vararg)
|
|
225
|
+
if node.args.kwarg is not None:
|
|
226
|
+
all_args.append(node.args.kwarg)
|
|
227
|
+
for arg in all_args:
|
|
228
|
+
if arg.arg in ("self", "cls"):
|
|
229
|
+
continue
|
|
230
|
+
if ann_str := _annotation_to_str(arg.annotation):
|
|
231
|
+
ast_params[arg.arg] = ann_str
|
|
232
|
+
return ast_params
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def check_type_consistency(
|
|
236
|
+
parsed: dict[str, Any],
|
|
237
|
+
node: ast.FunctionDef | ast.AsyncFunctionDef,
|
|
238
|
+
) -> list[str]:
|
|
239
|
+
"""Compare docstring types with function annotations.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
parsed (dict[str, Any]): Parsed docstring dictionary
|
|
243
|
+
node (ast.FunctionDef | ast.AsyncFunctionDef): Function AST node
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
list[str]: List of error messages for type mismatches
|
|
247
|
+
"""
|
|
248
|
+
errors = []
|
|
249
|
+
ast_params = _get_ast_param_types(node)
|
|
250
|
+
|
|
251
|
+
# Compare Args
|
|
252
|
+
for arg in parsed.get("Args", []):
|
|
253
|
+
doc_type = arg.get("type")
|
|
254
|
+
if not doc_type:
|
|
255
|
+
continue
|
|
256
|
+
param_name = arg.get("name")
|
|
257
|
+
if not param_name or param_name not in ast_params:
|
|
258
|
+
continue
|
|
259
|
+
ast_type = ast_params[param_name]
|
|
260
|
+
if _normalize_type(doc_type) != _normalize_type(ast_type):
|
|
261
|
+
errors.append(
|
|
262
|
+
f"Parameter '{param_name}': docstring says '{doc_type}' but annotation says '{ast_type}'",
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
# Compare Returns (handle both dict and string "None" from parse_google_docstring)
|
|
266
|
+
returns = parsed.get("Returns")
|
|
267
|
+
doc_ret: str | None = None
|
|
268
|
+
if isinstance(returns, dict):
|
|
269
|
+
doc_ret = returns.get("type")
|
|
270
|
+
elif isinstance(returns, str):
|
|
271
|
+
doc_ret = returns
|
|
272
|
+
ast_ret = _annotation_to_str(node.returns)
|
|
273
|
+
if doc_ret and ast_ret and _normalize_type(doc_ret) != _normalize_type(ast_ret):
|
|
274
|
+
errors.append(
|
|
275
|
+
f"Returns: docstring says '{doc_ret}' but annotation says '{ast_ret}'",
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
return errors
|
|
279
|
+
|
|
280
|
+
|
|
153
281
|
def _check_reference_fields(reference: dict[str, Any], index: int) -> list[str]:
|
|
154
282
|
"""Check a single reference for missing or empty fields.
|
|
155
283
|
|
|
@@ -177,7 +305,7 @@ def _check_reference_fields(reference: dict[str, Any], index: int) -> list[str]:
|
|
|
177
305
|
|
|
178
306
|
|
|
179
307
|
def check_references(docstring_dict: dict[str, Any]) -> list[str]:
|
|
180
|
-
"""Check references section for common errors.
|
|
308
|
+
"""Check references section for common formatting errors.
|
|
181
309
|
|
|
182
310
|
Args:
|
|
183
311
|
docstring_dict (dict[str, Any]): Parsed docstring dictionary
|
|
@@ -216,7 +344,7 @@ def check_references(docstring_dict: dict[str, Any]) -> list[str]:
|
|
|
216
344
|
|
|
217
345
|
|
|
218
346
|
def validate_docstring(docstring: str) -> list[str]:
|
|
219
|
-
"""Perform additional validation on
|
|
347
|
+
"""Perform additional validation on docstring format and structure.
|
|
220
348
|
|
|
221
349
|
Args:
|
|
222
350
|
docstring (str): The docstring to validate
|
|
@@ -250,7 +378,7 @@ def validate_docstring(docstring: str) -> list[str]:
|
|
|
250
378
|
|
|
251
379
|
|
|
252
380
|
def check_returns_section_name(docstring: str) -> list[str]:
|
|
253
|
-
"""Check for incorrect Returns section names.
|
|
381
|
+
"""Check for incorrect Returns section names (e.g. return vs Returns).
|
|
254
382
|
|
|
255
383
|
Args:
|
|
256
384
|
docstring (str): The docstring to check
|
|
@@ -267,8 +395,73 @@ def check_returns_section_name(docstring: str) -> list[str]:
|
|
|
267
395
|
return errors
|
|
268
396
|
|
|
269
397
|
|
|
398
|
+
def _extract_short_description(description: str) -> str:
|
|
399
|
+
r"""Extract short description: first paragraph (up to first blank line), normalized.
|
|
400
|
+
|
|
401
|
+
Leading and trailing whitespace (including blank lines) are stripped before
|
|
402
|
+
paragraph detection. Splits on one or more blank lines (\\n\\s*\\n), takes
|
|
403
|
+
the first part, then normalizes internal whitespace (tabs, newlines, multiple
|
|
404
|
+
spaces) to single spaces. Joins multi-line first paragraph for meta description use.
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
description (str): Full description text
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
str: First paragraph as single line, or empty string
|
|
411
|
+
"""
|
|
412
|
+
if not description:
|
|
413
|
+
return ""
|
|
414
|
+
parts = re.split(r"\n\s*\n", description.strip())
|
|
415
|
+
first_para = parts[0].strip() if parts else ""
|
|
416
|
+
return " ".join(first_para.split()) if first_para else ""
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def check_short_description_length(
|
|
420
|
+
parsed: dict[str, Any],
|
|
421
|
+
min_length: int,
|
|
422
|
+
max_length: int = 0,
|
|
423
|
+
) -> list[str]:
|
|
424
|
+
"""Check that the short description meets length requirements.
|
|
425
|
+
|
|
426
|
+
Short description is the first paragraph (up to first blank line).
|
|
427
|
+
SEO: 120-160 chars recommended for meta descriptions.
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
parsed (dict[str, Any]): Parsed docstring dictionary
|
|
431
|
+
min_length (int): Minimum length (0 to disable)
|
|
432
|
+
max_length (int): Maximum length (0 to disable)
|
|
433
|
+
|
|
434
|
+
Returns:
|
|
435
|
+
list[str]: List of error messages for length violations
|
|
436
|
+
"""
|
|
437
|
+
short_desc = _extract_short_description(parsed.get("Description") or "")
|
|
438
|
+
if not short_desc:
|
|
439
|
+
return []
|
|
440
|
+
|
|
441
|
+
errors: list[str] = []
|
|
442
|
+
if min_length > 0 and len(short_desc) < min_length:
|
|
443
|
+
preview = (
|
|
444
|
+
short_desc[:SHORT_DESC_PREVIEW_LENGTH] + "..."
|
|
445
|
+
if len(short_desc) > SHORT_DESC_PREVIEW_LENGTH
|
|
446
|
+
else short_desc
|
|
447
|
+
)
|
|
448
|
+
errors.append(
|
|
449
|
+
f"Short description too short ({len(short_desc)} chars, min {min_length}): '{preview}'",
|
|
450
|
+
)
|
|
451
|
+
if max_length > 0 and len(short_desc) > max_length:
|
|
452
|
+
preview = (
|
|
453
|
+
short_desc[:SHORT_DESC_PREVIEW_LENGTH] + "..."
|
|
454
|
+
if len(short_desc) > SHORT_DESC_PREVIEW_LENGTH
|
|
455
|
+
else short_desc
|
|
456
|
+
)
|
|
457
|
+
errors.append(
|
|
458
|
+
f"Short description too long ({len(short_desc)} chars, max {max_length}): '{preview}'",
|
|
459
|
+
)
|
|
460
|
+
return errors
|
|
461
|
+
|
|
462
|
+
|
|
270
463
|
def check_returns_type(docstring_dict: dict[str, Any]) -> list[str]:
|
|
271
|
-
"""Check Returns
|
|
464
|
+
"""Check that the Returns section has proper type annotation."""
|
|
272
465
|
errors = []
|
|
273
466
|
if returns := docstring_dict.get("Returns"):
|
|
274
467
|
# Special case: Returns section just contains "None"
|
|
@@ -286,7 +479,7 @@ def check_returns_type(docstring_dict: dict[str, Any]) -> list[str]:
|
|
|
286
479
|
|
|
287
480
|
|
|
288
481
|
def _format_error(context: DocstringContext, error: str) -> str:
|
|
289
|
-
"""Format an error message consistently.
|
|
482
|
+
"""Format an error message consistently with file, line, and name.
|
|
290
483
|
|
|
291
484
|
Args:
|
|
292
485
|
context (DocstringContext): Docstring context
|
|
@@ -333,7 +526,7 @@ def safe_execute(
|
|
|
333
526
|
|
|
334
527
|
|
|
335
528
|
def _check_returns_section(context: DocstringContext, docstring: str) -> list[str]:
|
|
336
|
-
"""Check the Returns section name.
|
|
529
|
+
"""Check the Returns section name for correct spelling.
|
|
337
530
|
|
|
338
531
|
Args:
|
|
339
532
|
context (DocstringContext): Docstring context
|
|
@@ -352,7 +545,7 @@ def _check_returns_section(context: DocstringContext, docstring: str) -> list[st
|
|
|
352
545
|
|
|
353
546
|
|
|
354
547
|
def _validate_docstring_format(context: DocstringContext, docstring: str) -> list[str]:
|
|
355
|
-
"""Validate docstring format.
|
|
548
|
+
"""Validate docstring format for common structural issues.
|
|
356
549
|
|
|
357
550
|
Args:
|
|
358
551
|
context (DocstringContext): Docstring context
|
|
@@ -371,7 +564,7 @@ def _validate_docstring_format(context: DocstringContext, docstring: str) -> lis
|
|
|
371
564
|
|
|
372
565
|
|
|
373
566
|
def _parse_and_check_returns(context: DocstringContext, docstring: str) -> tuple[list[str], dict[str, Any] | None]:
|
|
374
|
-
"""Parse docstring and check
|
|
567
|
+
"""Parse docstring and check that the Returns section has proper type.
|
|
375
568
|
|
|
376
569
|
Args:
|
|
377
570
|
context (DocstringContext): Docstring context
|
|
@@ -408,7 +601,7 @@ def _parse_and_check_returns(context: DocstringContext, docstring: str) -> tuple
|
|
|
408
601
|
|
|
409
602
|
|
|
410
603
|
def _check_additional_validations(context: DocstringContext, parsed: dict[str, Any]) -> list[str]:
|
|
411
|
-
"""Run additional validations on parsed docstring.
|
|
604
|
+
"""Run additional validations on the parsed docstring dictionary.
|
|
412
605
|
|
|
413
606
|
Args:
|
|
414
607
|
context (DocstringContext): Docstring context
|
|
@@ -438,11 +631,36 @@ def _check_additional_validations(context: DocstringContext, parsed: dict[str, A
|
|
|
438
631
|
)
|
|
439
632
|
errors.extend(ref_errors)
|
|
440
633
|
|
|
634
|
+
if context.min_short_description_length > 0 or context.max_short_description_length > 0:
|
|
635
|
+
length_errors, _ = safe_execute(
|
|
636
|
+
context,
|
|
637
|
+
check_short_description_length,
|
|
638
|
+
parsed,
|
|
639
|
+
context.min_short_description_length,
|
|
640
|
+
context.max_short_description_length,
|
|
641
|
+
error_prefix="Error checking short description length",
|
|
642
|
+
)
|
|
643
|
+
errors.extend(length_errors)
|
|
644
|
+
|
|
645
|
+
if (
|
|
646
|
+
context.check_type_consistency
|
|
647
|
+
and context.node is not None
|
|
648
|
+
and isinstance(context.node, (ast.FunctionDef, ast.AsyncFunctionDef))
|
|
649
|
+
):
|
|
650
|
+
consistency_errors, _ = safe_execute(
|
|
651
|
+
context,
|
|
652
|
+
check_type_consistency,
|
|
653
|
+
parsed,
|
|
654
|
+
context.node,
|
|
655
|
+
error_prefix="Error checking type consistency",
|
|
656
|
+
)
|
|
657
|
+
errors.extend(consistency_errors)
|
|
658
|
+
|
|
441
659
|
return errors
|
|
442
660
|
|
|
443
661
|
|
|
444
662
|
def _process_docstring(context: DocstringContext, docstring: str) -> list[str]:
|
|
445
|
-
"""Process a single docstring.
|
|
663
|
+
"""Process a single docstring and collect all validation errors.
|
|
446
664
|
|
|
447
665
|
Args:
|
|
448
666
|
context (DocstringContext): Docstring context
|
|
@@ -482,14 +700,20 @@ def check_file(
|
|
|
482
700
|
require_param_types: bool = False,
|
|
483
701
|
verbose: bool = False,
|
|
484
702
|
check_references: bool = True,
|
|
703
|
+
check_type_consistency: bool = False,
|
|
704
|
+
min_short_description_length: int = 0,
|
|
705
|
+
max_short_description_length: int = 0,
|
|
485
706
|
) -> list[str]:
|
|
486
|
-
"""Check docstrings in a file.
|
|
707
|
+
"""Check docstrings in a Python file for parsing and validation errors.
|
|
487
708
|
|
|
488
709
|
Args:
|
|
489
710
|
file_path (Path): Path to the Python file
|
|
490
711
|
require_param_types (bool): Whether parameter types are required
|
|
491
712
|
verbose (bool): Whether to print verbose output
|
|
492
713
|
check_references (bool): Whether to check references for errors
|
|
714
|
+
check_type_consistency (bool): Whether to compare docstring types with annotations
|
|
715
|
+
min_short_description_length (int): Minimum length for short description (0 to disable)
|
|
716
|
+
max_short_description_length (int): Maximum length for short description (0 to disable)
|
|
493
717
|
|
|
494
718
|
Returns:
|
|
495
719
|
list[str]: List of error messages
|
|
@@ -508,7 +732,7 @@ def check_file(
|
|
|
508
732
|
print(error_msg)
|
|
509
733
|
return errors
|
|
510
734
|
|
|
511
|
-
for name, line_no, docstring,
|
|
735
|
+
for name, line_no, docstring, node in docstrings:
|
|
512
736
|
context = DocstringContext(
|
|
513
737
|
file_path=file_path,
|
|
514
738
|
line_no=line_no,
|
|
@@ -516,6 +740,10 @@ def check_file(
|
|
|
516
740
|
verbose=verbose,
|
|
517
741
|
require_param_types=require_param_types,
|
|
518
742
|
check_references=check_references,
|
|
743
|
+
check_type_consistency=check_type_consistency,
|
|
744
|
+
min_short_description_length=min_short_description_length,
|
|
745
|
+
max_short_description_length=max_short_description_length,
|
|
746
|
+
node=node,
|
|
519
747
|
)
|
|
520
748
|
errors.extend(_process_docstring(context, docstring))
|
|
521
749
|
|
|
@@ -528,6 +756,9 @@ def scan_directory(
|
|
|
528
756
|
require_param_types: bool = False,
|
|
529
757
|
verbose: bool = False,
|
|
530
758
|
check_references: bool = True,
|
|
759
|
+
check_type_consistency: bool = False,
|
|
760
|
+
min_short_description_length: int = 0,
|
|
761
|
+
max_short_description_length: int = 0,
|
|
531
762
|
) -> list[str]:
|
|
532
763
|
"""Scan a directory for Python files and check their docstrings.
|
|
533
764
|
|
|
@@ -537,6 +768,9 @@ def scan_directory(
|
|
|
537
768
|
require_param_types (bool): Whether parameter types are required
|
|
538
769
|
verbose (bool): Whether to print verbose output
|
|
539
770
|
check_references (bool): Whether to check references for errors
|
|
771
|
+
check_type_consistency (bool): Whether to compare docstring types with annotations
|
|
772
|
+
min_short_description_length (int): Minimum length for short description (0 to disable)
|
|
773
|
+
max_short_description_length (int): Maximum length for short description (0 to disable)
|
|
540
774
|
|
|
541
775
|
Returns:
|
|
542
776
|
list[str]: List of error messages
|
|
@@ -559,12 +793,22 @@ def scan_directory(
|
|
|
559
793
|
break
|
|
560
794
|
|
|
561
795
|
if not should_exclude:
|
|
562
|
-
errors.extend(
|
|
796
|
+
errors.extend(
|
|
797
|
+
check_file(
|
|
798
|
+
py_file,
|
|
799
|
+
require_param_types,
|
|
800
|
+
verbose,
|
|
801
|
+
check_references,
|
|
802
|
+
check_type_consistency,
|
|
803
|
+
min_short_description_length,
|
|
804
|
+
max_short_description_length,
|
|
805
|
+
),
|
|
806
|
+
)
|
|
563
807
|
return errors
|
|
564
808
|
|
|
565
809
|
|
|
566
810
|
def _parse_args() -> argparse.Namespace:
|
|
567
|
-
"""Parse command line arguments.
|
|
811
|
+
"""Parse command line arguments for the docstring checker.
|
|
568
812
|
|
|
569
813
|
Returns:
|
|
570
814
|
argparse.Namespace: Parsed command line arguments
|
|
@@ -582,29 +826,53 @@ def _parse_args() -> argparse.Namespace:
|
|
|
582
826
|
action="store_true",
|
|
583
827
|
help="Require parameter types in docstrings",
|
|
584
828
|
)
|
|
585
|
-
parser.
|
|
829
|
+
ref_group = parser.add_mutually_exclusive_group()
|
|
830
|
+
ref_group.add_argument(
|
|
586
831
|
"--check-references",
|
|
587
832
|
action="store_true",
|
|
588
833
|
help="Check references for errors",
|
|
589
834
|
)
|
|
590
|
-
|
|
835
|
+
ref_group.add_argument(
|
|
591
836
|
"--no-check-references",
|
|
592
837
|
action="store_true",
|
|
593
838
|
help="Skip reference checking",
|
|
594
839
|
)
|
|
840
|
+
type_consistency_group = parser.add_mutually_exclusive_group()
|
|
841
|
+
type_consistency_group.add_argument(
|
|
842
|
+
"--check-type-consistency",
|
|
843
|
+
action="store_true",
|
|
844
|
+
help="Compare docstring types with function annotations",
|
|
845
|
+
)
|
|
846
|
+
type_consistency_group.add_argument(
|
|
847
|
+
"--no-check-type-consistency",
|
|
848
|
+
action="store_true",
|
|
849
|
+
help="Skip type consistency checking",
|
|
850
|
+
)
|
|
595
851
|
parser.add_argument(
|
|
596
852
|
"--exclude-files",
|
|
597
853
|
help="Comma-separated list of filenames to exclude",
|
|
598
854
|
default="",
|
|
599
855
|
)
|
|
600
856
|
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
|
|
857
|
+
parser.add_argument(
|
|
858
|
+
"--min-short-description-length",
|
|
859
|
+
type=int,
|
|
860
|
+
metavar="N",
|
|
861
|
+
help="Minimum length for short description (0 to disable)",
|
|
862
|
+
)
|
|
863
|
+
parser.add_argument(
|
|
864
|
+
"--max-short-description-length",
|
|
865
|
+
type=int,
|
|
866
|
+
metavar="N",
|
|
867
|
+
help="Maximum length for short description (0 to disable, 160 recommended for SEO)",
|
|
868
|
+
)
|
|
601
869
|
return parser.parse_args()
|
|
602
870
|
|
|
603
871
|
|
|
604
872
|
def _get_config_values(
|
|
605
873
|
args: argparse.Namespace,
|
|
606
874
|
config: dict[str, Any],
|
|
607
|
-
) -> tuple[list[str], bool, bool, bool, list[str]]:
|
|
875
|
+
) -> tuple[list[str], bool, bool, bool, bool, int, int, list[str]]:
|
|
608
876
|
"""Get configuration values from command line arguments and config file.
|
|
609
877
|
|
|
610
878
|
Args:
|
|
@@ -612,11 +880,14 @@ def _get_config_values(
|
|
|
612
880
|
config (dict[str, Any]): Configuration dictionary
|
|
613
881
|
|
|
614
882
|
Returns:
|
|
615
|
-
tuple[list[str], bool, bool, bool, list[str]]: Tuple containing:
|
|
883
|
+
tuple[list[str], bool, bool, bool, bool, int, int, list[str]]: Tuple containing:
|
|
616
884
|
- List of paths to check
|
|
617
885
|
- Whether to require parameter types
|
|
618
|
-
- Whether to check references
|
|
619
886
|
- Whether to enable verbose output
|
|
887
|
+
- Whether to check references
|
|
888
|
+
- Whether to check type consistency
|
|
889
|
+
- Minimum short description length
|
|
890
|
+
- Maximum short description length
|
|
620
891
|
- List of files to exclude
|
|
621
892
|
"""
|
|
622
893
|
# Get paths
|
|
@@ -628,13 +899,20 @@ def _get_config_values(
|
|
|
628
899
|
# Get verbose
|
|
629
900
|
verbose = args.verbose or config["verbose"]
|
|
630
901
|
|
|
631
|
-
# Get check_references - handle both positive and negative flags
|
|
902
|
+
# Get check_references - handle both positive and negative flags (mutually exclusive)
|
|
632
903
|
check_references = config["check_references"]
|
|
633
904
|
if args.check_references:
|
|
634
905
|
check_references = True
|
|
635
906
|
if args.no_check_references:
|
|
636
907
|
check_references = False
|
|
637
908
|
|
|
909
|
+
# Get check_type_consistency - handle both positive and negative flags (mutually exclusive)
|
|
910
|
+
check_type_consistency = config.get("check_type_consistency", False)
|
|
911
|
+
if args.check_type_consistency:
|
|
912
|
+
check_type_consistency = True
|
|
913
|
+
if args.no_check_type_consistency:
|
|
914
|
+
check_type_consistency = False
|
|
915
|
+
|
|
638
916
|
# Get exclude_files
|
|
639
917
|
exclude_files = []
|
|
640
918
|
if args.exclude_files:
|
|
@@ -644,7 +922,26 @@ def _get_config_values(
|
|
|
644
922
|
if not exclude_files:
|
|
645
923
|
exclude_files = config["exclude_files"]
|
|
646
924
|
|
|
647
|
-
|
|
925
|
+
# Get min_short_description_length - CLI overrides config
|
|
926
|
+
min_short_description_length = config.get("min_short_description_length", 0)
|
|
927
|
+
if args.min_short_description_length is not None:
|
|
928
|
+
min_short_description_length = args.min_short_description_length
|
|
929
|
+
|
|
930
|
+
# Get max_short_description_length - CLI overrides config
|
|
931
|
+
max_short_description_length = config.get("max_short_description_length", 0)
|
|
932
|
+
if args.max_short_description_length is not None:
|
|
933
|
+
max_short_description_length = args.max_short_description_length
|
|
934
|
+
|
|
935
|
+
return (
|
|
936
|
+
paths,
|
|
937
|
+
require_param_types,
|
|
938
|
+
verbose,
|
|
939
|
+
check_references,
|
|
940
|
+
check_type_consistency,
|
|
941
|
+
min_short_description_length,
|
|
942
|
+
max_short_description_length,
|
|
943
|
+
exclude_files,
|
|
944
|
+
)
|
|
648
945
|
|
|
649
946
|
|
|
650
947
|
def _process_paths(
|
|
@@ -653,8 +950,11 @@ def _process_paths(
|
|
|
653
950
|
require_param_types: bool,
|
|
654
951
|
verbose: bool,
|
|
655
952
|
check_references: bool,
|
|
953
|
+
check_type_consistency: bool,
|
|
954
|
+
min_short_description_length: int,
|
|
955
|
+
max_short_description_length: int,
|
|
656
956
|
) -> list[str]:
|
|
657
|
-
"""Process paths and check docstrings.
|
|
957
|
+
"""Process paths and check docstrings in each file or directory.
|
|
658
958
|
|
|
659
959
|
Args:
|
|
660
960
|
paths (list[str]): List of paths to check
|
|
@@ -662,6 +962,9 @@ def _process_paths(
|
|
|
662
962
|
require_param_types (bool): Whether parameter types are required
|
|
663
963
|
verbose (bool): Whether to print verbose output
|
|
664
964
|
check_references (bool): Whether to check references for errors
|
|
965
|
+
check_type_consistency (bool): Whether to compare docstring types with annotations
|
|
966
|
+
min_short_description_length (int): Minimum length for short description (0 to disable)
|
|
967
|
+
max_short_description_length (int): Maximum length for short description (0 to disable)
|
|
665
968
|
|
|
666
969
|
Returns:
|
|
667
970
|
list[str]: List of error messages
|
|
@@ -670,10 +973,27 @@ def _process_paths(
|
|
|
670
973
|
for path_str in paths:
|
|
671
974
|
path = Path(path_str)
|
|
672
975
|
if path.is_dir():
|
|
673
|
-
errors = scan_directory(
|
|
976
|
+
errors = scan_directory(
|
|
977
|
+
path,
|
|
978
|
+
exclude_files,
|
|
979
|
+
require_param_types,
|
|
980
|
+
verbose,
|
|
981
|
+
check_references,
|
|
982
|
+
check_type_consistency,
|
|
983
|
+
min_short_description_length,
|
|
984
|
+
max_short_description_length,
|
|
985
|
+
)
|
|
674
986
|
all_errors.extend(errors)
|
|
675
987
|
elif path.is_file() and path.suffix == ".py":
|
|
676
|
-
errors = check_file(
|
|
988
|
+
errors = check_file(
|
|
989
|
+
path,
|
|
990
|
+
require_param_types,
|
|
991
|
+
verbose,
|
|
992
|
+
check_references,
|
|
993
|
+
check_type_consistency,
|
|
994
|
+
min_short_description_length,
|
|
995
|
+
max_short_description_length,
|
|
996
|
+
)
|
|
677
997
|
all_errors.extend(errors)
|
|
678
998
|
else:
|
|
679
999
|
print(f"Error: {path} is not a directory or Python file")
|
|
@@ -681,7 +1001,7 @@ def _process_paths(
|
|
|
681
1001
|
|
|
682
1002
|
|
|
683
1003
|
def main() -> None:
|
|
684
|
-
"""Run the docstring checker.
|
|
1004
|
+
"""Run the docstring checker and exit with appropriate status code.
|
|
685
1005
|
|
|
686
1006
|
Returns:
|
|
687
1007
|
None
|
|
@@ -693,7 +1013,16 @@ def main() -> None:
|
|
|
693
1013
|
args = _parse_args()
|
|
694
1014
|
|
|
695
1015
|
# Get configuration values
|
|
696
|
-
|
|
1016
|
+
(
|
|
1017
|
+
paths,
|
|
1018
|
+
require_param_types,
|
|
1019
|
+
verbose,
|
|
1020
|
+
check_references,
|
|
1021
|
+
check_type_consistency,
|
|
1022
|
+
min_short_description_length,
|
|
1023
|
+
max_short_description_length,
|
|
1024
|
+
exclude_files,
|
|
1025
|
+
) = _get_config_values(args, config)
|
|
697
1026
|
|
|
698
1027
|
# Print configuration if verbose
|
|
699
1028
|
if verbose:
|
|
@@ -701,6 +1030,9 @@ def main() -> None:
|
|
|
701
1030
|
print(f" Paths: {paths}")
|
|
702
1031
|
print(f" Require parameter types: {require_param_types}")
|
|
703
1032
|
print(f" Check references: {check_references}")
|
|
1033
|
+
print(f" Check type consistency: {check_type_consistency}")
|
|
1034
|
+
print(f" Min short description length: {min_short_description_length}")
|
|
1035
|
+
print(f" Max short description length: {max_short_description_length}")
|
|
704
1036
|
print(f" Exclude files: {exclude_files}")
|
|
705
1037
|
|
|
706
1038
|
# Check if paths is empty
|
|
@@ -717,6 +1049,9 @@ def main() -> None:
|
|
|
717
1049
|
require_param_types,
|
|
718
1050
|
verbose,
|
|
719
1051
|
check_references,
|
|
1052
|
+
check_type_consistency,
|
|
1053
|
+
min_short_description_length,
|
|
1054
|
+
max_short_description_length,
|
|
720
1055
|
):
|
|
721
1056
|
for error in all_errors:
|
|
722
1057
|
print(error)
|
|
File without changes
|
{google_docstring_parser-0.0.9 → google_docstring_parser-0.0.11}/google_docstring_parser/__init__.py
RENAMED
|
File without changes
|
{google_docstring_parser-0.0.9 → google_docstring_parser-0.0.11}/google_docstring_parser/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|