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
19 changes: 4 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,11 @@ jobs:
- name: Run tests with coverage
run: pytest tests/ --cov=dissmodel --cov-report=term-missing --cov-report=xml

# Scoped to the modules whose Examples sections are runnable doctests;
# the remaining modules have illustrative pseudo-examples (external
# data / optional deps) — converting them is tracked as a follow-up.
# All >>> examples in the package are self-contained doctests;
# illustrative snippets use plain ```python fences instead
# (see CONTRIBUTING.md, "Documentation / Docstrings").
- name: Run doctests
run: |
pytest --doctest-modules \
dissmodel/core/model.py \
dissmodel/core/environment.py \
dissmodel/geo/raster/backend.py \
dissmodel/geo/raster/raster_grid.py \
dissmodel/geo/raster/raster_model.py \
dissmodel/geo/raster/cellular_automaton.py \
dissmodel/geo/raster/sync_model.py \
dissmodel/geo/vector/vector_grid.py \
dissmodel/geo/vector/cellular_automaton.py \
dissmodel/geo/vector/sync_model.py
run: pytest --doctest-modules dissmodel

- name: Run mypy
run: mypy dissmodel --ignore-missing-imports
11 changes: 11 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,16 @@ If you have an idea for a new feature or improvement, please open an issue and t
- Use type hints where possible.
- Write docstrings for new functions and classes (NumPy style).

## Documentation / Docstrings

Examples in NumPy-style docstrings that use `>>>` prompts are executed as
doctests in CI (`pytest --doctest-modules dissmodel`) and must be fully
self-contained and runnable — every name they use must be defined within the
example itself, and the expected output must match exactly. Longer
illustrative examples that assume objects from a broader context (e.g. an
existing GeoDataFrame, `Environment`, or model instance) should use plain
` ```python ` fenced code blocks instead of `>>>` prompts; mkdocstrings
renders both forms in the API reference.

## License
By contributing, you agree that your contributions will be licensed under the MIT License.
19 changes: 11 additions & 8 deletions dissmodel/geo/vector/fill.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ def _generate_sample(data: _SampleData, size: int = 1) -> list[Any]:
--------
>>> import random; random.seed(0)
>>> _generate_sample([1, 2, 3], size=3)
[2, 1, 3]
[3, 3, 2]
>>> _generate_sample({"min": 0, "max": 1}, size=2)
[0, 1]
[1, 1]
"""
if isinstance(data, dict):
if "min" in data and "max" in data:
Expand Down Expand Up @@ -286,12 +286,15 @@ def fill(strategy: FillStrategy | str, **kwargs: Any) -> Any:

Examples
--------
>>> fill(FillStrategy.RANDOM_SAMPLE, gdf=grid, attr="state",
... data=[0, 1], seed=42)
>>> fill("min_distance", from_gdf=grid, to_gdf=roads,
... attr_name="dist_road")
>>> fill(FillStrategy.PATTERN, gdf=grid, attr="zone",
... pattern=[[1, 2], [3, 4]])
```python
# grid, roads: existing GeoDataFrames (e.g. from vector_grid / read_file)
fill(FillStrategy.RANDOM_SAMPLE, gdf=grid, attr="state",
data=[0, 1], seed=42)
fill("min_distance", from_gdf=grid, to_gdf=roads,
attr_name="dist_road")
fill(FillStrategy.PATTERN, gdf=grid, attr="zone",
pattern=[[1, 2], [3, 4]])
```
"""
key = FillStrategy(strategy)
if key not in _fill_strategies:
Expand Down
29 changes: 20 additions & 9 deletions dissmodel/geo/vector/neighborhood.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,14 @@ def attach_neighbors(

Examples
--------
>>> from libpysal.weights import Queen
>>> gdf = attach_neighbors(gdf, strategy=Queen)
>>> gdf = attach_neighbors(gdf, neighbors_dict="neighborhood.json")
>>> gdf = attach_neighbors(gdf, strategy=Queen, ids="cell_id")
```python
from libpysal.weights import Queen

