Metadata-Version: 2.4
Name: cjm-nbdev-docments
Version: 0.0.2
Summary: Tool for validating and updating documentation in nbdev projects for use with the fastcore.docments module.
Home-page: https://github.com/cj-mills/cjm-nbdev-docments
Author: Christian J. Mills
Author-email: 9126128+cj-mills@users.noreply.github.com
License: Apache Software License 2.0
Keywords: nbdev jupyter notebook python
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: English
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: License :: OSI Approved :: Apache Software License
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastcore
Requires-Dist: nbdev
Requires-Dist: execnb
Provides-Extra: dev
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: keywords
Dynamic: license
Dynamic: license-file
Dynamic: provides-extra
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# cjm-nbdev-docments


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

## Overview

`cjm-nbdev-docments` helps nbdev users adopt and maintain the
[fastcore.docments](https://fastcore.fast.ai/docments.html)
documentation style. Instead of traditional docstrings, `docments` uses
inline parameter documentation, making code more concise and readable.

### What is docments style?

Instead of this:

``` python
def add(x, y):
    """Add two numbers.
    
    Args:
        x: First number
        y: Second number
        
    Returns:
        Sum of x and y
    """
    return x + y
```

Docments style looks like this:

``` python
def add(
    x: int,  # First number
    y: int   # Second number  
) -> int:  # Sum of x and y
    "Add two numbers"
    return x + y
```

### Key Features

- **🔍 Comprehensive Scanning**: Automatically scans all exported
  functions and classes in your nbdev notebooks
- **✅ Compliance Checking**: Verifies that all parameters and return
  values have proper documentation
- **📊 Detailed Reports**: Generate text or JSON reports showing
  compliance status
- **🔧 Auto-fix Support**: Automatically add TODO placeholders for
  missing documentation
- **🔄 Docstring Conversion**: Convert existing Google/NumPy/Sphinx
  style docstrings to docments format
- **💻 CLI Interface**: Easy-to-use command-line tool integrated with
  nbdev workflow

## Installation

Install latest from the GitHub
[repository](https://github.com/cj-mills/cjm-nbdev-docments):

``` sh
$ pip install git+https://github.com/cj-mills/cjm-nbdev-docments.git
```

or from [conda](https://anaconda.org/cj-mills/cjm-nbdev-docments)

``` sh
$ conda install -c cj-mills cjm_nbdev_docments
```

or from [pypi](https://pypi.org/project/cjm-nbdev-docments/)

``` sh
$ pip install cjm_nbdev_docments
```

## Quick Start

### Basic Usage

Check your nbdev project for documentation compliance:

``` bash
# Check all notebooks in your project
nbdev-docments

# Get detailed report including compliant functions
nbdev-docments --verbose

# Save report to a file
nbdev-docments --output compliance-report.txt
```

### Auto-fixing Non-compliant Code

Automatically add TODO placeholders for missing documentation:

``` bash
# Preview what would be fixed
nbdev-docments --fix --dry-run

# Apply fixes
nbdev-docments --fix
```

### Converting Existing Docstrings

Convert traditional docstrings to docments format:

``` bash
# Convert Google/NumPy/Sphinx style docstrings
nbdev-docments --fix --convert-docstrings
```

## Detailed Usage Examples

### Checking a Single Function

You can check individual functions for compliance:

``` python
from cjm_nbdev_docments.core import check_function

def example_func(x, y):
    return x + y

check_function(example_func)
```

### Checking a Specific Notebook

Check a single notebook file:

``` python
from cjm_nbdev_docments.core import check_notebook

check_notebook("00_core.ipynb")
```

### Programmatic Usage

For integration into your own tools:

``` python
from cjm_nbdev_docments.report import check_project, generate_json_report

# Check entire project
results = check_project()

# Generate JSON report
report = generate_json_report(results)

# Process results programmatically
for notebook, data in report['by_notebook'].items():
    print(f"{notebook}: {len(data['non_compliant'])} issues")
```

## What Gets Checked?

The tool checks for:

1.  **Function/Method Documentation**:
    - Presence of a docstring
    - Documentation for each parameter (except `self`)
    - Documentation for return values (when return type is annotated)
2.  **Type Hints**:
    - Missing type annotations for parameters
    - Missing return type annotations
3.  **Class Documentation**:
    - Presence of class docstrings
4.  **TODO Tracking**:
    - Identifies documentation with TODO placeholders
    - Helps track documentation debt

## CLI Reference

    nbdev-docments [OPTIONS]

    Options:
      --nbs-path PATH           Path to notebooks directory (defaults to nbdev config)
      --format {text,json}      Output format (default: text)
      --output, -o PATH         Save report to file instead of printing
      --verbose, -v             Show compliant definitions in text report
      --quiet, -q               Only show summary (exit code indicates compliance)
      --todos-only              Show only functions with TODO placeholders
      --fix                     Auto-fix non-compliant functions by adding placeholder docs
      --convert-docstrings      Convert existing Google/NumPy/Sphinx docstrings to docments format
      --dry-run                 Show what would be fixed without making changes
      -h, --help                Show help message and exit

### Exit Codes

- `0`: All checked definitions are compliant
- `1`: One or more definitions are non-compliant

## Module Overview

Detailed documentation for each module in the project:

### Core (`00_core.ipynb`)

> Core functionality for checking docments compliance

#### Import

``` python
from cjm-nbdev-docments.core import (
    DocmentsCheckResult,
    extract_param_docs_from_func,
    extract_param_docs,
    check_return_doc,
    count_todos_in_docs,
    check_has_docstring_from_func,
    check_has_docstring,
    check_type_hints,
    check_params_documentation,
    determine_compliance,
    check_definition,
    check_notebook,
    check_function
)
```

#### Functions

``` python
def extract_param_docs_from_func(
    func: Callable    # Function object to extract docs from
) -> Dict[str, str]:  # Mapping of parameter names to their documentation
    "Extract parameter documentation from function object using fastcore.docments"
```

``` python
def extract_param_docs(
    source:str    # Function source code
) -> Dict[str, str]:  # Mapping of parameter names to their documentation
    "Extract parameter documentation from function source using docments style (fallback)"
```

``` python
def check_return_doc(
    source: str  # Function source code
) -> bool:  # Whether return is documented
    "Check if function has return documentation"
```

``` python
def count_todos_in_docs(
    source: str,  # Function/class source code
    name: str  # Name of the function/class for AST parsing
) -> Tuple[int, bool]:  # (todo_count, has_todos)
    "Count TODO placeholders only in documentation (docstring, param docs, return docs)"
```

``` python
def check_has_docstring_from_func(
    func: Callable  # Function object to check
) -> bool:  # Whether the function has a docstring
    "Check if a function has a docstring using fastcore.docments"
```

``` python
def check_has_docstring(
    source: str,  # Function/class source code
    name: str  # Name of the function/class
) -> bool:  # Whether the definition has a docstring
    "Check if a function/class has a docstring using AST parsing (fallback)"
```

``` python
def check_type_hints(
    definition: Dict[str, Any]  # Definition dict from scanner
) -> Tuple[Dict[str, bool], List[str], bool]:  # (params_with_type_hints, missing_type_hints, return_has_type_hint)
    "Check which parameters and return value have type hints"
```

``` python
def check_params_documentation(
    definition: Dict[str, Any],  # Definition dict from scanner
    source: str  # Function source code
) -> Tuple[Dict[str, bool], List[str], bool]:  # (params_documented, missing_params, return_documented)
    "Check parameter and return documentation for a function"
```

``` python
def determine_compliance(
    has_docstring: bool,  # Whether definition has a docstring
    params_documented: Dict[str, bool],  # Which params have documentation
    return_documented: bool  # Whether return is documented
) -> bool:  # Overall compliance status
    "Determine if a definition is compliant based on documentation checks"
```

``` python
def check_definition(
    definition: Dict[str, Any]  # Definition dict from scanner
) -> DocmentsCheckResult:  # Check result with compliance details
    "Check a function/class definition for docments compliance"
```

``` python
def check_notebook(
    nb_path: str  # Path to notebook file  
) -> None:  # Prints compliance report
    "Check a specific notebook for docments compliance"
```

``` python
def check_function(
    func:Callable          # Function object to check
) -> DocmentsCheckResult:  # Check result for the function
    "Check a single function for docments compliance"
```

#### Classes

``` python
@dataclass
class DocmentsCheckResult:
    "Result of checking a function/class for docments compliance"
    
    name: str  # Name of the function/class
    type: str  # Type (FunctionDef, ClassDef, etc.)
    notebook: str  # Source notebook
    has_docstring: bool  # Whether it has a docstring
    params_documented: Dict[str, bool]  # Which params have documentation
    return_documented: bool  # Whether return is documented
    missing_params: List[str]  # Parameters missing documentation
    is_compliant: bool  # Overall compliance status
    source: str  # Source code of the definition
    has_todos: bool = False  # Whether it contains TODO placeholders
    todo_count: int = 0  # Number of TODO placeholders found
    params_with_type_hints: Dict[str, bool]  # Which params have type hints
    return_has_type_hint: bool = False  # Whether return has type hint
    params_missing_type_hints: List[str]  # Parameters missing type hints
    
```

### Scanner (`01_scanner.ipynb`)

> Scan nbdev notebooks for exported functions and classes

#### Import

``` python
from cjm-nbdev-docments.scanner import (
    get_export_cells,
    extract_definitions,
    scan_notebook,
    scan_project
)
```

#### Functions

``` python
def get_export_cells(
    nb_path: Path    # Path to the notebook file
) -> List[Dict[str, Any]]:  # List of cells with export directives
    "Extract all code cells from a notebook that have export directives"
```

``` python
def extract_definitions(
    source: str  # Python source code
) -> List[Dict[str, Any]]:  # List of function/class definitions with metadata
    "Extract function and class definitions from source code"
```

``` python
def scan_notebook(
    nb_path: Path  # Path to the notebook to scan
) -> List[Dict[str, Any]]:  # List of exported definitions with metadata
    "Scan a notebook and extract all exported function/class definitions"
```

``` python
def scan_project(
    nbs_path: Optional[Path] = None,  # Path to notebooks directory (defaults to config.nbs_path)
    pattern: str = "*.ipynb"  # Pattern for notebook files to scan
) -> List[Dict[str, Any]]:  # All exported definitions found in the project
    "Scan all notebooks in a project for exported definitions"
```

### Report Generator (`02_report.ipynb`)

> Generate compliance reports for docments validation

#### Import

``` python
from cjm-nbdev-docments.report import (
    check_project,
    generate_text_report,
    generate_json_report
)
```

#### Functions

``` python
def check_project(
    nbs_path: Optional[Path] = None  # Path to notebooks directory
) -> List[DocmentsCheckResult]:  # List of check results for all definitions
    "Check all exported definitions in a project for docments compliance"
```

``` python
def _generate_summary_stats(
    results: List[DocmentsCheckResult]  # Check results to summarize
) -> List[str]:  # Lines of summary statistics
    "Generate summary statistics section of the report"
```

``` python
def _generate_non_compliant_section(
    results: List[DocmentsCheckResult],  # Check results
    by_notebook: Dict[str, List[DocmentsCheckResult]]  # Results grouped by notebook
) -> List[str]:  # Lines of non-compliant section
    "Generate non-compliant definitions section of the report"
```

``` python
def _generate_todos_section(
    results: List[DocmentsCheckResult],  # Check results
    by_notebook: Dict[str, List[DocmentsCheckResult]]  # Results grouped by notebook
) -> List[str]:  # Lines of TODOs section
    "Generate TODO placeholders section of the report"
```

``` python
def _generate_compliant_section(
    results: List[DocmentsCheckResult],  # Check results
    by_notebook: Dict[str, List[DocmentsCheckResult]]  # Results grouped by notebook
) -> List[str]:  # Lines of compliant section
    "Generate compliant definitions section of the report"
```

``` python
def generate_text_report(
    results: List[DocmentsCheckResult],  # Check results from check_project
    verbose: bool = False  # Include detailed information
) -> str:  # Formatted text report
    "Generate a human-readable text report of compliance results"
```

``` python
def generate_json_report(
    results: List[DocmentsCheckResult]  # Check results from check_project
) -> Dict[str, Any]:  # JSON-serializable report data
    "Generate a JSON report of compliance results"
```

### Auto-Fix (`03_autofix.ipynb`)

> Automatically add placeholder documentation to non-compliant functions

#### Import

``` python
from cjm-nbdev-docments.autofix import (
    find_signature_boundaries,
    split_parameters,
    parse_single_line_signature,
    generate_param_todo_comment,
    generate_return_todo_comment,
    build_fixed_single_line_function,
    fix_multi_line_signature,
    fix_class_definition,
    insert_function_docstring,
    fix_single_line_function,
    fix_multi_line_function,
    generate_fixed_source,
    fix_notebook,
    DocstringInfo,
    detect_docstring_style,
    parse_google_docstring,
    parse_numpy_docstring,
    parse_sphinx_docstring,
    extract_docstring_info,
    convert_to_docments_format,
    convert_single_line_to_docments,
    convert_multiline_to_docments,
    replace_docstring_in_body,
    generate_fixed_source_with_conversion,
    fix_notebook_with_conversion
)
```

#### Functions

``` python
@patch
def needs_fixing(
    self: DocmentsCheckResult
) -> bool:  # TODO: Add return description
    "Check if this definition needs any fixing"
```

``` python
@patch
def get_param_name(
    self: DocmentsCheckResult,
    param_str: str  # TODO: Add description
) -> str:  # TODO: Add return description
    "Extract parameter name from a parameter string"
```

``` python
@patch
def needs_param_fix(
    self: DocmentsCheckResult,
    param_name: str  # TODO: Add description
) -> bool:  # TODO: Add return description
    "Check if a parameter needs documentation or type hint fixes"
```

``` python
def find_signature_boundaries(
    lines: List[str]  # Source code lines
) -> tuple[int, int]:  # (def_line_idx, sig_end_idx) or (-1, -1) if not found
    "Find the start and end lines of a function signature"
```

``` python
def split_parameters(
    params_str: str  # Parameter string from function signature
) -> List[str]:  # List of individual parameter strings
    "Split a parameter string into individual parameters, handling nested types"
```

``` python
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"
```

``` python
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"
```

``` python
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"
```

``` python
def build_fixed_single_line_function(
    parsed: dict,  # Parsed signature components
    params: List[str],  # Individual parameter strings
    result: DocmentsCheckResult  # Check result with missing params info
) -> List[str]:  # Lines of fixed function signature
    "Build a fixed single-line function with documentation comments"
```

``` python
def fix_multi_line_signature(
    lines: List[str],  # 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[str]:  # Fixed lines for the signature portion
    "Fix a multi-line function signature by adding parameter comments"
```

``` python
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"
```

``` python
def insert_function_docstring(
    lines: List[str],  # Fixed function lines
    def_line_idx: int,  # Index of function definition line
    indent: str  # Base indentation for the function
) -> List[str]:  # Lines with docstring inserted
    "Insert a TODO docstring after the function signature"
```

``` python
def fix_single_line_function(
    lines: List[str],  # All source lines
    def_line_idx: int,  # Index of function definition line
    result: DocmentsCheckResult  # Check result with missing params info
) -> List[str]:  # Fixed lines for the function
    "Fix a single-line function signature by converting to multi-line with parameter comments"
```

``` python
def fix_multi_line_function(
    lines: List[str],  # 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[str]:  # Fixed lines for the function
    "Fix a multi-line function signature by adding parameter comments"
```

``` python
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"
```

``` python
def fix_notebook(
    nb_path: Path,  # Path to notebook to fix
    dry_run: bool = False  # If True, show changes without saving
) -> Dict[str, Any]:  # Summary of changes made
    "Fix non-compliant functions in a notebook by adding placeholder documentation"
```

``` python
def detect_docstring_style(
    docstring: str  # Docstring text to analyze
) -> str:  # Detected style: 'google', 'numpy', 'sphinx', 'docments', or 'unknown'
    "Detect the style of a docstring"
```

``` python
def parse_google_docstring(
    docstring: str  # Google-style docstring text
) -> DocstringInfo:  # Parsed docstring information
    "Parse a Google-style docstring"
```

``` python
def parse_numpy_docstring(
    docstring: str  # NumPy-style docstring text
) -> DocstringInfo:  # Parsed docstring information
    "Parse a NumPy-style docstring"
```

``` python
def parse_sphinx_docstring(
    docstring: str  # Sphinx-style docstring text
) -> DocstringInfo:  # Parsed docstring information
    "Parse a Sphinx-style docstring"
```

``` python
def extract_docstring_info(
    source: str,  # Function source code
    name: str  # Function name
) -> Optional[DocstringInfo]:  # Extracted docstring information or None
    "Extract docstring information from function source code"
```

``` python
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"
```

``` python
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[str]:  # Multi-line signature with docments comments
    "Convert single-line function signature to multi-line docments format"
```

``` python
def convert_multiline_to_docments(
    sig_lines: List[str],  # Multi-line function signature
    docstring_info: DocstringInfo,  # Extracted docstring information
    result: DocmentsCheckResult  # Check result with missing params info
) -> List[str]:  # Multi-line signature with docments comments
    "Convert multi-line function signature to docments format"
```

``` python
def replace_docstring_in_body(
    body_lines: List[str],  # Function body lines
    description: str,  # New description to use
    def_line: str  # Function definition line for indentation
) -> List[str]:  # Modified body lines
    "Replace the docstring in function body with a simple description"
```

``` python
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"
```

``` python
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[str, Any]:  # Summary of changes made
    "Fix non-compliant functions in a notebook, optionally converting docstrings to docments format"
```

#### Classes

``` python
class DocstringInfo(NamedTuple):
    "Information extracted from a docstring"
```

### CLI Interface (`04_cli.ipynb`)

> Command-line interface for docments compliance checking

#### Import

``` python
from cjm-nbdev-docments.cli import (
    create_parser,
    handle_autofix,
    generate_report,
    output_report,
    main
)
```

#### Functions

``` python
def create_parser(
) -> argparse.ArgumentParser:  # TODO: Add return description
    "Create and configure the argument parser for docments CLI"
```

``` python
def handle_autofix(
    args: argparse.Namespace  # Parsed command line arguments
) -> int:  # Exit code
    "Handle auto-fix mode for non-compliant functions"
```

``` python
def generate_report(
    results: list,  # Check results from check_project
    format: str,  # Output format ("text" or "json")
    verbose: bool = False  # Whether to show compliant definitions
) -> str:  # Generated report as string
    "Generate a report in the specified format"
```

``` python
def output_report(
    report: str,  # Report content to output
    output_path: Optional[Path] = None,  # File path to save report to
    quiet: bool = False  # Whether to suppress output
) -> None:  # TODO: Add return description
    "Output the report to console or file"
```

``` python
def main(
    args: Optional[list] = None  # Command line arguments (for testing)
) -> int:  # Exit code (0 for success, 1 for non-compliance)
    "Main CLI entry point for docments checker"
```
