Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/skills
35 changes: 35 additions & 0 deletions .codex/environments/environment.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY
version = 1
name = "ridgeplot"

[setup]
script = '''
if git remote | grep -q '^origin$'; then
echo "info: git remote 'origin' already exists"
else
git remote add origin https://github.com/tpvasconcelos/ridgeplot.git
fi

if git rev-parse --is-shallow-repository 2>/dev/null | grep -q true; then
git fetch --unshallow
else
echo "info: git repository is already complete (not shallow)"
fi

git fetch -v
'''

[[actions]]
name = "source venv"
icon = "tool"
command = "source .venv/bin/activate"

[[actions]]
name = "tox -m tests"
icon = "test"
command = "uvx tox -m tests"

[[actions]]
name = "tox -m static-quick"
icon = "test"
command = "uvx tox -m static-quick"
1 change: 1 addition & 0 deletions .codex/skills
1 change: 1 addition & 0 deletions .cursor/skills
3 changes: 2 additions & 1 deletion .cursor/worktrees.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"setup-worktree": [
"make init"
"make init",
"source .venv/bin/activate"
]
}
221 changes: 221 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
# ridgeplot Development Guide for AI Agents

`ridgeplot` is a Python package for beautiful, interactive ridgeline plots built on Plotly.

## Start Here

- Read this file first, then the most relevant source file(s).
- Prefer local docs and tests over memory; do not guess behavior.
- If requirements are unclear or risky, ask a concrete question before changing code.

## Stack and Style Constraints

- Python >=3.10
- Plotly graph objects (dependency is `plotly>=5.20`)
- Line length 100, formatting with ruff
- Docstrings are NumPy style
- Type annotations are modern and throughout with strict checking via pyright

## Quick Commands

### Environment Setup

```shell
# Initialize full development environment (recommended for first-time setup)
# This creates .venv, installs dependencies, and sets up pre-commit hooks
make init

# Activate the virtual environment
source .venv/bin/activate
```

### Running Tests

```shell
# Run all test suites (unit + e2e + cicd_utils)
uvx tox -m tests

# Run a specific test suite
uvx tox -e tests-unit # Unit tests with coverage
uvx tox -e tests-e2e # End-to-end tests
uvx tox -e tests-cicd_utils # CI/CD utilities tests

# Run pytest directly with custom options
uvx tox -e pytest -- tests/unit/test_init.py --no-cov
uvx tox -e pytest -- -k "test_specific_function" --no-cov
```

### Linting and Formatting

```shell
# Run the main static checks
uvx tox -m static-quick

# Run the entire suite of static checks (incl. all pre-commit hooks)
# If running from the main branch, you'll need
# to skip the 'no-commit-to-branch' check with:
SKIP='no-commit-to-branch' uvx tox -m static

# Run all pre-commit hooks on all files
uvx pre-commit run --all-files

# Run specific pre-commit hooks
uvx pre-commit run ruff-format --all-files

# Run type checking with pyright only
uvx tox -e typing
```

### Documentation

```shell
# Build static documentation
uvx tox -e docs-static
```

## Project Map

```text
src/ridgeplot/
├── __init__.py # Public API exports
├── _ridgeplot.py # Main ridgeplot() function
├── _figure_factory.py # Plotly Figure construction
├── _kde.py # Kernel Density Estimation
├── _hist.py # Histogram binning
├── _types.py # Type aliases and type guards
├── _utils.py # Utility functions
├── _missing.py # Sentinel for missing values
├── _version.py # Version string (setuptools-scm)
├── _color/ # Color handling
│ ├── colorscale.py # Colorscale resolution
│ ├── css_colors.py # CSS color parsing
│ ├── interpolation.py # Color interpolation
│ └── utils.py # Color utilities
├── _obj/traces/ # Trace objects
│ ├── area.py # Area trace (filled curves)
│ ├── bar.py # Bar trace (histograms)
│ └── base.py # Base trace class
├── _vendor/ # Vendored dependencies
└── datasets/ # Built-in datasets
└── data/ # CSV data files

tests/
├── conftest.py # Shared pytest fixtures
├── unit/ # Unit tests for individual modules
├── e2e/ # End-to-end tests with expected outputs
│ └── artifacts/ # JSON artifacts for e2e comparisons
└── cicd_utils/ # Tests for CI/CD utilities

cicd_utils/ # CI/CD helper modules
├── cicd/ # Scripts and test helpers
└── ridgeplot_examples/ # Example implementations for docs/testing
```

## Public API

The public API exposes one main function: `ridgeplot.ridgeplot(...) -> go.Figure`

The `ridgeplot()` docstring in `src/ridgeplot/_ridgeplot.py` contains the API contract.

`ridgeplot.datasets.load_probly()` and
`ridgeplot.datasets.load_lincoln_weather()` are legacy loaders kept for
backwards compatibility only.

## Key Data Flow

1. Users call `ridgeplot(samples=...)` or `ridgeplot(densities=...)`.
2. If samples are provided, KDE in `_kde.py` or histogram binning in `_hist.py`
produces densities.
3. Densities are normalised if the `norm` parameter is set.
4. `create_ridgeplot()` in `_figure_factory.py` builds the Plotly Figure by
resolving colors, creating traces, and applying layout settings.
5. The function returns a `plotly.graph_objects.Figure`.

## Key Files to Know

