docstring-format-checker 0.8.0__tar.gz → 0.9.0__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.
- {docstring_format_checker-0.8.0 → docstring_format_checker-0.9.0}/PKG-INFO +1 -1
- {docstring_format_checker-0.8.0 → docstring_format_checker-0.9.0}/pyproject.toml +1 -1
- {docstring_format_checker-0.8.0 → docstring_format_checker-0.9.0}/src/docstring_format_checker/__init__.py +1 -1
- {docstring_format_checker-0.8.0 → docstring_format_checker-0.9.0}/src/docstring_format_checker/cli.py +9 -5
- {docstring_format_checker-0.8.0 → docstring_format_checker-0.9.0}/src/docstring_format_checker/config.py +64 -14
- {docstring_format_checker-0.8.0 → docstring_format_checker-0.9.0}/src/docstring_format_checker/core.py +21 -15
- {docstring_format_checker-0.8.0 → docstring_format_checker-0.9.0}/README.md +0 -0
- {docstring_format_checker-0.8.0 → docstring_format_checker-0.9.0}/src/docstring_format_checker/utils/__init__.py +0 -0
- {docstring_format_checker-0.8.0 → docstring_format_checker-0.9.0}/src/docstring_format_checker/utils/exceptions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: docstring-format-checker
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: A CLI tool to check and validate Python docstring formatting and completeness
|
|
5
5
|
Author: Chris Mahoney
|
|
6
6
|
Author-email: Chris Mahoney <docstring-format-checker@data-science-extensions.com>
|
|
@@ -4,7 +4,7 @@ Docstring Format Checker.
|
|
|
4
4
|
A CLI tool to check and validate Python docstring formatting and completeness.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
__version__ = "v0.
|
|
7
|
+
__version__ = "v0.9.0"
|
|
8
8
|
__author__ = "Chris Mahoney"
|
|
9
9
|
__email__ = "docstring-format-checker@data-science-extensions.com"
|
|
10
10
|
|
|
@@ -56,7 +56,11 @@ from typer import Argument, CallbackParam, Context, Exit, Option, Typer, echo
|
|
|
56
56
|
|
|
57
57
|
# ## Local First Party Imports ----
|
|
58
58
|
from docstring_format_checker import __version__
|
|
59
|
-
from docstring_format_checker.config import
|
|
59
|
+
from docstring_format_checker.config import (
|
|
60
|
+
Config,
|
|
61
|
+
find_config_file,
|
|
62
|
+
load_config,
|
|
63
|
+
)
|
|
60
64
|
from docstring_format_checker.core import DocstringChecker, DocstringError
|
|
61
65
|
|
|
62
66
|
|
|
@@ -527,21 +531,21 @@ def check_docstrings(
|
|
|
527
531
|
if not config_path.exists():
|
|
528
532
|
console.print(_red(f"Error: Configuration file does not exist: {config}"))
|
|
529
533
|
raise Exit(1)
|
|
530
|
-
|
|
534
|
+
config_obj = load_config(config_path)
|
|
531
535
|
else:
|
|
532
536
|
# Try to find config file automatically
|
|
533
537
|
found_config: Optional[Path] = find_config_file(target_path if target_path.is_dir() else target_path.parent)
|
|
534
538
|
if found_config:
|
|
535
|
-
|
|
539
|
+
config_obj: Config = load_config(found_config)
|
|
536
540
|
else:
|
|
537
|
-
|
|
541
|
+
config_obj: Config = load_config()
|
|
538
542
|
|
|
539
543
|
except Exception as e:
|
|
540
544
|
console.print(_red(f"Error loading configuration: {e}"))
|
|
541
545
|
raise Exit(1)
|
|
542
546
|
|
|
543
547
|
# Initialize checker
|
|
544
|
-
checker = DocstringChecker(
|
|
548
|
+
checker = DocstringChecker(config_obj)
|
|
545
549
|
|
|
546
550
|
# Check files
|
|
547
551
|
try:
|
|
@@ -70,7 +70,9 @@ else:
|
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
__all__: list[str] = [
|
|
73
|
+
"GlobalConfig",
|
|
73
74
|
"SectionConfig",
|
|
75
|
+
"Config",
|
|
74
76
|
"DEFAULT_CONFIG",
|
|
75
77
|
"load_config",
|
|
76
78
|
"find_config_file",
|
|
@@ -92,13 +94,29 @@ VALID_TYPES: tuple[str, ...] = (
|
|
|
92
94
|
|
|
93
95
|
# ---------------------------------------------------------------------------- #
|
|
94
96
|
# #
|
|
95
|
-
#
|
|
97
|
+
# Config ####
|
|
96
98
|
# #
|
|
97
99
|
# ---------------------------------------------------------------------------- #
|
|
98
100
|
|
|
99
101
|
|
|
100
102
|
## --------------------------------------------------------------------------- #
|
|
101
|
-
##
|
|
103
|
+
## GlobalConfig ####
|
|
104
|
+
## --------------------------------------------------------------------------- #
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass
|
|
108
|
+
class GlobalConfig:
|
|
109
|
+
"""
|
|
110
|
+
Global configuration for docstring checking behavior.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
allow_undefined_sections: bool = False
|
|
114
|
+
require_docstrings: bool = True
|
|
115
|
+
check_private: bool = False
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
## --------------------------------------------------------------------------- #
|
|
119
|
+
## SectionConfig ####
|
|
102
120
|
## --------------------------------------------------------------------------- #
|
|
103
121
|
|
|
104
122
|
|
|
@@ -176,12 +194,29 @@ def _validate_config_order(config_sections: list[SectionConfig]) -> None:
|
|
|
176
194
|
|
|
177
195
|
# ---------------------------------------------------------------------------- #
|
|
178
196
|
# #
|
|
179
|
-
#
|
|
197
|
+
# Config Container ####
|
|
180
198
|
# #
|
|
181
199
|
# ---------------------------------------------------------------------------- #
|
|
182
200
|
|
|
183
201
|
|
|
184
|
-
|
|
202
|
+
@dataclass
|
|
203
|
+
class Config:
|
|
204
|
+
"""
|
|
205
|
+
Complete configuration containing global settings and section definitions.
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
global_config: GlobalConfig
|
|
209
|
+
sections: list[SectionConfig]
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
# ---------------------------------------------------------------------------- #
|
|
213
|
+
# #
|
|
214
|
+
# Default Configuration ####
|
|
215
|
+
# #
|
|
216
|
+
# ---------------------------------------------------------------------------- #
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
DEFAULT_SECTIONS: list[SectionConfig] = [
|
|
185
220
|
SectionConfig(
|
|
186
221
|
order=1,
|
|
187
222
|
name="summary",
|
|
@@ -241,7 +276,13 @@ DEFAULT_CONFIG: list[SectionConfig] = [
|
|
|
241
276
|
]
|
|
242
277
|
|
|
243
278
|
|
|
244
|
-
|
|
279
|
+
DEFAULT_CONFIG: Config = Config(
|
|
280
|
+
global_config=GlobalConfig(),
|
|
281
|
+
sections=DEFAULT_SECTIONS,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def load_config(config_path: Optional[Union[str, Path]] = None) -> Config:
|
|
245
286
|
"""
|
|
246
287
|
!!! note "Summary"
|
|
247
288
|
Load configuration from a TOML file or return default configuration.
|
|
@@ -253,8 +294,8 @@ def load_config(config_path: Optional[Union[str, Path]] = None) -> list[SectionC
|
|
|
253
294
|
Default: `None`.
|
|
254
295
|
|
|
255
296
|
Returns:
|
|
256
|
-
(
|
|
257
|
-
|
|
297
|
+
(Config):
|
|
298
|
+
Configuration object containing global settings and section definitions.
|
|
258
299
|
|
|
259
300
|
Raises:
|
|
260
301
|
(FileNotFoundError):
|
|
@@ -271,6 +312,7 @@ def load_config(config_path: Optional[Union[str, Path]] = None) -> list[SectionC
|
|
|
271
312
|
else:
|
|
272
313
|
return DEFAULT_CONFIG
|
|
273
314
|
|
|
315
|
+
# Convert to Path object and check existence
|
|
274
316
|
config_path = Path(config_path)
|
|
275
317
|
if not config_path.exists():
|
|
276
318
|
raise FileNotFoundError(f"Configuration file not found: {config_path}")
|
|
@@ -292,6 +334,13 @@ def load_config(config_path: Optional[Union[str, Path]] = None) -> list[SectionC
|
|
|
292
334
|
if tool_config is None:
|
|
293
335
|
return DEFAULT_CONFIG
|
|
294
336
|
|
|
337
|
+
# Parse global configuration flags
|
|
338
|
+
global_config = GlobalConfig(
|
|
339
|
+
allow_undefined_sections=tool_config.get("allow_undefined_sections", False),
|
|
340
|
+
require_docstrings=tool_config.get("require_docstrings", True),
|
|
341
|
+
check_private=tool_config.get("check_private", False),
|
|
342
|
+
)
|
|
343
|
+
|
|
295
344
|
# Parse sections configuration
|
|
296
345
|
sections_config: list[SectionConfig] = []
|
|
297
346
|
if "sections" in tool_config:
|
|
@@ -315,16 +364,17 @@ def load_config(config_path: Optional[Union[str, Path]] = None) -> list[SectionC
|
|
|
315
364
|
except (KeyError, TypeError, ValueError, InvalidTypeValuesError) as e:
|
|
316
365
|
raise InvalidConfigError(f"Invalid section configuration: {section_data}. Error: {e}") from e
|
|
317
366
|
|
|
367
|
+
# Use default sections if none provided, otherwise validate and sort
|
|
318
368
|
if not sections_config:
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
369
|
+
sections_config = DEFAULT_SECTIONS
|
|
370
|
+
else:
|
|
371
|
+
# Validate no duplicate order values
|
|
372
|
+
_validate_config_order(config_sections=sections_config)
|
|
323
373
|
|
|
324
|
-
|
|
325
|
-
|
|
374
|
+
# Sort by order
|
|
375
|
+
sections_config.sort(key=lambda x: x.order)
|
|
326
376
|
|
|
327
|
-
return sections_config
|
|
377
|
+
return Config(global_config=global_config, sections=sections_config)
|
|
328
378
|
|
|
329
379
|
|
|
330
380
|
def find_config_file(start_path: Optional[Path] = None) -> Optional[Path]:
|
|
@@ -50,7 +50,7 @@ from pathlib import Path
|
|
|
50
50
|
from typing import Iterator, Literal, NamedTuple, Optional, Union
|
|
51
51
|
|
|
52
52
|
# ## Local First Party Imports ----
|
|
53
|
-
from docstring_format_checker.config import SectionConfig
|
|
53
|
+
from docstring_format_checker.config import Config, SectionConfig
|
|
54
54
|
from docstring_format_checker.utils.exceptions import (
|
|
55
55
|
DirectoryNotFoundError,
|
|
56
56
|
DocstringError,
|
|
@@ -95,18 +95,19 @@ class DocstringChecker:
|
|
|
95
95
|
Main class for checking docstring format and completeness.
|
|
96
96
|
"""
|
|
97
97
|
|
|
98
|
-
def __init__(self,
|
|
98
|
+
def __init__(self, config: Config) -> None:
|
|
99
99
|
"""
|
|
100
100
|
!!! note "Summary"
|
|
101
101
|
Initialize the docstring checker.
|
|
102
102
|
|
|
103
103
|
Params:
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
config (Config):
|
|
105
|
+
Configuration object containing global settings and section definitions.
|
|
106
106
|
"""
|
|
107
|
-
self.
|
|
108
|
-
self.
|
|
109
|
-
self.
|
|
107
|
+
self.config = config
|
|
108
|
+
self.sections_config: list[SectionConfig] = config.sections
|
|
109
|
+
self.required_sections: list[SectionConfig] = [s for s in config.sections if s.required]
|
|
110
|
+
self.optional_sections: list[SectionConfig] = [s for s in config.sections if not s.required]
|
|
110
111
|
|
|
111
112
|
def check_file(self, file_path: Union[str, Path]) -> list[DocstringError]:
|
|
112
113
|
"""
|
|
@@ -279,7 +280,9 @@ class DocstringChecker:
|
|
|
279
280
|
self.checker: DocstringChecker = checker
|
|
280
281
|
|
|
281
282
|
def visit_ClassDef(self, node: ast.ClassDef) -> None:
|
|
282
|
-
|
|
283
|
+
# Skip private classes unless check_private is enabled
|
|
284
|
+
should_check: bool = self.checker.config.global_config.check_private or not node.name.startswith("_")
|
|
285
|
+
if should_check:
|
|
283
286
|
items.append(
|
|
284
287
|
FunctionAndClassDetails(
|
|
285
288
|
item_type="class",
|
|
@@ -304,9 +307,10 @@ class DocstringChecker:
|
|
|
304
307
|
def _visit_function(self, node: Union[ast.FunctionDef, ast.AsyncFunctionDef]) -> None:
|
|
305
308
|
"""Visit function definition node (sync or async)."""
|
|
306
309
|
|
|
307
|
-
|
|
310
|
+
# Skip private functions unless check_private is enabled
|
|
311
|
+
should_check: bool = self.checker.config.global_config.check_private or not node.name.startswith("_")
|
|
312
|
+
if should_check:
|
|
308
313
|
# Skip @overload functions - they don't need docstrings
|
|
309
|
-
|
|
310
314
|
if not self.checker._is_overload_function(node):
|
|
311
315
|
item_type: Literal["function", "method"] = "method" if self.class_stack else "function"
|
|
312
316
|
parent_class: Optional[str] = self.class_stack[-1] if self.class_stack else None
|
|
@@ -378,7 +382,8 @@ class DocstringChecker:
|
|
|
378
382
|
applicable_sections.append(section)
|
|
379
383
|
|
|
380
384
|
if not docstring:
|
|
381
|
-
if
|
|
385
|
+
# Only require docstrings if the global flag is enabled
|
|
386
|
+
if requires_docstring and self.config.global_config.require_docstrings:
|
|
382
387
|
message: str = f"Missing docstring for {item.item_type}"
|
|
383
388
|
raise DocstringError(
|
|
384
389
|
message=message,
|
|
@@ -387,7 +392,7 @@ class DocstringChecker:
|
|
|
387
392
|
item_name=item.name,
|
|
388
393
|
item_type=item.item_type,
|
|
389
394
|
)
|
|
390
|
-
return # No docstring required
|
|
395
|
+
return # No docstring required or docstring requirement disabled
|
|
391
396
|
|
|
392
397
|
# Validate docstring sections if docstring exists
|
|
393
398
|
self._validate_docstring_sections(docstring, item, file_path)
|
|
@@ -452,9 +457,10 @@ class DocstringChecker:
|
|
|
452
457
|
if self._has_both_returns_and_yields(docstring):
|
|
453
458
|
errors.append("Docstring cannot have both Returns and Yields sections")
|
|
454
459
|
|
|
455
|
-
# Check for undefined sections in docstring
|
|
456
|
-
|
|
457
|
-
|
|
460
|
+
# Check for undefined sections in docstring (only if not allowed)
|
|
461
|
+
if not self.config.global_config.allow_undefined_sections:
|
|
462
|
+
undefined_errors: list[str] = self._check_undefined_sections(docstring)
|
|
463
|
+
errors.extend(undefined_errors)
|
|
458
464
|
|
|
459
465
|
# Check admonition values match configuration
|
|
460
466
|
admonition_errors: list[str] = self._check_admonition_values(docstring)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|