# gdf: an existing GeoDataFrame (e.g. from vector_grid or read_file)
gdf = attach_neighbors(gdf, strategy=Queen)
gdf = attach_neighbors(gdf, neighbors_dict="neighborhood.json")
gdf = attach_neighbors(gdf, strategy=Queen, ids="cell_id")
```
"""
resolved = _resolve_neighbors_dict(neighbors_dict)

Expand Down Expand Up @@ -173,8 +177,10 @@ def get_neighbors(gdf: gpd.GeoDataFrame, idx: Any) -> list[Any]:

Examples
--------
>>> get_neighbors(gdf, "10-5")
['9-5', '11-5', '10-4', '10-6']
```python
# gdf: a GeoDataFrame with a "_neighs" column from attach_neighbors()
get_neighbors(gdf, "10-5") # ['9-5', '11-5', '10-4', '10-6']
```
"""
if "_neighs" not in gdf.columns:
raise ValueError(
Expand Down Expand Up @@ -216,8 +222,10 @@ def get_neighbor_values(

Examples
--------
>>> get_neighbor_values(gdf, "10-5", "land_use")
[1, 1, 2, 1]
```python
# gdf: a GeoDataFrame with a "_neighs" column from attach_neighbors()
get_neighbor_values(gdf, "10-5", "land_use") # [1, 1, 2, 1]
```
"""
neighbors = get_neighbors(gdf, idx)
if attr not in gdf.columns:
Expand Down Expand Up @@ -246,7 +254,10 @@ def export_neighbors(gdf: gpd.GeoDataFrame, path: str) -> None:

Examples
--------
>>> export_neighbors(gdf, "neighborhood.json")
```python
# gdf: a GeoDataFrame with a "_neighs" column from attach_neighbors()
export_neighbors(gdf, "neighborhood.json")
```
"""
if "_neighs" not in gdf.columns:
raise ValueError(
Expand Down
16 changes: 10 additions & 6 deletions dissmodel/io/_xarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,10 @@ def load_xarray(uri: str, minio_client=None, **kwargs):

Examples
--------
>>> backend, checksum = load_xarray("simulation_step_42.nc")
>>> backend.band_names()
['uso', 'alt', 'solo']
```python
backend, checksum = load_xarray("simulation_step_42.nc")
backend.band_names() # ['uso', 'alt', 'solo']
```
"""
try:
import xarray as xr
Expand Down Expand Up @@ -119,10 +120,13 @@ def save_xarray(

Examples
--------
>>> checksum = save_xarray(backend, "output_step_42.nc", step=42)
```python
# backend: a RasterBackend (e.g. from load_xarray or vector_to_raster_backend)
checksum = save_xarray(backend, "output_step_42.nc", step=42)

>>> # save as Zarr (requires zarr package)
>>> checksum = save_xarray(backend, "output.zarr", step=42)
# save as Zarr (requires zarr package)
checksum = save_xarray(backend, "output.zarr", step=42)
```
"""
try:
import xarray # noqa: F401 — availability check; used via to_xarray()
Expand Down
20 changes: 11 additions & 9 deletions dissmodel/io/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,17 @@ def vector_to_raster_backend(

Examples
--------
>>> # From file path
>>> b = vector_to_raster_backend(
... "data/mangue_grid.shp", resolution=100, attrs=["uso", "alt"]
... )

>>> # From in-memory GeoDataFrame
>>> import geopandas as gpd
>>> gdf = gpd.read_file("data/mangue_grid.shp").to_crs("EPSG:31984")
>>> b = vector_to_raster_backend(gdf, resolution=100, attrs={"uso": -1})
```python
# From file path
b = vector_to_raster_backend(
"data/mangue_grid.shp", resolution=100, attrs=["uso", "alt"]
)

# From in-memory GeoDataFrame
import geopandas as gpd
gdf = gpd.read_file("data/mangue_grid.shp").to_crs("EPSG:31984")
b = vector_to_raster_backend(gdf, resolution=100, attrs={"uso": -1})
```
"""
try:
import rasterio
Expand Down
10 changes: 7 additions & 3 deletions dissmodel/visualization/chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,13 @@ class Chart(Model):

Examples
--------
>>> env = Environment(end_time=30)
>>> Chart(show_legend=True, show_grid=True, title="SIR Model")
>>> env.run()
```python
from dissmodel.core import Environment

env = Environment(end_time=30)
Chart(show_legend=True, show_grid=True, title="SIR Model")
env.run()
```
"""

fig: matplotlib.figure.Figure
Expand Down
11 changes: 8 additions & 3 deletions dissmodel/visualization/map.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,14 @@ class Map(Model):

Examples
--------
>>> env = Environment(end_time=10)
>>> Map(gdf=grid, plot_params={"column": "state", "cmap": "viridis"})
>>> env.run()
```python
from dissmodel.core import Environment

# grid: a GeoDataFrame (e.g. from vector_grid or read_file)
env = Environment(end_time=10)
Map(gdf=grid, plot_params={"column": "state", "cmap": "viridis"})
env.run()
```
"""

# Narrowing the base Model.setup(**kwargs) contract is intentional:
Expand Down
7 changes: 5 additions & 2 deletions dissmodel/visualization/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ def display_inputs(obj: Any, st: Any) -> None:

Examples
--------
>>> display_inputs(sir_model, st.sidebar)
>>> display_inputs(ca_model, st)
```python
# sir_model, ca_model: Model instances; st: the imported streamlit module
display_inputs(sir_model, st.sidebar)
display_inputs(ca_model, st)
```
"""
annotations: dict[str, Any] = getattr(obj, "__annotations__", {})

Expand Down
Loading