Development Workflow#
This guide covers the day-to-day development practices for contributing to NPAP.
Code Style#
NPAP uses Ruff for both linting and formatting. This ensures consistent code style across the project.
Configuration#
Ruff is configured in pyproject.toml:
Line length: 100 characters
Docstring style: NumPy convention
Type hints: Expected throughout
Commands#
Command |
Description |
|---|---|
|
Check for linting issues |
|
Auto-fix linting issues |
|
Format code |
|
Check formatting without changes |
Pre-commit Integration#
Pre-commit hooks run Ruff automatically before each commit:
# Install hooks (one-time)
pre-commit install
# Run manually on all files
pre-commit run --all-files
Testing#
NPAP uses pytest for testing.
Running Tests#
Command |
Description |
|---|---|
|
Run all tests |
|
Run with coverage report |
|
Run specific test file |
|
Run tests matching pattern |
|
Verbose output |
|
Stop on first failure |
Writing Tests#
Place tests in the test/ directory following the naming convention test_*.py:
# test/test_my_feature.py
import pytest
import networkx as nx
from npap import PartitionAggregatorManager
class TestMyFeature:
def test_basic_functionality(self):
"""Test the basic case."""
manager = PartitionAggregatorManager()
# ... test code ...
assert result == expected
def test_edge_case(self):
"""Test edge case handling."""
# ...
def test_error_handling(self):
"""Test that appropriate errors are raised."""
with pytest.raises(ValueError):
# Code that should raise ValueError
pass
Test Coverage#
Aim for high test coverage on new code:
# Generate coverage report
pytest --cov=npap --cov-report=html
# Open the HTML report
open htmlcov/index.html # or just browse to the file
Building Documentation#
Documentation is built with Sphinx using the pydata-sphinx-theme.
Local Build#
cd docs
sphinx-build -b html . _build/html
Open _build/html/index.html in your browser to view.
Live Reload (Development)#
For live reload during documentation development:
pip install sphinx-autobuild
cd docs
sphinx-autobuild . _build/html
This starts a local server at http://127.0.0.1:8000 that auto-refreshes on changes.
Documentation Style#
Format: Markdown (MyST) for user guides, RST for API docs
Docstrings: NumPy convention
Code examples: Include runnable examples where possible
Example docstring:
def partition(self, strategy: str, n_clusters: int, **kwargs) -> PartitionResult:
"""Partition the loaded graph into clusters.
Parameters
----------
strategy : str
Name of the partitioning strategy to use.
n_clusters : int
Number of clusters to create.
**kwargs
Additional arguments passed to the strategy.
Returns
-------
PartitionResult
Object containing cluster assignments and metadata.
Raises
------
PartitioningError
If the partitioning algorithm fails.
ValidationError
If required node attributes are missing.
Examples
--------
>>> manager = PartitionAggregatorManager()
>>> manager.load_data("networkx_direct", graph=G)
>>> partition = manager.partition("geographical_kmeans", n_clusters=10)
>>> print(partition.n_clusters)
10
"""
Regenerating the example notebooks#
The notebooks in examples/ are rendered in the documentation from their
stored outputs — the build never executes them, so it stays fast and needs
no network access. After changing anything they exercise, re-run them and commit
the result:
pip install -e ".[dev,docs]"
PLOTLY_RENDERER=notebook_connected jupyter nbconvert \
--to notebook --execute --inplace \
--ExecutePreprocessor.timeout=1800 \
examples/getting_started.ipynb examples/european_network_pypsa.ipynb
Two details matter here:
PLOTLY_RENDERER=notebook_connectedis not optional. Without it Plotly stores only its ownapplication/vnd.plotly.v1+jsonmime type, which the documentation builder skips. The notebooks still look fine in Jupyter, but every map silently disappears from the rendered pages.The default cell timeout is 30 seconds, far below what clustering 6,800 buses takes, hence
--ExecutePreprocessor.timeout=1800.
Type Hints#
Use type hints throughout your code:
from typing import Any
import networkx as nx
def my_function(graph: nx.DiGraph, n_clusters: int = 10, **kwargs: Any) -> dict[int, list[str]]:
"""Function with type hints."""
...
Project Structure#
Understanding the project structure helps when making changes:
npap/
├── __init__.py # Public API exports
├── interfaces.py # Abstract base classes and dataclasses
├── managers.py # Manager classes
├── exceptions.py # Custom exceptions
├── visualization.py # Plotting functionality
├── input/ # Data loading strategies
│ ├── csv_loader.py
│ ├── networkx_loader.py
│ └── va_loader.py
├── partitioning/ # Partitioning strategies
│ ├── geographical.py
│ ├── electrical.py
│ └── va_geographical.py
└── aggregation/ # Aggregation strategies
├── topology.py
├── physical.py
└── properties.py
Common Tasks#
Adding a New Partitioning Strategy#
Create strategy class inheriting from
PartitioningStrategyImplement
required_attributesproperty andpartitionmethodAdd tests in
test/test_partitioning.pyRegister in
PartitioningManagerinitializationUpdate documentation
See Extending NPAP for detailed instructions.
Fixing a Bug#
Write a failing test that reproduces the bug
Fix the bug
Verify the test passes
Check no other tests broke
Updating Documentation#
Edit the relevant
.mdor.rstfile indocs/Build locally to verify:
sphinx-build -b html docs docs/_build/htmlCheck for any Sphinx warnings
Next Steps#
Extending NPAP - Creating custom strategies
Pull Requests - Submitting your changes