docstring-format-checker 0.5.0__tar.gz → 0.6.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.5.0 → docstring_format_checker-0.6.0}/PKG-INFO +1 -1
- {docstring_format_checker-0.5.0 → docstring_format_checker-0.6.0}/pyproject.toml +1 -1
- {docstring_format_checker-0.5.0 → docstring_format_checker-0.6.0}/src/docstring_format_checker/__init__.py +1 -1
- {docstring_format_checker-0.5.0 → docstring_format_checker-0.6.0}/src/docstring_format_checker/cli.py +66 -17
- {docstring_format_checker-0.5.0 → docstring_format_checker-0.6.0}/src/docstring_format_checker/core.py +32 -0
- {docstring_format_checker-0.5.0 → docstring_format_checker-0.6.0}/README.md +0 -0
- {docstring_format_checker-0.5.0 → docstring_format_checker-0.6.0}/src/docstring_format_checker/config.py +0 -0
- {docstring_format_checker-0.5.0 → docstring_format_checker-0.6.0}/src/docstring_format_checker/utils/__init__.py +0 -0
- {docstring_format_checker-0.5.0 → docstring_format_checker-0.6.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.6.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.6.0"
|
|
8
8
|
__author__ = "Chris Mahoney"
|
|
9
9
|
__email__ = "docstring-format-checker@data-science-extensions.com"
|
|
10
10
|
|
|
@@ -376,13 +376,36 @@ def _display_results(results: dict[str, list[DocstringError]], quiet: bool, outp
|
|
|
376
376
|
console.print(_green("✓ All docstrings are valid!"))
|
|
377
377
|
return 0
|
|
378
378
|
|
|
379
|
-
# Count total errors
|
|
380
|
-
|
|
379
|
+
# Count total errors (individual error messages, not error objects)
|
|
380
|
+
total_individual_errors: int = 0
|
|
381
|
+
total_functions: int = 0
|
|
382
|
+
|
|
383
|
+
for errors in results.values():
|
|
384
|
+
total_functions += len(errors) # Count functions/items with errors
|
|
385
|
+
for error in errors:
|
|
386
|
+
# Count individual error messages within each error object
|
|
387
|
+
if "; " in error.message:
|
|
388
|
+
individual_errors = [msg.strip() for msg in error.message.split("; ") if msg.strip()]
|
|
389
|
+
total_individual_errors += len(individual_errors)
|
|
390
|
+
else:
|
|
391
|
+
total_individual_errors += 1
|
|
392
|
+
|
|
393
|
+
total_errors: int = total_individual_errors
|
|
381
394
|
total_files: int = len(results)
|
|
382
395
|
|
|
383
396
|
if quiet:
|
|
384
|
-
# In quiet mode, only show summary
|
|
385
|
-
|
|
397
|
+
# In quiet mode, only show summary with improved format
|
|
398
|
+
if total_functions == 1:
|
|
399
|
+
functions_text = f"1 function"
|
|
400
|
+
else:
|
|
401
|
+
functions_text = f"{total_functions} functions"
|
|
402
|
+
|
|
403
|
+
if total_files == 1:
|
|
404
|
+
files_text = f"1 file"
|
|
405
|
+
else:
|
|
406
|
+
files_text = f"{total_files} files"
|
|
407
|
+
|
|
408
|
+
console.print(_red(f"{NEW_LINE}Found {total_errors} error(s) in {functions_text} over {files_text}"))
|
|
386
409
|
return 1
|
|
387
410
|
|
|
388
411
|
if output == "table":
|
|
@@ -411,22 +434,43 @@ def _display_results(results: dict[str, list[DocstringError]], quiet: bool, outp
|
|
|
411
434
|
console.print(table)
|
|
412
435
|
|
|
413
436
|
else:
|
|
414
|
-
# Show compact output
|
|
437
|
+
# Show compact output - each individual error on its own line
|
|
415
438
|
for file_path, errors in results.items():
|
|
416
439
|
console.print(f"{NEW_LINE}{_cyan(file_path)}")
|
|
417
440
|
for error in errors:
|
|
418
|
-
#
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
441
|
+
# Split error message into individual errors for list mode
|
|
442
|
+
if "; " in error.message:
|
|
443
|
+
individual_errors = [msg.strip() for msg in error.message.split("; ") if msg.strip()]
|
|
444
|
+
for individual_error in individual_errors:
|
|
445
|
+
formatted_error = f"- {individual_error}"
|
|
446
|
+
if error.line_number > 0:
|
|
447
|
+
console.print(
|
|
448
|
+
f" [red]Line {error.line_number}[/red] - {error.item_type} '{error.item_name}': {formatted_error}"
|
|
449
|
+
)
|
|
450
|
+
else:
|
|
451
|
+
console.print(f" {_red('Error')}: {formatted_error}")
|
|
425
452
|
else:
|
|
426
|
-
|
|
453
|
+
# Single error message
|
|
454
|
+
formatted_error = f"- {error.message.strip()}"
|
|
455
|
+
if error.line_number > 0:
|
|
456
|
+
console.print(
|
|
457
|
+
f" [red]Line {error.line_number}[/red] - {error.item_type} '{error.item_name}': {formatted_error}"
|
|
458
|
+
)
|
|
459
|
+
else:
|
|
460
|
+
console.print(f" {_red('Error')}: {formatted_error}")
|
|
461
|
+
|
|
462
|
+
# Summary - more descriptive message
|
|
463
|
+
if total_functions == 1:
|
|
464
|
+
functions_text = f"1 function"
|
|
465
|
+
else:
|
|
466
|
+
functions_text = f"{total_functions} functions"
|
|
467
|
+
|
|
468
|
+
if total_files == 1:
|
|
469
|
+
files_text = f"1 file"
|
|
470
|
+
else:
|
|
471
|
+
files_text = f"{total_files} files"
|
|
427
472
|
|
|
428
|
-
|
|
429
|
-
console.print(_red(f"{NEW_LINE}Found {total_errors} error(s) in {total_files} file(s)"))
|
|
473
|
+
console.print(_red(f"{NEW_LINE}Found {total_errors} error(s) in {functions_text} over {files_text}"))
|
|
430
474
|
|
|
431
475
|
return 1
|
|
432
476
|
|
|
@@ -439,7 +483,7 @@ def _display_results(results: dict[str, list[DocstringError]], quiet: bool, outp
|
|
|
439
483
|
|
|
440
484
|
|
|
441
485
|
# This will be the default behavior when no command is specified
|
|
442
|
-
def
|
|
486
|
+
def check_docstrings(
|
|
443
487
|
path: str,
|
|
444
488
|
config: Optional[str] = None,
|
|
445
489
|
exclude: Optional[list[str]] = None,
|
|
@@ -456,14 +500,19 @@ def _check_docstrings(
|
|
|
456
500
|
The path to the file or directory to check.
|
|
457
501
|
config (Optional[str]):
|
|
458
502
|
The path to the configuration file.
|
|
503
|
+
Default: `None`.
|
|
459
504
|
exclude (Optional[list[str]]):
|
|
460
505
|
List of glob patterns to exclude from checking.
|
|
506
|
+
Default: `None`.
|
|
461
507
|
quiet (bool):
|
|
462
508
|
Whether to suppress output.
|
|
509
|
+
Default: `False`.
|
|
463
510
|
output (str):
|
|
464
511
|
Output format: 'table' or 'list'.
|
|
512
|
+
Default: `'list'`.
|
|
465
513
|
check (bool):
|
|
466
514
|
Whether to throw error if issues are found.
|
|
515
|
+
Default: `False`.
|
|
467
516
|
|
|
468
517
|
Returns:
|
|
469
518
|
(None):
|
|
@@ -626,7 +675,7 @@ def main(
|
|
|
626
675
|
console.print(_red(f"Error: Invalid output format '{output}'. Use 'table' or 'list'."))
|
|
627
676
|
raise Exit(1)
|
|
628
677
|
|
|
629
|
-
|
|
678
|
+
check_docstrings(
|
|
630
679
|
path=path,
|
|
631
680
|
config=config,
|
|
632
681
|
exclude=exclude,
|
|
@@ -962,9 +962,41 @@ class DocstringChecker:
|
|
|
962
962
|
if current_section and stripped_line and not stripped_line.startswith(("!", "?", "#")):
|
|
963
963
|
# Look for parameter/type definitions
|
|
964
964
|
if ":" in stripped_line:
|
|
965
|
+
# Skip description lines that start with common description words
|
|
966
|
+
description_prefixes = [
|
|
967
|
+
"default:",
|
|
968
|
+
"note:",
|
|
969
|
+
"example:",
|
|
970
|
+
"see:",
|
|
971
|
+
"warning:",
|
|
972
|
+
"info:",
|
|
973
|
+
"tip:",
|
|
974
|
+
"returns:",
|
|
975
|
+
]
|
|
976
|
+
is_description_line = any(
|
|
977
|
+
stripped_line.lower().startswith(prefix) for prefix in description_prefixes
|
|
978
|
+
)
|
|
979
|
+
|
|
980
|
+
# Skip lines that are clearly descriptions (containing "Default:", etc.)
|
|
981
|
+
if (
|
|
982
|
+
is_description_line
|
|
983
|
+
or "Default:" in stripped_line
|
|
984
|
+
or "Output format:" in stripped_line
|
|
985
|
+
or "Show examples:" in stripped_line
|
|
986
|
+
):
|
|
987
|
+
continue
|
|
988
|
+
|
|
965
989
|
# For list_name_and_type sections, check format like "name (type):" or "(type):"
|
|
966
990
|
if current_section.type == "list_name_and_type":
|
|
967
991
|
# Pattern: name (type): or (type):
|
|
992
|
+
# But skip if it doesn't look like a parameter definition (e.g., has multiple words before the colon)
|
|
993
|
+
colon_part = stripped_line.split(":")[0].strip()
|
|
994
|
+
# Skip if it contains phrases that indicate it's a description, not a parameter
|
|
995
|
+
if any(
|
|
996
|
+
word in colon_part.lower() for word in ["default", "output", "format", "show", "example"]
|
|
997
|
+
):
|
|
998
|
+
continue
|
|
999
|
+
|
|
968
1000
|
if not re.search(r"\([^)]+\):", stripped_line):
|
|
969
1001
|
errors.append(
|
|
970
1002
|
f"Section '{current_section.name}' (type: '{current_section.type}') requires "
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|