| File | Purpose |
|------------------------------------|-----------------------------|
| `src/ridgeplot/_ridgeplot.py` | Main `ridgeplot()` function |
| `src/ridgeplot/_figure_factory.py` | Figure construction logic |
| `src/ridgeplot/_types.py` | All type aliases and guards |
| `tests/unit/test_ridgeplot.py` | Core function tests |
| `cicd_utils/ridgeplot_examples/` | Example scripts for docs |
| `docs/` | User and developer docs |
| `tox.ini` | CI environment definitions |
| `ruff.toml` | Linting configuration |

Documentation: https://ridgeplot.readthedocs.io/en/stable/

## Workflow Expectations

### When Adding New Features

1. Start with `src/ridgeplot/_ridgeplot.py` to understand the entry point.
2. Add parameters following existing patterns and deprecation handling.
3. Update types in `src/ridgeplot/_types.py` when introducing new structures.
4. Add tests in `tests/unit/` with good coverage.
5. Update docstrings to match the new behavior.

### When Fixing Bugs

1. Write a failing test first.
2. Fix the issue with minimal changes.
3. Ensure tests and type checks pass.
4. Verify docstrings remain accurate.

### Type Annotations

- Uses **pyright** in strict mode (see `pyrightconfig.json`)
- All functions must be fully typed, following modern Python typing practices and existing project conventions.
- Use `TYPE_CHECKING` blocks for import-only types
- Use type guards for runtime type narrowing (see `_types.py`)
- Follow this general principle for user-facing code: be contravariant in the input type and covariant in the output type
- Add type aliases close to their usage. If widely used, place in `_types.py`.

## Common Patterns

**Handling deprecated parameters:**

```python
if deprecated_param is not MISSING:
if new_param is not None:
raise ValueError("Cannot use both...")
warnings.warn("...", DeprecationWarning, stacklevel=2)
new_param = deprecated_param
```

**Type narrowing with guards:**

```python
if is_shallow_samples(samples):
samples = nest_shallow_collection(samples)
samples = cast("Samples", samples)
```

**Lazy imports for performance:**

```python
# Heavy imports inside functions to reduce import time
def _coerce_to_densities(...):
from ridgeplot._kde import estimate_densities # statsmodels is slow to import
```

## Testing Notes

- Use `--no-cov` flag during development for faster test runs
- Run targeted tests via: `uvx tox -e pytest -- tests/unit/test_foo.py -k "test_bar" --no-cov`
- The `tests/e2e/artifacts/` directory contains expected Plotly Figure JSON for e2e tests

## CI/CD Pipeline

GitHub Actions runs tests on Python 3.10–3.14 across Ubuntu, macOS, and Windows.
Codecov minimums are 98% overall and 100% diff coverage for new code.

## Notes for AI Assistants

1. Run tests after changes when feasible, starting with the smallest relevant subset.
2. Run `uvx tox -e typing` if types are touched or errors are likely.
3. Run `uvx pre-commit run ruff-format --all-files` to format code.
4. Preserve deprecation behavior and public API stability.
5. Keep changes minimal and aligned with existing patterns.
6. Respect existing patterns - this is a mature codebase with consistent style.
1 change: 1 addition & 0 deletions CLAUDE.md
5 changes: 4 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,14 @@ prune docs/api/autogen
prune docs/api/public

# Misc
recursive-include .cursor *.json
recursive-include .claude *.md
recursive-include .codex *.toml *.md
recursive-include .cursor *.json *.md
recursive-include .github *.yml *.yaml
recursive-include cicd_utils README.md *.py *.sh
recursive-include misc *.py *.ipynb *.txt *.png
recursive-include requirements *.txt
recursive-include skills *.md
recursive-include tests *.py
recursive-include tests/e2e/artifacts *.json

Expand Down
5 changes: 2 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ VENV_BIN := $(VENV_PATH)/bin
OFFLINE ?= 0
ifeq ($(OFFLINE), 1)
_UV_OFFLINE_ARG = --offline

else
_UV_OFFLINE_ARG =
endif
Expand Down Expand Up @@ -58,7 +57,7 @@ _check-sys: ## Check system requirements
fi


$(VENV_PATH): _check-sys ## create a virtual environment
$(VENV_PATH): | _check-sys ## create a virtual environment
@echo "==> Creating local virtual environment under: $(VENV_PATH)/ ($(BASE_PYTHON))"
@uv venv $(_UV_OFFLINE_ARG) --python="$(BASE_PYTHON)" --seed "$(VENV_PATH)"

Expand All @@ -78,7 +77,7 @@ install: $(VENV_PATH) ## install all local development dependencies
.PHONY: jupyter-init
jupyter-init: install ## initialise a jupyter environment
@echo "==> Setting up jupyterlab environment..."
@$(VENV_BIN)/uv pip install --upgrade ipykernel jupyter
@uv pip install --upgrade ipykernel jupyter
@$(VENV_BIN)/python -m ipykernel install --user --name='ridgeplot' --display-name='ridgeplot'


Expand Down
2 changes: 2 additions & 0 deletions docs/reference/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Unreleased changes

### Developer Experience

- Add AGENTS.md, CLAUDE.md, .codex/, and .cursor/ helper files for AI-assisted development ({gh-pr}`367`)
- Small improvements to the project's Makefile ({gh-pr}`367`)
- Add a basic version of Cursor's `worktrees.json` config ({gh-pr}`364`)

### CI/CD
Expand Down
10 changes: 10 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,13 @@ namespaces = false
# and we want to push X.devM to TestPyPi
# on every merge to the `main` branch
local_scheme = "no-local-version"

[tool.check-manifest]
ignore = [
".claude/skills",
".claude/skills/**",
".codex/skills",
".codex/skills/**",
".cursor/skills",
".cursor/skills/**",
]
Loading