docstring-format-checker 1.6.1__tar.gz → 1.6.2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: docstring-format-checker
3
- Version: 1.6.1
3
+ Version: 1.6.2
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>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "docstring-format-checker"
3
- version = "1.6.1"
3
+ version = "1.6.2"
4
4
  description = "A CLI tool to check and validate Python docstring formatting and completeness"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -748,9 +748,10 @@ class DocstringChecker:
748
748
  section_name: str = section.name.lower()
749
749
 
750
750
  if section_name == "params" and isinstance(item.node, (ast.FunctionDef, ast.AsyncFunctionDef)):
751
- # Check params section exists and is properly formatted
752
- if not self._check_params_section(docstring, item.node):
753
- return "Missing or invalid Params section"
751
+ # Check params section exists and is properly formatted with detailed error reporting
752
+ is_valid, error_message = self._check_params_section_detailed(docstring, item.node)
753
+ if not is_valid:
754
+ return error_message
754
755
 
755
756
  # If validate_param_types is enabled, validate type annotations match
756
757
  if self.config.global_config.validate_param_types:
@@ -925,6 +926,113 @@ class DocstringChecker:
925
926
 
926
927
  return True
927
928
 
929
+ def _extract_documented_params(self, docstring: str) -> list[str]:
930
+ """
931
+ !!! note "Summary"
932
+ Extract parameter names from the Params section of a docstring.
933
+
934
+ Params:
935
+ docstring (str):
936
+ The docstring to parse.
937
+
938
+ Returns:
939
+ (list[str]):
940
+ List of parameter names found in the Params section.
941
+ """
942
+ documented_params: list[str] = []
943
+ param_pattern: str = r"^\s*(\w+)\s*\([^)]+\):"
944
+ lines: list[str] = docstring.split("\n")
945
+ in_params_section: bool = False
946
+
947
+ for line in lines:
948
+ # Check if we've entered the Params section
949
+ if "Params:" in line:
950
+ in_params_section = True
951
+ continue
952
+
953
+ # Check if we've left the Params section (next section starts)
954
+ if in_params_section and re.match(r"^[ ]{0,4}[A-Z]\w+:", line):
955
+ break
956
+
957
+ # Extract parameter name
958
+ if in_params_section:
959
+ match = re.match(param_pattern, line)
960
+ if match:
961
+ documented_params.append(match.group(1))
962
+
963
+ return documented_params
964
+
965
+ def _build_param_mismatch_error(self, missing_in_docstring: list[str], extra_in_docstring: list[str]) -> str:
966
+ """
967
+ !!! note "Summary"
968
+ Build detailed error message for parameter mismatches.
969
+
970
+ Params:
971
+ missing_in_docstring (list[str]):
972
+ Parameters in signature but not in docstring.
973
+ extra_in_docstring (list[str]):
974
+ Parameters in docstring but not in signature.
975
+
976
+ Returns:
977
+ (str):
978
+ Formatted error message.
979
+ """
980
+ error_parts: list[str] = []
981
+
982
+ if missing_in_docstring:
983
+ missing_str: str = "', '".join(missing_in_docstring)
984
+ error_parts.append(f" - In signature but not in docstring: '{missing_str}'")
985
+
986
+ if extra_in_docstring:
987
+ extra_str: str = "', '".join(extra_in_docstring)
988
+ error_parts.append(f" - In docstring but not in signature: '{extra_str}'")
989
+
990
+ return "Parameter mismatch:\n" + "\n".join(error_parts)
991
+
992
+ def _check_params_section_detailed(
993
+ self, docstring: str, node: Union[ast.FunctionDef, ast.AsyncFunctionDef]
994
+ ) -> tuple[bool, Optional[str]]:
995
+ """
996
+ !!! note "Summary"
997
+ Check if the Params section exists and documents all parameters, with detailed error reporting.
998
+
999
+ Params:
1000
+ docstring (str):
1001
+ The docstring to check.
1002
+ node (Union[ast.FunctionDef, ast.AsyncFunctionDef]):
1003
+ The function node to check.
1004
+
1005
+ Returns:
1006
+ (tuple[bool, Optional[str]]):
1007
+ Tuple of (is_valid, error_message). If valid, error_message is None.
1008
+ """
1009
+
1010
+ # Get function parameters (excluding 'self' and 'cls' for methods)
1011
+ signature_params: list[str] = [arg.arg for arg in node.args.args if arg.arg not in ("self", "cls")]
1012
+
1013
+ if not signature_params:
1014
+ return (True, None) # No parameters to document
1015
+
1016
+ # Check if Params section exists
1017
+ if not re.search(r"Params:", docstring):
1018
+ return (False, "Params section not found in docstring")
1019
+
1020
+ # Extract documented parameters from docstring
1021
+ documented_params: list[str] = self._extract_documented_params(docstring)
1022
+
1023
+ # Find parameters in signature but not in docstring
1024
+ missing_in_docstring: list[str] = [p for p in signature_params if p not in documented_params]
1025
+
1026
+ # Find parameters in docstring but not in signature
1027
+ extra_in_docstring: list[str] = [p for p in documented_params if p not in signature_params]
1028
+
1029
+ # Build detailed error message if there are mismatches
1030
+ if missing_in_docstring or extra_in_docstring:
1031
+ error_message: str = self._build_param_mismatch_error(missing_in_docstring, extra_in_docstring)
1032
+ return (False, error_message)
1033
+
1034
+ return (True, None)
1035
+
928
1036
  def _extract_param_types(self, node: Union[ast.FunctionDef, ast.AsyncFunctionDef]) -> dict[str, str]:
929
1037
  """
930
1038
  !!! note "Summary"