Metadata-Version: 2.4
Name: docdoctor
Version: 0.1.0
Summary: LLM-assisted Python docstring reformatter and generator via Ollama
License: MIT
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27.0
Requires-Dist: libcst>=1.1.0
Requires-Dist: rich>=13.0.0
Requires-Dist: typer>=0.12.0
Requires-Dist: unidiff>=0.7.5
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: ruff>=0.4.0; extra == 'dev'
Description-Content-Type: text/markdown

# docdoctor

**LLM-assisted Python docstring reformatter and generator.**

`docdoctor` uses a local [Ollama](https://ollama.com) model (default: `qwen2.5-coder`) to:

- **Reformat** existing docstrings to a target style while preserving all content.
- **Generate** new docstrings from scratch, inferred from function/class signatures, type hints, and bodies.

Patches are output as unified diffs by default — no files are modified until you review and apply them. In-place modification is available as an explicit opt-in.

---

## Features

- Supports **Google**, **NumPy/NumpyDoc**, and **Sphinx/reST** styles out of the box.
- **Custom style** support: pass your own formatting instruction as a string or file.
- **Patch-first** workflow: inspect diffs before committing any change.
- `--inplace` flag for direct modification (with confirmation prompt).
- Processes single files, multiple files, or entire directories recursively.
- `--only-missing` to skip functions that already have docstrings.
- Configurable Ollama model, host, temperature.
- Rich terminal output with progress tracking.


    original_values = df[column_measurement].copy()
    numeric_values = pd.to_numeric(original_values, errors="coerce")
    mask_non_numeric = numeric_values.isna() & original_values.notna()
    non_numeric_unique = original_values[mask_non_numeric].unique()

---

## Requirements

- Python ≥ 3.10
- [Ollama](https://ollama.com) running locally (`ollama serve`)
- The target model pulled: `ollama pull qwen2.5-coder`

---

## Installation

### From source with `uv` (recommended)

```bash
git clone https://github.com/yourorg/docdoctor.git
cd docdoctor
uv sync
```

This creates a virtual environment in `.venv` and installs all dependencies.

### With `pip`

```bash
pip install .
```

### Development install

```bash
uv sync --extra dev
# or
pip install -e ".[dev]"
```

---

## Quick start

```bash
# Check that Ollama is reachable and the model is available
docdoctor check

# Generate patches for a single file (NumPy style, default)
docdoctor run mymodule.py

# Generate patches for a whole package
docdoctor run src/mypackage/

# Use Google style
docdoctor run src/mypackage/ --style google

# Use reST/Sphinx style
docdoctor run src/mypackage/ --style rest

# Write patches to a dedicated directory
docdoctor run src/mypackage/ --patch-dir patches/

# Apply a patch
patch -p1 < src/mypackage/mymodule.py.patch

# Apply all patches at once
find . -name "*.patch" | xargs -I{} patch -p1 < {}
```

---

## Workflow diagram

```
Parse file with libcst
        │
        ▼
Extract function/class nodes + existing docstrings
        │
        ▼
  Docstring present?
   ┌────┴─────┐
  Yes         No
   │           │
   ▼           ▼
Reformat    Generate
prompt      prompt
(preserve    (infer from
 content)    signature +
             body)
   │           │
   └─────┬─────┘
         ▼
   Ollama LLM call
         │
         ▼
  Inject via libcst
  (lossless round-trip)
         │
         ▼
  Write .patch file
  (or modify in place)
```

---

## Usage reference

```
Usage: docdoctor [OPTIONS] SOURCES...

Arguments:
  SOURCES  Python files or directories to process. [required]

Options:
  -s, --style         [google|rest|numpy|custom]  Target docstring style. [default: numpy]
  -i, --inplace                                   Modify files in place (asks for confirmation).
  -p, --patch-dir     PATH                        Directory to write .patch files.
  -m, --model         TEXT                        Ollama model name. [default: qwen2.5-coder]
      --host          TEXT                        Ollama server URL. [default: http://localhost:11434]
  -t, --temperature   FLOAT                       LLM sampling temperature. [default: 0.1]
  -c, --custom-instruction TEXT                   Custom format instruction (required for --style=custom).
      --only-missing                              Only process functions/classes without a docstring.
  -v, --version                                   Show version and exit.
  --help                                          Show this message and exit.
```

### Subcommands

```bash
docdoctor list-styles    # Print all supported styles with examples
docdoctor check          # Verify Ollama is running and model is available
```

---

## Custom docstring style

Pass a formatting instruction directly:

```bash
docdoctor src/ \
  --style custom \
  --custom-instruction "Use Epytext style. Parameters as @param name: description. Returns as @return: description."
```

Or load from a file:

```bash
cat > my_style.txt << 'EOF'
Use a compact one-line summary followed by a Parameters section with YAML-style
entries: `- name (type): description`. End with a Returns: line.
EOF

docdoctor src/ --style custom --custom-instruction my_style.txt
```

---

## Using a different model

Any model available in your Ollama instance works:

```bash
# Use Llama 3.1 8B
docdoctor src/ --model llama3.1:8b

# Use DeepSeek Coder
docdoctor src/ --model deepseek-coder-v2

# Use a remote Ollama server
docdoctor src/ --host http://192.168.1.100:11434 --model qwen2.5-coder
```

---

## Patch workflow detail

By default, `docdoctor` writes a `.patch` file next to each processed source file:

```
src/
  mymodule.py
  mymodule.py.patch   ← generated by docdoctor
```

Review the patch:

```bash
cat src/mymodule.py.patch
```

Apply it:

```bash
patch -p1 < src/mymodule.py.patch
```

Reject it (just delete the `.patch` file):

```bash
rm src/mymodule.py.patch
```

Write all patches to a single directory:

```bash
docdoctor src/ --patch-dir ./docdoctores/
```

---

## In-place mode

If you are confident in the output (e.g. working in a git branch), use `--inplace`:

```bash
# docdoctor will ask for confirmation before modifying anything
docdoctor src/mypackage/ --inplace

# Combine with --only-missing to fill gaps without touching existing docs
docdoctor src/mypackage/ --inplace --only-missing
```

> ⚠️ Always commit or back up your code before using `--inplace`.

---

## Running tests

```bash
uv run pytest tests/ -v
# or
hatch run test
```

---

## Project structure

```
docdoctor/
├── src/docdoctor/
│   ├── __init__.py
│   ├── __version__.py
│   ├── cli.py              # Typer CLI
│   ├── ollama_client.py    # HTTP client for Ollama /api/generate
│   ├── pipeline.py         # Orchestration: collect → LLM → patch/apply
│   ├── styles.py           # Style specs and prompt templates
│   └── transformer.py      # libcst CST visitor/transformer
├── tests/
│   └── test_core.py
├── pyproject.toml
└── README.md
```

---

## Design notes

- **`libcst` for CST transformation** — lossless round-tripping ensures that whitespace, comments, and code formatting outside docstrings are never altered, unlike `ast` + `ast.unparse`.
- **Patch-first default** — the LLM output should always be reviewed. Docstring generation can hallucinate parameter descriptions or miss nuance.
- **Low temperature (0.1)** — keeps output deterministic and structured; raise it if you want more creative summaries.
- **One LLM call per function/class** — straightforward and inspectable; no batching that obscures which node produced which output.

---

## License

MIT
