Automatically add placeholder documentation to non-compliant functions
DocmentsCheckResult.needs_fixing
def needs_fixing()->bool: # Whether the definition needs fixing
Check if this definition needs any fixing
DocmentsCheckResult.get_param_name
def get_param_name( param_str:str, # Parameter string (e.g., "x: int" or "y=10"))->str: # Extracted parameter name
Extract parameter name from a parameter string
DocmentsCheckResult.needs_param_fix
def needs_param_fix( param_name:str, # Name of the parameter to check)->bool: # Whether the parameter needs fixing
Check if a parameter needs documentation or type hint fixes
find_signature_boundaries
def find_signature_boundaries( lines:List, # Source code lines)->tuple: # (def_line_idx, sig_end_idx) or (-1, -1) if not found
Find the start and end lines of a function signature
split_parameters
def split_parameters( params_str:str, # Parameter string from function signature)->List: # List of individual parameter strings
Split a parameter string into individual parameters, handling nested types
parse_single_line_signature
def parse_single_line_signature( sig_line:str, # Single-line function signature)->dict: # Parsed components of the signature
Parse a single-line function signature into its components
generate_param_todo_comment
def generate_param_todo_comment( param_name:str, # Parameter name result:DocmentsCheckResult, # Check result with type hint and doc info existing_comment:str='', # Existing comment text (without #))->str: # TODO comment to add
Generate appropriate TODO comment for a parameter based on what’s missing
generate_return_todo_comment
def generate_return_todo_comment( result:DocmentsCheckResult, # Check result with type hint and doc info existing_comment:str='', # Existing comment text (without #))->str: # TODO comment to add
Generate appropriate TODO comment for return value based on what’s missing
build_fixed_single_line_function
def build_fixed_single_line_function( parsed:dict, # Parsed signature components params:List, # Individual parameter strings result:DocmentsCheckResult, # Check result with missing params info)->List: # Lines of fixed function signature
Build a fixed single-line function with documentation comments
fix_multi_line_signature
def fix_multi_line_signature( lines:List, # All source lines def_line_idx:int, # Start of function definition sig_end_idx:int, # End of function signature result:DocmentsCheckResult, # Check result with missing params info)->List: # Fixed lines for the signature portion
Fix a multi-line function signature by adding parameter comments
fix_class_definition
def fix_class_definition( result:DocmentsCheckResult, # Check result with non-compliant class)->str: # Fixed source code with class docstring
Fix a class definition by adding a docstring if missing
insert_function_docstring
def insert_function_docstring( lines:List, # Fixed function lines def_line_idx:int, # Index of function definition line indent:str, # Base indentation for the function)->List: # Lines with docstring inserted
Insert a TODO docstring after the function signature
fix_single_line_function
def fix_single_line_function( lines:List, # All source lines def_line_idx:int, # Index of function definition line result:DocmentsCheckResult, # Check result with missing params info)->List: # Fixed lines for the function
Fix a single-line function signature by converting to multi-line with parameter comments
fix_multi_line_function
def fix_multi_line_function( lines:List, # All source lines def_line_idx:int, # Start of function definition sig_end_idx:int, # End of function signature result:DocmentsCheckResult, # Check result with missing params info)->List: # Fixed lines for the function
Fix a multi-line function signature by adding parameter comments
generate_fixed_source
def generate_fixed_source( result:DocmentsCheckResult, # Check result with non-compliant function)->str: # Fixed source code with placeholder documentation
Generate fixed source code for a non-compliant function or class
fix_notebook
def fix_notebook( nb_path:Path, # Path to notebook to fix dry_run:bool=False, # If True, show changes without saving)->Dict: # Summary of changes made
Fix non-compliant functions in a notebook by adding placeholder documentation
def extract_docstring_info( source:str, # Function source code name:str, # Function name)->Optional: # Extracted docstring information or None
Extract docstring information from function source code
convert_to_docments_format
def convert_to_docments_format( source:str, # Original function source code docstring_info:DocstringInfo, # Extracted docstring information result:DocmentsCheckResult, # Check result with missing params info)->str: # Converted source code in docments format
Convert function source to docments format using extracted docstring info
convert_single_line_to_docments
def convert_single_line_to_docments( sig_line:str, # Single-line function signature docstring_info:DocstringInfo, # Extracted docstring information result:DocmentsCheckResult, # Check result with missing params info)->List: # Multi-line signature with docments comments
Convert single-line function signature to multi-line docments format
convert_multiline_to_docments
def convert_multiline_to_docments( sig_lines:List, # Multi-line function signature docstring_info:DocstringInfo, # Extracted docstring information result:DocmentsCheckResult, # Check result with missing params info)->List: # Multi-line signature with docments comments
Convert multi-line function signature to docments format
replace_docstring_in_body
def replace_docstring_in_body( body_lines:List, # Function body lines description:str, # New description to use def_line:str, # Function definition line for indentation)->List: # Modified body lines
Replace the docstring in function body with a simple description
generate_fixed_source_with_conversion
def generate_fixed_source_with_conversion( result:DocmentsCheckResult, # Check result with non-compliant function)->str: # Fixed source code with converted documentation
Generate fixed source code, converting existing docstrings to docments format if possible
fix_notebook_with_conversion
def fix_notebook_with_conversion( nb_path:Path, # Path to notebook to fix dry_run:bool=False, # If True, show changes without saving convert_docstrings:bool=True, # If True, convert existing docstrings to docments format)->Dict: # Summary of changes made
Fix non-compliant functions in a notebook, optionally converting docstrings to docments format
Testing
def test_docstring_detection_and_parsing():"""Test docstring style detection and parsing for all supported formats"""print("🧪 Testing Docstring Detection and Parsing")print("="*50)# Test docstrings for different styles test_docstrings = [# Google style ('google', '''"""Calculate the sum of two numbers. Args: x (int): The first number to add y (int): The second number to add Returns: int: The sum of x and y """'''),# NumPy style ('numpy', '''"""Calculate the sum of two numbers. Parameters ---------- x : int The first number to add y : int The second number to add Returns ------- int The sum of x and y """'''),# Sphinx style ('sphinx', '''"""Calculate the sum of two numbers. :param x: The first number to add :param y: The second number to add :returns: The sum of x and y """'''),# Unknown style ('unknown', '''"""Just a simple description without structured parameters."""''') ]# Test detectionprint("📋 Style Detection Results:")for expected_style, docstring in test_docstrings: detected_style = detect_docstring_style(docstring) status ="✅"if detected_style == expected_style else"❌"print(f"{status}{expected_style.title()}: {detected_style}")# Test parsing for structured formatsprint("\n📖 Parsing Results:")for style_name, docstring in test_docstrings[:3]: # Skip unknown styleif style_name =='google': parsed = parse_google_docstring(docstring)elif style_name =='numpy': parsed = parse_numpy_docstring(docstring)elif style_name =='sphinx': parsed = parse_sphinx_docstring(docstring)print(f"\n{style_name.title()} parsing:")print(f" Description: {parsed.description}")print(f" Parameters: {list(parsed.params.keys())}")print(f" Returns: {'Yes'if parsed.returns else'No'}")print("\n✅ Docstring detection and parsing tests completed")# Run testtest_docstring_detection_and_parsing()
🧪 Testing Docstring Detection and Parsing
==================================================
📋 Style Detection Results:
✅ Google: google
✅ Numpy: numpy
✅ Sphinx: sphinx
✅ Unknown: unknown
📖 Parsing Results:
Google parsing:
Description: Calculate the sum of two numbers.
Parameters: ['x', 'y']
Returns: Yes
Numpy parsing:
Description: Calculate the sum of two numbers.
Parameters: ['x', 'y']
Returns: Yes
Sphinx parsing:
Description: """Calculate the sum of two numbers. """
Parameters: ['x', 'y']
Returns: Yes
✅ Docstring detection and parsing tests completed
def test_function_fixing():"""Test basic function fixing for various scenarios"""print("\n🔧 Testing Function Fixing")print("="*50)# Test cases with different compliance issues test_cases = [ {'name': 'missing_all_docs','source': '''def bad_function(x, y, z=10): result = x + y + z return result''','args': [ {'name': 'x', 'annotation': None}, {'name': 'y', 'annotation': None}, {'name': 'z', 'annotation': None} ],'returns': None,'description': 'Missing all documentation and type hints' }, {'name': 'typed_function','source': '''def typed_function(name: str, age: int) -> str: return f"{name} is {age} years old"''','args': [ {'name': 'name', 'annotation': 'str'}, {'name': 'age', 'annotation': 'int'} ],'returns': 'str','description': 'Has type hints but missing parameter documentation' }, {'name': 'partially_documented','source': '''def get_export_cells( nb_path: Path, # Path to the notebook file fake_test_path: Path ) -> List[Dict[str, Any]]: # List of cells with export directives """Extract all code cells from a notebook that have export directives""" nb = read_nb(nb_path) return []''','args': [ {'name': 'nb_path', 'annotation': 'Path'}, {'name': 'fake_test_path', 'annotation': 'Path'} ],'returns': 'List[Dict[str, Any]]','description': 'Partially documented - missing one parameter doc' } ]for test_case in test_cases:print(f"\n📝 Testing: {test_case['description']}")print(f"Function: {test_case['name']}")print(f"\nSource:\n{test_case['source']}\n")# Create test definition test_def = {'name': test_case['name'],'type': 'FunctionDef','source': test_case['source'],'notebook': 'test.ipynb','args': test_case['args'],'returns': test_case['returns'] }# Check compliance result = check_definition(test_def)print(f"Has docstring: {result.has_docstring}")print(f"Is compliant: {result.is_compliant}")ifnot result.is_compliant:print(f"Missing: {result.missing_params}")# Apply fix fixed_source = generate_fixed_source(result)print(f"\nFixed Source:\n{fixed_source}\n")print("\n✅ Function fixing tests completed")# Run testtest_function_fixing()
🔧 Testing Function Fixing
==================================================
📝 Testing: Missing all documentation and type hints
Function: missing_all_docs
Source:
def bad_function(x, y, z=10):
result = x + y + z
return result
Has docstring: False
Is compliant: False
Missing: ['x', 'y', 'z']
Fixed Source:
def bad_function(
x, # TODO: Add type hint and description
y, # TODO: Add type hint and description
z=10 # TODO: Add type hint and description
):
"""TODO: Add function description"""
result = x + y + z
return result
📝 Testing: Has type hints but missing parameter documentation
Function: typed_function
Source:
def typed_function(name: str, age: int) -> str:
return f"{name} is {age} years old"
Has docstring: False
Is compliant: False
Missing: ['name', 'age', 'return']
Fixed Source:
def typed_function(
name: str, # TODO: Add description
age: int # TODO: Add description
) -> str: # TODO: Add return description
"""TODO: Add function description"""
return f"{name} is {age} years old"
📝 Testing: Partially documented - missing one parameter doc
Function: partially_documented
Source:
def get_export_cells(
nb_path: Path, # Path to the notebook file
fake_test_path: Path
) -> List[Dict[str, Any]]: # List of cells with export directives
"""Extract all code cells from a notebook that have export directives"""
nb = read_nb(nb_path)
return []
Has docstring: False
Is compliant: False
Missing: ['fake_test_path']
Fixed Source:
def get_export_cells(
nb_path: Path, # Path to the notebook file
fake_test_path: Path # TODO: Add description
) -> List[Dict[str, Any]]: # List of cells with export directives
"""Extract all code cells from a notebook that have export directives"""
nb = read_nb(nb_path)
return []
✅ Function fixing tests completed
def test_docstring_conversion():"""Test conversion from various docstring formats to docments style"""print("\n🔄 Testing Docstring Conversion")print("="*50)# Test functions with different docstring formats test_functions = [ {'name': 'google_example','source': '''def google_example(name: str, age: int, active: bool = True) -> str: """Generate a user profile string. Args: name (str): The user's full name age (int): The user's age in years active (bool): Whether the user is currently active Returns: str: A formatted profile string """ return f"{name} ({age}) - {'Active' if active else 'Inactive'}"''','args': [ {'name': 'name', 'annotation': 'str'}, {'name': 'age', 'annotation': 'int'}, {'name': 'active', 'annotation': 'bool'} ],'returns': 'str','style': 'Google' }, {'name': 'numpy_example','source': '''def numpy_example(data: list, threshold: float = 0.5) -> dict: """Process data based on threshold. Parameters ---------- data : list Input data to process threshold : float Minimum threshold value Returns ------- dict Processing results with statistics """ return {'processed': len(data), 'threshold': threshold}''','args': [ {'name': 'data', 'annotation': 'list'}, {'name': 'threshold', 'annotation': 'float'} ],'returns': 'dict','style': 'NumPy' } ]for func_info in test_functions:print(f"\n📝 Testing {func_info['style']} Style Conversion")print(f"Function: {func_info['name']}")print(f"\nSource:\n{func_info['source']}\n")# Create test definition test_def = {'name': func_info['name'],'type': 'FunctionDef','source': func_info['source'],'notebook': 'test.ipynb','args': func_info['args'],'returns': func_info['returns'] }# Check original compliance result = check_definition(test_def)# Extract and verify docstring info docstring_info = extract_docstring_info(result.source, result.name)if docstring_info:print(f"Docstring type: {docstring_info.docstring_type}")print(f"Parameters found: {list(docstring_info.params.keys())}")print(f"Return info: {'Yes'if docstring_info.returns else'No'}")# Convert to docments format converted = generate_fixed_source_with_conversion(result)print(f"\nConverted Source:\n{converted}\n")# Verify converted version is compliant test_def_converted = test_def.copy() test_def_converted['source'] = converted result_converted = check_definition(test_def_converted)print("\n✅ Docstring conversion tests completed")# Run testtest_docstring_conversion()
🔄 Testing Docstring Conversion
==================================================
📝 Testing Google Style Conversion
Function: google_example
Source:
def google_example(name: str, age: int, active: bool = True) -> str:
"""Generate a user profile string.
Args:
name (str): The user's full name
age (int): The user's age in years
active (bool): Whether the user is currently active
Returns:
str: A formatted profile string
"""
return f"{name} ({age}) - {'Active' if active else 'Inactive'}"
Docstring type: google
Parameters found: ['name', 'age', 'active']
Return info: Yes
Converted Source:
def google_example(
name: str, # The user's full name
age: int, # The user's age in years
active: bool = True # Whether the user is currently active
) -> str: # str: A formatted profile string
"""Generate a user profile string."""
return f"{name} ({age}) - {'Active' if active else 'Inactive'}"
📝 Testing NumPy Style Conversion
Function: numpy_example
Source:
def numpy_example(data: list, threshold: float = 0.5) -> dict:
"""Process data based on threshold.
Parameters
----------
data : list
Input data to process
threshold : float
Minimum threshold value
Returns
-------
dict
Processing results with statistics
"""
return {'processed': len(data), 'threshold': threshold}
Docstring type: numpy
Parameters found: ['data', 'threshold']
Return info: Yes
Converted Source:
def numpy_example(
data: list, # Input data to process
threshold: float = 0.5 # Minimum threshold value
) -> dict: # dict Processing results with statistics
"""Process data based on threshold."""
return {'processed': len(data), 'threshold': threshold}
✅ Docstring conversion tests completed