docstring-format-checker 1.3.0__tar.gz → 1.5.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-1.3.0 → docstring_format_checker-1.5.0}/PKG-INFO +2 -1
- {docstring_format_checker-1.3.0 → docstring_format_checker-1.5.0}/pyproject.toml +37 -9
- {docstring_format_checker-1.3.0 → docstring_format_checker-1.5.0}/src/docstring_format_checker/cli.py +303 -102
- {docstring_format_checker-1.3.0 → docstring_format_checker-1.5.0}/src/docstring_format_checker/config.py +140 -44
- docstring_format_checker-1.5.0/src/docstring_format_checker/core.py +1742 -0
- docstring_format_checker-1.5.0/src/docstring_format_checker/utils/exceptions.py +103 -0
- docstring_format_checker-1.3.0/src/docstring_format_checker/core.py +0 -1090
- docstring_format_checker-1.3.0/src/docstring_format_checker/utils/exceptions.py +0 -69
- {docstring_format_checker-1.3.0 → docstring_format_checker-1.5.0}/README.md +0 -0
- {docstring_format_checker-1.3.0 → docstring_format_checker-1.5.0}/src/docstring_format_checker/__init__.py +0 -0
- {docstring_format_checker-1.3.0 → docstring_format_checker-1.5.0}/src/docstring_format_checker/utils/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: docstring-format-checker
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.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>
|
|
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
21
|
Classifier: Intended Audience :: Developers
|
|
21
22
|
Classifier: Environment :: Console
|
|
22
23
|
Requires-Dist: typer>=0.9.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "docstring-format-checker"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.5.0"
|
|
4
4
|
description = "A CLI tool to check and validate Python docstring formatting and completeness"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -23,6 +23,7 @@ classifiers = [
|
|
|
23
23
|
"Programming Language :: Python :: 3.11",
|
|
24
24
|
"Programming Language :: Python :: 3.12",
|
|
25
25
|
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Programming Language :: Python :: 3.14",
|
|
26
27
|
"Intended Audience :: Developers",
|
|
27
28
|
"Environment :: Console",
|
|
28
29
|
]
|
|
@@ -83,6 +84,7 @@ test = [
|
|
|
83
84
|
"pytest-sugar==1.*",
|
|
84
85
|
"pytest-xdist==3.*",
|
|
85
86
|
"requests==2.*",
|
|
87
|
+
"complexipy==4.*",
|
|
86
88
|
]
|
|
87
89
|
|
|
88
90
|
[tool.black]
|
|
@@ -147,6 +149,32 @@ lines_after_imports = 2
|
|
|
147
149
|
[tool.codespell]
|
|
148
150
|
ignore-words-list = "demog"
|
|
149
151
|
|
|
152
|
+
[tool.pylint.main]
|
|
153
|
+
disable = [
|
|
154
|
+
"C0103", # invalid-name
|
|
155
|
+
"C0301", # line-too-long
|
|
156
|
+
"C0302", # too-many-lines
|
|
157
|
+
"R0913", # too-many-arguments
|
|
158
|
+
"R0914", # too-many-locals
|
|
159
|
+
"R0915", # too-many-statements
|
|
160
|
+
"R0917", # too-many-positional-arguments
|
|
161
|
+
"R1705", # no-else-return
|
|
162
|
+
"R1724", # no-else-continue
|
|
163
|
+
"W0107", # unnecessary-pass
|
|
164
|
+
"W0212", # protected-access
|
|
165
|
+
"W0612", # unused-variable
|
|
166
|
+
"W0613", # unused-argument
|
|
167
|
+
"W0622", # redefined-builtin
|
|
168
|
+
"W0718", # broad-exception-caught
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
[tool.complexipy]
|
|
172
|
+
paths = "src/docstring_format_checker"
|
|
173
|
+
max-complexity-allowed = 15
|
|
174
|
+
quiet = false
|
|
175
|
+
ignore-complexity = false
|
|
176
|
+
sort = "asc"
|
|
177
|
+
|
|
150
178
|
[tool.bump_version.replacements]
|
|
151
179
|
files = [
|
|
152
180
|
{ file = "src/docstring_format_checker/__init__.py", pattern = "__version__ = \"{VERSION}\"" },
|
|
@@ -159,14 +187,14 @@ allow_undefined_sections = false
|
|
|
159
187
|
require_docstrings = true
|
|
160
188
|
check_private = true
|
|
161
189
|
sections = [
|
|
162
|
-
{ order
|
|
163
|
-
{ order
|
|
164
|
-
{ order
|
|
165
|
-
{ order
|
|
166
|
-
{ order
|
|
167
|
-
{ order
|
|
168
|
-
{ order
|
|
169
|
-
{ order
|
|
190
|
+
{ order=1, name="summary", type="free_text", required=true, admonition="note", prefix="!!!" },
|
|
191
|
+
{ order=2, name="details", type="free_text", required=false, admonition="abstract", prefix="???+" },
|
|
192
|
+
{ order=3, name="params", type="list_name_and_type", required=false, admonition=false },
|
|
193
|
+
{ order=4, name="raises", type="list_type", required=false, admonition=false },
|
|
194
|
+
{ order=5, name="returns", type="list_name_and_type", required=false, admonition=false },
|
|
195
|
+
{ order=6, name="yields", type="list_type", required=false, admonition=false },
|
|
196
|
+
{ order=7, name="examples", type="free_text", required=false, admonition="example", prefix="???+" },
|
|
197
|
+
{ order=8, name="notes", type="free_text", required=false, admonition="note", prefix="???" },
|
|
170
198
|
]
|
|
171
199
|
|
|
172
200
|
[build-system]
|
|
@@ -360,12 +360,18 @@ def _format_error_messages(error_message: str) -> str:
|
|
|
360
360
|
errors: list[str] = error_message.split("; ")
|
|
361
361
|
formatted_errors: list[str] = [f"- {error.strip()}" for error in errors if error.strip()]
|
|
362
362
|
return ";\n".join(formatted_errors) + "."
|
|
363
|
+
|
|
364
|
+
# Single error message
|
|
363
365
|
else:
|
|
364
|
-
# Single error message
|
|
365
366
|
return f"- {error_message.strip()}."
|
|
366
367
|
|
|
367
368
|
|
|
368
|
-
def _display_results(
|
|
369
|
+
def _display_results(
|
|
370
|
+
results: dict[str, list[DocstringError]],
|
|
371
|
+
quiet: bool,
|
|
372
|
+
output: str,
|
|
373
|
+
check: bool,
|
|
374
|
+
) -> int:
|
|
369
375
|
"""
|
|
370
376
|
!!! note "Summary"
|
|
371
377
|
Display the results of docstring checking.
|
|
@@ -386,100 +392,193 @@ def _display_results(results: dict[str, list[DocstringError]], quiet: bool, outp
|
|
|
386
392
|
"""
|
|
387
393
|
if not results:
|
|
388
394
|
if not quiet:
|
|
389
|
-
console.print(_green("
|
|
395
|
+
console.print(_green("✅ All docstrings are valid!"))
|
|
390
396
|
return 0
|
|
391
397
|
|
|
392
|
-
# Count
|
|
398
|
+
# Count errors and generate summary statistics
|
|
399
|
+
error_stats = _count_errors_and_files(results)
|
|
400
|
+
|
|
401
|
+
if quiet:
|
|
402
|
+
_display_quiet_summary(error_stats)
|
|
403
|
+
return 1
|
|
404
|
+
|
|
405
|
+
# Display detailed results based on output format
|
|
406
|
+
if output == "table":
|
|
407
|
+
_display_table_output(results)
|
|
408
|
+
else:
|
|
409
|
+
_display_list_output(results)
|
|
410
|
+
|
|
411
|
+
# Display final summary
|
|
412
|
+
_display_final_summary(error_stats)
|
|
413
|
+
return 1
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def _count_errors_and_files(results: dict[str, list[DocstringError]]) -> dict[str, int]:
|
|
417
|
+
"""
|
|
418
|
+
!!! note "Summary"
|
|
419
|
+
Count total errors, functions, and files from results.
|
|
420
|
+
|
|
421
|
+
Params:
|
|
422
|
+
results (dict[str, list[DocstringError]]):
|
|
423
|
+
Dictionary mapping file paths to lists of errors.
|
|
424
|
+
|
|
425
|
+
Returns:
|
|
426
|
+
(dict[str, int]):
|
|
427
|
+
Dictionary containing total_errors, total_functions, and total_files.
|
|
428
|
+
"""
|
|
393
429
|
total_individual_errors: int = 0
|
|
394
430
|
total_functions: int = 0
|
|
395
431
|
|
|
396
432
|
for errors in results.values():
|
|
397
|
-
total_functions += len(errors)
|
|
433
|
+
total_functions += len(errors)
|
|
398
434
|
for error in errors:
|
|
399
|
-
# Count individual error messages within each error object
|
|
400
435
|
if "; " in error.message:
|
|
401
|
-
individual_errors = [msg.strip() for msg in error.message.split("; ") if msg.strip()]
|
|
436
|
+
individual_errors: list[str] = [msg.strip() for msg in error.message.split("; ") if msg.strip()]
|
|
402
437
|
total_individual_errors += len(individual_errors)
|
|
403
438
|
else:
|
|
404
439
|
total_individual_errors += 1
|
|
405
440
|
|
|
406
|
-
total_errors:
|
|
407
|
-
total_files: int = len(results)
|
|
441
|
+
return {"total_errors": total_individual_errors, "total_functions": total_functions, "total_files": len(results)}
|
|
408
442
|
|
|
409
|
-
if quiet:
|
|
410
|
-
# In quiet mode, only show summary with improved format
|
|
411
|
-
if total_functions == 1:
|
|
412
|
-
functions_text = f"1 function"
|
|
413
|
-
else:
|
|
414
|
-
functions_text = f"{total_functions} functions"
|
|
415
443
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
444
|
+
def _display_quiet_summary(error_stats: dict[str, int]) -> None:
|
|
445
|
+
"""
|
|
446
|
+
!!! note "Summary"
|
|
447
|
+
Display summary in quiet mode.
|
|
420
448
|
|
|
421
|
-
|
|
422
|
-
|
|
449
|
+
Params:
|
|
450
|
+
error_stats (dict[str, int]):
|
|
451
|
+
Dictionary containing total_errors, total_functions, and total_files.
|
|
452
|
+
"""
|
|
453
|
+
functions_text = (
|
|
454
|
+
"1 function" if error_stats["total_functions"] == 1 else f"{error_stats['total_functions']} functions"
|
|
455
|
+
)
|
|
456
|
+
files_text: str = "1 file" if error_stats["total_files"] == 1 else f"{error_stats['total_files']} files"
|
|
423
457
|
|
|
424
|
-
|
|
425
|
-
# Show detailed table
|
|
426
|
-
table = Table(show_header=True, header_style="bold magenta")
|
|
427
|
-
table.add_column("File", style="cyan", no_wrap=False)
|
|
428
|
-
table.add_column("Line", justify="right", style="white")
|
|
429
|
-
table.add_column("Item", style="yellow")
|
|
430
|
-
table.add_column("Type", style="blue")
|
|
431
|
-
table.add_column("Error", style="red")
|
|
432
|
-
|
|
433
|
-
for file_path, errors in results.items():
|
|
434
|
-
for i, error in enumerate(errors):
|
|
435
|
-
file_display: str = file_path if i == 0 else ""
|
|
436
|
-
|
|
437
|
-
# Format error message with improved formatting
|
|
438
|
-
formatted_error_message: str = _format_error_messages(error.message)
|
|
439
|
-
|
|
440
|
-
table.add_row(
|
|
441
|
-
file_display,
|
|
442
|
-
str(error.line_number) if error.line_number > 0 else "",
|
|
443
|
-
error.item_name,
|
|
444
|
-
error.item_type,
|
|
445
|
-
formatted_error_message,
|
|
446
|
-
)
|
|
447
|
-
console.print(table)
|
|
458
|
+
console.print(_red(f"{NEW_LINE}Found {error_stats['total_errors']} error(s) in {functions_text} over {files_text}"))
|
|
448
459
|
|
|
460
|
+
|
|
461
|
+
def _display_table_output(results: dict[str, list[DocstringError]]) -> None:
|
|
462
|
+
"""
|
|
463
|
+
!!! note "Summary"
|
|
464
|
+
Display results in table format.
|
|
465
|
+
|
|
466
|
+
Params:
|
|
467
|
+
results (dict[str, list[DocstringError]]):
|
|
468
|
+
Dictionary mapping file paths to lists of errors.
|
|
469
|
+
"""
|
|
470
|
+
table = Table(show_header=True, header_style="bold magenta")
|
|
471
|
+
table.add_column("File", style="cyan", no_wrap=False)
|
|
472
|
+
table.add_column("Line", justify="right", style="white")
|
|
473
|
+
table.add_column("Item", style="yellow")
|
|
474
|
+
table.add_column("Type", style="blue")
|
|
475
|
+
table.add_column("Error", style="red")
|
|
476
|
+
|
|
477
|
+
for file_path, errors in results.items():
|
|
478
|
+
for i, error in enumerate(errors):
|
|
479
|
+
file_display = file_path if i == 0 else ""
|
|
480
|
+
formatted_error_message = _format_error_messages(error.message)
|
|
481
|
+
|
|
482
|
+
table.add_row(
|
|
483
|
+
file_display,
|
|
484
|
+
str(error.line_number) if error.line_number > 0 else "",
|
|
485
|
+
error.item_name,
|
|
486
|
+
error.item_type,
|
|
487
|
+
formatted_error_message,
|
|
488
|
+
)
|
|
489
|
+
console.print(table)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
def _create_error_header(error: DocstringError) -> str:
|
|
493
|
+
"""
|
|
494
|
+
!!! note "Summary"
|
|
495
|
+
Create formatted header for a single error.
|
|
496
|
+
|
|
497
|
+
Params:
|
|
498
|
+
error (DocstringError):
|
|
499
|
+
The error to create a header for.
|
|
500
|
+
|
|
501
|
+
Returns:
|
|
502
|
+
(str):
|
|
503
|
+
Formatted header string with line number, item type, and name.
|
|
504
|
+
"""
|
|
505
|
+
if error.line_number > 0:
|
|
506
|
+
return f" [red]Line {error.line_number}[/red] - {error.item_type} '{error.item_name}':"
|
|
449
507
|
else:
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
console.print(f"{NEW_LINE}{_cyan(file_path)}")
|
|
453
|
-
for error in errors:
|
|
454
|
-
# Print the header line with line number, item type and name
|
|
455
|
-
if error.line_number > 0:
|
|
456
|
-
console.print(f" [red]Line {error.line_number}[/red] - {error.item_type} '{error.item_name}':")
|
|
457
|
-
else:
|
|
458
|
-
console.print(f" {_red('Error')} - {error.item_type} '{error.item_name}':")
|
|
459
|
-
|
|
460
|
-
# Split error message into individual errors and indent them
|
|
461
|
-
if "; " in error.message:
|
|
462
|
-
individual_errors = [msg.strip() for msg in error.message.split("; ") if msg.strip()]
|
|
463
|
-
for individual_error in individual_errors:
|
|
464
|
-
console.print(f" - {individual_error}")
|
|
465
|
-
else:
|
|
466
|
-
# Single error message
|
|
467
|
-
console.print(f" - {error.message.strip()}")
|
|
468
|
-
|
|
469
|
-
# Summary - more descriptive message
|
|
470
|
-
if total_functions == 1:
|
|
471
|
-
functions_text = f"1 function"
|
|
472
|
-
else:
|
|
473
|
-
functions_text = f"{total_functions} functions"
|
|
508
|
+
return f" {_red('Error')} - {error.item_type} '{error.item_name}':"
|
|
509
|
+
|
|
474
510
|
|
|
475
|
-
|
|
476
|
-
|
|
511
|
+
def _split_error_messages(message: str) -> list[str]:
|
|
512
|
+
"""
|
|
513
|
+
!!! note "Summary"
|
|
514
|
+
Split compound error message into individual messages.
|
|
515
|
+
|
|
516
|
+
Params:
|
|
517
|
+
message (str):
|
|
518
|
+
The error message to split.
|
|
519
|
+
|
|
520
|
+
Returns:
|
|
521
|
+
(list[str]):
|
|
522
|
+
List of individual error messages.
|
|
523
|
+
"""
|
|
524
|
+
if "; " in message:
|
|
525
|
+
return [msg.strip() for msg in message.split("; ") if msg.strip()]
|
|
477
526
|
else:
|
|
478
|
-
|
|
527
|
+
return [message.strip()]
|
|
479
528
|
|
|
480
|
-
console.print(_red(f"{NEW_LINE}Found {total_errors} error(s) in {functions_text} over {files_text}"))
|
|
481
529
|
|
|
482
|
-
|
|
530
|
+
def _format_error_output(error: DocstringError) -> list[str]:
|
|
531
|
+
"""
|
|
532
|
+
!!! note "Summary"
|
|
533
|
+
Format single error for display output.
|
|
534
|
+
|
|
535
|
+
Params:
|
|
536
|
+
error (DocstringError):
|
|
537
|
+
The error to format.
|
|
538
|
+
|
|
539
|
+
Returns:
|
|
540
|
+
(list[str]):
|
|
541
|
+
List of formatted lines to print.
|
|
542
|
+
"""
|
|
543
|
+
lines: list[str] = [_create_error_header(error)]
|
|
544
|
+
individual_errors: list[str] = _split_error_messages(error.message)
|
|
545
|
+
for individual_error in individual_errors:
|
|
546
|
+
lines.append(f" - {individual_error}")
|
|
547
|
+
return lines
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def _display_list_output(results: dict[str, list[DocstringError]]) -> None:
|
|
551
|
+
"""
|
|
552
|
+
!!! note "Summary"
|
|
553
|
+
Display results in list format.
|
|
554
|
+
|
|
555
|
+
Params:
|
|
556
|
+
results (dict[str, list[DocstringError]]):
|
|
557
|
+
Dictionary mapping file paths to lists of errors.
|
|
558
|
+
"""
|
|
559
|
+
for file_path, errors in results.items():
|
|
560
|
+
console.print(f"{NEW_LINE}{_cyan(file_path)}")
|
|
561
|
+
for error in errors:
|
|
562
|
+
output_lines: list[str] = _format_error_output(error)
|
|
563
|
+
for line in output_lines:
|
|
564
|
+
console.print(line)
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def _display_final_summary(error_stats: dict[str, int]) -> None:
|
|
568
|
+
"""
|
|
569
|
+
!!! note "Summary"
|
|
570
|
+
Display the final summary line.
|
|
571
|
+
|
|
572
|
+
Params:
|
|
573
|
+
error_stats (dict[str, int]):
|
|
574
|
+
Dictionary containing total_errors, total_functions, and total_files.
|
|
575
|
+
"""
|
|
576
|
+
functions_text: str = (
|
|
577
|
+
"1 function" if error_stats["total_functions"] == 1 else f"{error_stats['total_functions']} functions"
|
|
578
|
+
)
|
|
579
|
+
files_text: str = "1 file" if error_stats["total_files"] == 1 else f"{error_stats['total_files']} files"
|
|
580
|
+
|
|
581
|
+
console.print(_red(f"{NEW_LINE}Found {error_stats['total_errors']} error(s) in {functions_text} over {files_text}"))
|
|
483
582
|
|
|
484
583
|
|
|
485
584
|
# ---------------------------------------------------------------------------- #
|
|
@@ -525,45 +624,153 @@ def check_docstrings(
|
|
|
525
624
|
(None):
|
|
526
625
|
Nothing is returned.
|
|
527
626
|
"""
|
|
627
|
+
# Validate and process input paths
|
|
628
|
+
target_paths: list[Path] = _validate_and_process_paths(paths)
|
|
629
|
+
|
|
630
|
+
# Load and validate configuration
|
|
631
|
+
config_obj: Config = _load_and_validate_config(config, target_paths)
|
|
528
632
|
|
|
529
|
-
#
|
|
633
|
+
# Initialize checker and process all paths
|
|
634
|
+
checker = DocstringChecker(config_obj)
|
|
635
|
+
all_results: dict[str, list[DocstringError]] = _process_all_paths(checker, target_paths, exclude)
|
|
636
|
+
|
|
637
|
+
# Display results and handle exit
|
|
638
|
+
exit_code: int = _display_results(all_results, quiet, output, check)
|
|
639
|
+
if exit_code != 0:
|
|
640
|
+
raise Exit(exit_code)
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def _validate_and_process_paths(paths: list[str]) -> list[Path]:
|
|
644
|
+
"""
|
|
645
|
+
!!! note "Summary"
|
|
646
|
+
Validate input paths and return valid paths.
|
|
647
|
+
|
|
648
|
+
Params:
|
|
649
|
+
paths (list[str]):
|
|
650
|
+
List of path strings to validate.
|
|
651
|
+
|
|
652
|
+
Raises:
|
|
653
|
+
(Exit):
|
|
654
|
+
If any paths do not exist.
|
|
655
|
+
|
|
656
|
+
Returns:
|
|
657
|
+
(list[Path]):
|
|
658
|
+
List of valid Path objects.
|
|
659
|
+
"""
|
|
530
660
|
path_objs: list[Path] = [Path(path) for path in paths]
|
|
531
661
|
target_paths: list[Path] = [p for p in path_objs if p.exists()]
|
|
532
662
|
invalid_paths: list[Path] = [p for p in path_objs if not p.exists()]
|
|
533
663
|
|
|
534
|
-
if
|
|
664
|
+
if invalid_paths:
|
|
535
665
|
console.print(
|
|
536
|
-
_red(
|
|
666
|
+
_red("[bold]Error: Paths do not exist:[/bold]"),
|
|
537
667
|
NEW_LINE,
|
|
538
668
|
NEW_LINE.join([f"- '{invalid_path}'" for invalid_path in invalid_paths]),
|
|
539
669
|
)
|
|
540
670
|
raise Exit(1)
|
|
541
671
|
|
|
542
|
-
|
|
672
|
+
return target_paths
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
def _load_and_validate_config(config: Optional[str], target_paths: list[Path]) -> Config:
|
|
676
|
+
"""
|
|
677
|
+
!!! note "Summary"
|
|
678
|
+
Load and validate configuration from file or auto-discovery.
|
|
679
|
+
|
|
680
|
+
Params:
|
|
681
|
+
config (Optional[str]):
|
|
682
|
+
Optional path to configuration file.
|
|
683
|
+
target_paths (list[Path]):
|
|
684
|
+
List of target paths for auto-discovery.
|
|
685
|
+
|
|
686
|
+
Raises:
|
|
687
|
+
(Exit):
|
|
688
|
+
If configuration loading fails.
|
|
689
|
+
|
|
690
|
+
Returns:
|
|
691
|
+
(Config):
|
|
692
|
+
Loaded configuration object.
|
|
693
|
+
"""
|
|
543
694
|
try:
|
|
544
695
|
if config:
|
|
545
|
-
|
|
546
|
-
if not config_path.exists():
|
|
547
|
-
console.print(_red(f"Error: Configuration file does not exist: {config}"))
|
|
548
|
-
raise Exit(1)
|
|
549
|
-
config_obj = load_config(config_path)
|
|
696
|
+
return _load_explicit_config(config)
|
|
550
697
|
else:
|
|
551
|
-
|
|
552
|
-
first_path: Path = target_paths[0]
|
|
553
|
-
found_config: Optional[Path] = find_config_file(first_path if first_path.is_dir() else first_path.parent)
|
|
554
|
-
if found_config:
|
|
555
|
-
config_obj: Config = load_config(found_config)
|
|
556
|
-
else:
|
|
557
|
-
config_obj: Config = load_config()
|
|
558
|
-
|
|
698
|
+
return _load_auto_discovered_config(target_paths)
|
|
559
699
|
except Exception as e:
|
|
560
700
|
console.print(_red(f"Error loading configuration: {e}"))
|
|
701
|
+
raise Exit(1) from e
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
def _load_explicit_config(config: str) -> Config:
|
|
705
|
+
"""
|
|
706
|
+
!!! note "Summary"
|
|
707
|
+
Load configuration from explicitly specified path.
|
|
708
|
+
|
|
709
|
+
Params:
|
|
710
|
+
config (str):
|
|
711
|
+
Path to configuration file.
|
|
712
|
+
|
|
713
|
+
Raises:
|
|
714
|
+
(Exit):
|
|
715
|
+
If configuration file does not exist.
|
|
716
|
+
|
|
717
|
+
Returns:
|
|
718
|
+
(Config):
|
|
719
|
+
Loaded configuration object.
|
|
720
|
+
"""
|
|
721
|
+
config_path = Path(config)
|
|
722
|
+
if not config_path.exists():
|
|
723
|
+
console.print(_red(f"Error: Configuration file does not exist: {config}"))
|
|
561
724
|
raise Exit(1)
|
|
725
|
+
return load_config(config_path)
|
|
562
726
|
|
|
563
|
-
# Initialize checker
|
|
564
|
-
checker = DocstringChecker(config_obj)
|
|
565
727
|
|
|
566
|
-
|
|
728
|
+
def _load_auto_discovered_config(target_paths: list[Path]) -> Config:
|
|
729
|
+
"""
|
|
730
|
+
!!! note "Summary"
|
|
731
|
+
Load configuration from auto-discovery or defaults.
|
|
732
|
+
|
|
733
|
+
Params:
|
|
734
|
+
target_paths (list[Path]):
|
|
735
|
+
List of target paths to search for configuration.
|
|
736
|
+
|
|
737
|
+
Returns:
|
|
738
|
+
(Config):
|
|
739
|
+
Loaded configuration object from found config or defaults.
|
|
740
|
+
"""
|
|
741
|
+
first_path: Path = target_paths[0]
|
|
742
|
+
search_path: Path = first_path if first_path.is_dir() else first_path.parent
|
|
743
|
+
found_config: Optional[Path] = find_config_file(search_path)
|
|
744
|
+
|
|
745
|
+
if found_config:
|
|
746
|
+
return load_config(found_config)
|
|
747
|
+
else:
|
|
748
|
+
return load_config()
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
def _process_all_paths(
|
|
752
|
+
checker: DocstringChecker, target_paths: list[Path], exclude: Optional[list[str]]
|
|
753
|
+
) -> dict[str, list[DocstringError]]:
|
|
754
|
+
"""
|
|
755
|
+
!!! note "Summary"
|
|
756
|
+
Process all target paths and collect docstring errors.
|
|
757
|
+
|
|
758
|
+
Params:
|
|
759
|
+
checker (DocstringChecker):
|
|
760
|
+
The checker instance to use.
|
|
761
|
+
target_paths (list[Path]):
|
|
762
|
+
List of paths to check (files or directories).
|
|
763
|
+
exclude (Optional[list[str]]):
|
|
764
|
+
Optional list of exclusion patterns.
|
|
765
|
+
|
|
766
|
+
Raises:
|
|
767
|
+
(Exit):
|
|
768
|
+
If an error occurs during checking.
|
|
769
|
+
|
|
770
|
+
Returns:
|
|
771
|
+
(dict[str, list[DocstringError]]):
|
|
772
|
+
Dictionary mapping file paths to lists of errors.
|
|
773
|
+
"""
|
|
567
774
|
all_results: dict[str, list[DocstringError]] = {}
|
|
568
775
|
|
|
569
776
|
try:
|
|
@@ -577,17 +784,11 @@ def check_docstrings(
|
|
|
577
784
|
target_path, exclude_patterns=exclude
|
|
578
785
|
)
|
|
579
786
|
all_results.update(directory_results)
|
|
580
|
-
|
|
581
787
|
except Exception as e:
|
|
582
788
|
console.print(_red(f"Error during checking: {e}"))
|
|
583
|
-
raise Exit(1)
|
|
789
|
+
raise Exit(1) from e
|
|
584
790
|
|
|
585
|
-
|
|
586
|
-
exit_code: int = _display_results(all_results, quiet, output, check)
|
|
587
|
-
|
|
588
|
-
# Always exit with error code if issues are found, regardless of check flag
|
|
589
|
-
if exit_code != 0:
|
|
590
|
-
raise Exit(exit_code)
|
|
791
|
+
return all_results
|
|
591
792
|
|
|
592
793
|
|
|
593
794
|
# ---------------------------------------------------------------------------- #
|