# Nhandu

[![PyPI version](https://img.shields.io/pypi/v/nhandu.svg)](https://pypi.org/project/nhandu/)
[![Python 3.10+](https://img.shields.io/pypi/pyversions/nhandu.svg)](https://pypi.org/project/nhandu/)
[![Tests](https://github.com/tresoldi/nhandu/actions/workflows/quality.yml/badge.svg)](https://github.com/tresoldi/nhandu/actions)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

**Literate programming for Python: write executable documents in plain `.py` files.**

Nhandu transforms Python files with markdown comments into beautiful, reproducible reports. It is lighter than Jupyter, simpler than Quarto, and perfectly git-friendly.

> *Nhandu* (/ɲãndu/, approximately "NYAN-doo") means "spider" in many Tupi-Guarani languages, reflecting its role in weaving code and documentation together, much like Knuth's original vision of literate programming.

## Installation

```bash
pip install nhandu
```

For Jupyter notebook conversion support:

```bash
pip install nhandu[jupyter]
```

**Requirements:** Python 3.10 or later.

## Quick Start

Create a file called `analysis.py`:

```python
#' # My First Analysis
#'
#' Lines starting with `#'` are markdown.
#' Everything else is regular Python code.

import math

#' ## Results

print(f"Pi is approximately {math.pi:.4f}")
print(f"Euler's number is approximately {math.e:.4f}")

#' Variables persist between code blocks:

radius = 5
area = math.pi * radius ** 2
print(f"Area of circle with radius {radius}: {area:.2f}")
```

Generate a report:

```bash
nhandu analysis.py                  # Markdown output → analysis.out.md
nhandu analysis.py --format html    # HTML output → analysis.html
```

## Why Nhandu?

Python's literate programming tools each come with trade-offs:

- **Jupyter notebooks** use JSON (messy git diffs), require a browser, and mix code with metadata
- **Quarto** is powerful but complex, with a steep learning curve
- **Pweave** is no longer maintained and incompatible with current Python versions

Nhandu offers a different approach:

| | Jupyter | Quarto | Nhandu |
|---|---------|--------|--------|
| File format | JSON (`.ipynb`) | Markdown (`.qmd`) | Python (`.py`) |
| Git diffs | Noisy | Clean | Clean |
| Requires browser/server | Yes | No | No |
| Configuration needed | Kernels, metadata | YAML headers, engines | Nothing |
| Editor support | Specialized | Any + plugins | Any Python editor |
| Learning curve | Medium | High | Minimal |

## Features

### Smart Output Capture

Nhandu automatically captures:

- **Print statements** and stdout
- **Matplotlib figures** (no `plt.show()` needed) — also works with seaborn, pandas `.plot()`, and other matplotlib-based libraries
- **Expression results** (last expression in a block is displayed, like Jupyter)

### Syntax Highlighting

Server-side syntax highlighting via [Pygments](https://pygments.org/) with 49 built-in themes. Popular themes include `github-dark` (default), `monokai`, `dracula`, `one-dark`, and `solarized-light`.

List all available themes:

```bash
nhandu --list-themes
```

### HTML Templates

Three built-in templates for HTML output:

- **`default`** — clean sans-serif layout
- **`academic`** — serif, paper-like styling for research reports
- **`corporate`** — blue accents, professional look

Select via `--template academic` or `template:` frontmatter. Add custom CSS overrides with `--template-css custom.css`. List available templates:

```bash
nhandu --list-templates
```

Templates are orthogonal to `--code-theme` (Pygments syntax highlighting).

### Output Formats

- **Markdown** (default) — convertible to PDF, Word, or LaTeX via [pandoc](https://pandoc.org/)
- **HTML** — self-contained files with embedded plots, syntax highlighting, and responsive styling

### Inline Code

Embed Python expressions directly in markdown text:

```python
total = 1500
target = 2000

#' We achieved <%= total %> in sales,
#' which is <%= (total/target)*100 %>% of our target.
```

- `<%= expression %>` — evaluate and display the result
- `<% statement %>` — execute without output

Inline code shares the same namespace as regular code blocks.

### Hidden Code Blocks

Run setup code without cluttering the report:

```python
#| hide
import pandas as pd
import matplotlib.pyplot as plt
plt.style.use('seaborn-v0_8')
#|

#' Now the analysis begins:
data = pd.read_csv("data.csv")
```

### YAML Frontmatter

Configure reports via optional frontmatter:

```python
#' ---
#' title: My Scientific Report
#' output: html
#' code_theme: dracula
#' template: academic
#' plot_dpi: 150
#' number_format: ".2f"
#' show_footer: false
#' ---
```

| Option | Default | Description |
|--------|---------|-------------|
| `title` | filename | Document title (used in HTML `<title>`) |
| `output` | `markdown` | Output format: `markdown` or `html` |
| `code_theme` | `github-dark` | Pygments syntax highlighting theme |
| `plot_dpi` | `100` | DPI for saved matplotlib figures |
| `number_format` | `.4f` | Python format spec for inline float values |
| `template` | `default` | HTML template: `default`, `academic`, or `corporate` |
| `template_css` | (none) | Path to custom CSS file appended to template styles |
| `show_footer` | `true` | Show "Made with Nhandu" footer in HTML |
| `working_dir` | (current) | Working directory for code execution |

Configuration can also be provided via a YAML file (`--config config.yaml`) or CLI arguments.

## CLI Reference

### Processing Documents

```bash
nhandu INPUT [OPTIONS]

# Examples:
nhandu analysis.py                        # → analysis.out.md (markdown)
nhandu analysis.py --format html          # → analysis.html
nhandu analysis.py -o report.html         # Specify output path
nhandu analysis.py --code-theme monokai   # Custom syntax theme
nhandu analysis.py --no-footer            # Disable HTML footer
nhandu analysis.py -v                     # Verbose with progress
nhandu analysis.py --parse-only           # Show structure, skip execution
```

### Options

```
positional:
  INPUT                         Input file (.py literate Python)

options:
  -o, --output PATH             Output file path
  --format {html,md,markdown}   Output format (default: markdown)
  --config PATH                 Configuration file (YAML)
  --working-dir PATH            Working directory for code execution
  --code-theme THEME            Syntax highlighting theme
  --template TEMPLATE           HTML template (default, academic, corporate)
  --template-css PATH           Custom CSS file to append to template styles
  --no-footer                   Disable footer in HTML output
  --parse-only                  Parse and show structure without executing
  -v, --verbose                 Verbose output with progress
  --list-themes                 List all available syntax themes
  --list-templates              List all available HTML templates
  --version                     Show version and exit
```

### Jupyter Notebook Conversion

Import from Jupyter:

```bash
nhandu import-notebook notebook.ipynb -o document.py
```

Export to Jupyter:

```bash
nhandu export-notebook document.py -o notebook.ipynb
nhandu export-notebook document.py -o notebook.ipynb --execute  # With outputs
```

Conversion preserves markdown cells, code cells, hidden cell tags, and metadata. Requires `pip install nhandu[jupyter]`.

## The Literate Python Format

The format is simple: lines starting with `#'` are markdown, everything else is Python code.

```python
#' # Heading
#'
#' Markdown text with **bold**, *italic*, `code`,
#' [links](https://example.com), and tables.

# This is a regular Python comment (stays in the code block)
x = 42

#' Back to documentation. The variable `x` is still in scope:

print(f"x = {x}")  # Output captured automatically
```

### Execution Model

- **Shared namespace** — all code blocks share the same Python environment
- **Sequential execution** — blocks run top to bottom, in document order
- **Automatic capture** — stdout, expression results, and matplotlib figures are captured without any special syntax

## Python API

Nhandu can also be used as a library:

```python
from nhandu import parse, execute, render

content = open("document.py").read()
doc = parse(content, source_path="document.py")
executed = execute(doc)
html = render(executed, format="html")
```

Progress tracking is available via callback:

```python
def on_progress(index, total, block):
    print(f"Block {index}/{total}: {'ERROR' if block.error else 'OK'}")

executed = execute(doc, on_progress=on_progress)
```

Available themes can be listed programmatically:

```python
from nhandu.renderer import list_themes
print(list_themes())
```

## Examples

The [`docs/`](https://github.com/tresoldi/nhandu/tree/main/docs/) directory contains complete demonstrations:

| Example | Description | Output |
|---------|-------------|--------|
| [01_hello_world.py](https://github.com/tresoldi/nhandu/tree/main/docs/01_hello_world.py) | Basic syntax and concepts | [HTML](https://htmlpreview.github.io/?https://github.com/tresoldi/nhandu/blob/main/docs/01_hello_world.py.html) |
| [02_data_analysis.py](https://github.com/tresoldi/nhandu/tree/main/docs/02_data_analysis.py) | Data analysis with the standard library | [HTML](https://htmlpreview.github.io/?https://github.com/tresoldi/nhandu/blob/main/docs/02_data_analysis.py.html) |
| [03_plotting.py](https://github.com/tresoldi/nhandu/tree/main/docs/03_plotting.py) | Matplotlib visualizations | [HTML](https://htmlpreview.github.io/?https://github.com/tresoldi/nhandu/blob/main/docs/03_plotting.py.html) |
| [04_scientific_computation.py](https://github.com/tresoldi/nhandu/tree/main/docs/04_scientific_computation.py) | NumPy numerical computing | [HTML](https://htmlpreview.github.io/?https://github.com/tresoldi/nhandu/blob/main/docs/04_scientific_computation.py.html) |
| [05_advanced_report.py](https://github.com/tresoldi/nhandu/tree/main/docs/05_advanced_report.py) | Pandas and multiple visualizations | [HTML](https://htmlpreview.github.io/?https://github.com/tresoldi/nhandu/blob/main/docs/05_advanced_report.py.html) |
| [06_inline_code.py](https://github.com/tresoldi/nhandu/tree/main/docs/06_inline_code.py) | Inline code evaluation | [HTML](https://htmlpreview.github.io/?https://github.com/tresoldi/nhandu/blob/main/docs/06_inline_code.py.html) |

## Project Information

### Citation

If you use Nhandu in your research, please cite:

```bibtex
@software{tresoldi2025nhandu,
  author = {Tresoldi, Tiago},
  title = {Nhandu: Literate Programming for Python},
  year = {2025},
  publisher = {Department of Linguistics and Philology, Uppsala University},
  address = {Uppsala, Sweden},
  url = {https://github.com/tresoldi/nhandu},
  orcid = {0000-0002-2863-1467}
}
```

### Acknowledgments

Nhandu is inspired by Donald Knuth's [literate programming](https://en.wikipedia.org/wiki/Literate_programming) vision, [knitr](https://yihui.org/knitr/) and R Markdown's approach to reproducible research, [Jupyter](https://jupyter.org/)'s interactive computing paradigm, [Quarto](https://quarto.org/)'s scientific publishing tools, and [Pweave](http://mpastell.com/pweave/)'s Python implementation.

The earliest stages of development took place within the [Cultural Evolution of Texts](https://github.com/evotext/) project, with funding from the [Riksbankens Jubileumsfond](https://www.rj.se/) (grant agreement ID: [MXM19-1087:1](https://www.rj.se/en/anslag/2019/cultural-evolution-of-texts/)).

### License

MIT License. See [LICENSE](LICENSE) for details.

### Links

- [PyPI](https://pypi.org/project/nhandu/) — package page
- [Issues](https://github.com/tresoldi/nhandu/issues) — bug reports and feature requests
- [Changelog](CHANGELOG.md) — version history
- [Roadmap](ROADMAP.md) — planned features
