Skip to content

Commit 001eee5

Browse files
gautehCopilot
andcommitted
types: add trajan.Dataset typed alias with .traj accessor
Ship trajan/__init__.pyi so LSPs (basedpyright, pyright, pylance, mypy) discover Dataset as a typed subclass of xr.Dataset with traj: Traj. Users annotate once and get full completion on all resulting datasets: ds: trajan.Dataset = xr.open_dataset('file.nc') ds.traj.speed() # <- LSP completion works At runtime Dataset = xr.Dataset (no overhead). The .pyi stub is authoritative for type checkers and takes precedence over the source. Also add 'from __future__ import annotations' to traj.py and guard 'from . import Dataset' behind TYPE_CHECKING to break the circular import that arises when traj.py uses Dataset in return annotations. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 821c773 commit 001eee5

3 files changed

Lines changed: 71 additions & 14 deletions

File tree

trajan/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,15 @@
1717

1818
from . import waves as _
1919

20+
from .traj import Traj
21+
22+
# Runtime alias so `trajan.Dataset` is usable as a value (e.g. isinstance checks).
23+
# The `.pyi` stub declares the typed subclass with `.traj` typed as Traj.
24+
Dataset = xr.Dataset
25+
2026
logger = logging.getLogger(__name__)
2127

28+
2229
__version__ = importlib.metadata.version("trajan")
2330

2431
def versions():

trajan/__init__.pyi

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""
2+
Stub file for trajan public API.
3+
4+
Declares ``trajan.Dataset`` — a typed alias for ``xr.Dataset`` that exposes
5+
the ``.traj`` accessor. Use it as a type annotation to get LSP completion:
6+
7+
import trajan
8+
ds: trajan.Dataset = xr.open_dataset("file.nc")
9+
ds.traj.speed() # <- full completion
10+
"""
11+
12+
import pandas as pd
13+
import xarray as xr
14+
from typing import Any
15+
16+
from .traj import Traj as Traj
17+
from .traj1d import Traj1d as Traj1d
18+
from .traj2d import Traj2d as Traj2d
19+
20+
class Dataset(xr.Dataset):
21+
"""xarray Dataset with the trajan ``.traj`` accessor typed."""
22+
23+
@property
24+
def traj(self) -> Traj: ...
25+
26+
def versions() -> str: ...
27+
28+
def read_csv(f: Any, **kwargs: Any) -> Dataset: ...
29+
30+
def from_dataframe(
31+
df: pd.DataFrame,
32+
lon: str = ...,
33+
lat: str = ...,
34+
time: str = ...,
35+
name: str | None = ...,
36+
*,
37+
__test_condense__: bool = ...,
38+
) -> Dataset: ...
39+
40+
def trajectory_dict_to_dataset(
41+
trajectory_dict: dict[str, Any],
42+
variable_attributes: dict[str, Any] | None = ...,
43+
global_attributes: dict[str, Any] | None = ...,
44+
) -> Dataset: ...

trajan/traj.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55
https://cfconventions.org/Data/cf-conventions/cf-conventions-1.10/cf-conventions.html#_multidimensional_array_representation_of_trajectories.
66
"""
77

8+
from __future__ import annotations
9+
810
from abc import abstractmethod
911
from datetime import timedelta
1012
from functools import cache
1113
import inspect
14+
from typing import TYPE_CHECKING
1215
import pyproj
1316
import numpy as np
1417
import xarray as xr
@@ -20,6 +23,9 @@
2023
from .plot import Plot
2124
from .animation import Animation
2225

26+
if TYPE_CHECKING:
27+
from . import Dataset
28+
2329
logger = logging.getLogger(__name__)
2430

2531

@@ -101,7 +107,7 @@ def grid_area(lons, lats):
101107

102108

103109
class Traj:
104-
ds: xr.Dataset
110+
ds: Dataset
105111

106112
__plot__: Plot
107113
__animate__: Animation
@@ -468,7 +474,7 @@ def ccrs(self) -> cartopy.crs.CRS:
468474
else:
469475
return None
470476

471-
def set_crs(self, crs) -> xr.Dataset:
477+
def set_crs(self, crs) -> Dataset:
472478
"""
473479
Returns a new dataset with the CF-supported grid-mapping / projection set to `crs`.
474480
@@ -570,7 +576,7 @@ def assign_cf_attrs(self,
570576
creator_email=None,
571577
title=None,
572578
summary=None,
573-
**kwargs) -> xr.Dataset:
579+
**kwargs) -> Dataset:
574580
"""
575581
Return a new dataset with CF-standard and common attributes set.
576582
@@ -724,7 +730,7 @@ def velocity_spectrum(self) -> xr.DataArray:
724730
# pass
725731

726732
@abstractmethod
727-
def distance_to(self, other) -> xr.Dataset:
733+
def distance_to(self, other) -> Dataset:
728734
"""
729735
Distance between trajectories or a single point.
730736
@@ -1002,7 +1008,7 @@ def get_area_convex_hull(self):
10021008
attrs={"units": "m2"})
10031009

10041010
@abstractmethod
1005-
def gridtime(self, times, time_varname=None) -> xr.Dataset:
1011+
def gridtime(self, times, time_varname=None) -> Dataset:
10061012
"""Interpolate dataset to a regular time interval or a different grid.
10071013
10081014
Parameters
@@ -1022,7 +1028,7 @@ def gridtime(self, times, time_varname=None) -> xr.Dataset:
10221028
"""
10231029

10241030
@abstractmethod
1025-
def sel(self, *args, **kwargs) -> xr.Dataset:
1031+
def sel(self, *args, **kwargs) -> Dataset:
10261032
"""Select on each trajectory. On 1D datasets this is just a shortcut for `Dataset.sel`.
10271033
10281034
Parameters
@@ -1041,7 +1047,7 @@ def sel(self, *args, **kwargs) -> xr.Dataset:
10411047
"""
10421048

10431049
@abstractmethod
1044-
def seltime(self, t0=None, t1=None) -> xr.Dataset:
1050+
def seltime(self, t0=None, t1=None) -> Dataset:
10451051
"""Select observations in time window between `t0` and `t1` (inclusive). For 1D datasets prefer to use `xarray.Dataset.sel`.
10461052
10471053
Parameters
@@ -1060,7 +1066,7 @@ def seltime(self, t0=None, t1=None) -> xr.Dataset:
10601066
"""
10611067

10621068
@abstractmethod
1063-
def iseltime(self, i) -> xr.Dataset:
1069+
def iseltime(self, i) -> Dataset:
10641070
"""Select observations by index (of non-nan, time, observation) across
10651071
trajectories. For 1D datasets prefer to use `xarray.Dataset.isel`.
10661072
@@ -1142,7 +1148,7 @@ def iseltime(self, i) -> xr.Dataset:
11421148
"""
11431149

11441150
@abstractmethod
1145-
def skill(self, expected, method='liu-weissberg', **kwargs) -> xr.Dataset:
1151+
def skill(self, expected, method='liu-weissberg', **kwargs) -> Dataset:
11461152
"""
11471153
Compare the skill score between this trajectory and an `expected` trajectory.
11481154
@@ -1209,7 +1215,7 @@ def skill(self, expected, method='liu-weissberg', **kwargs) -> xr.Dataset:
12091215
"""
12101216

12111217
@abstractmethod
1212-
def condense_obs(self) -> xr.Dataset:
1218+
def condense_obs(self) -> Dataset:
12131219
"""
12141220
Move all observations to the first index, so that the observation
12151221
dimension is reduced to a minimum. When creating ragged arrays the
@@ -1234,14 +1240,14 @@ def condense_obs(self) -> xr.Dataset:
12341240
"""
12351241

12361242
@abstractmethod
1237-
def to_1d(self) -> xr.Dataset:
1243+
def to_1d(self) -> Dataset:
12381244
"""
12391245
Convert dataset into a 1D dataset from. This is only possible if the
12401246
dataset has a single trajectory.
12411247
"""
12421248

12431249
@abstractmethod
1244-
def to_2d(self, obs_dim='obs') -> xr.Dataset:
1250+
def to_2d(self, obs_dim='obs') -> Dataset:
12451251
"""Convert the dataset to a 2D representation.
12461252
12471253
Parameters
@@ -1256,13 +1262,13 @@ def to_2d(self, obs_dim='obs') -> xr.Dataset:
12561262
"""
12571263

12581264
@abstractmethod
1259-
def append(self, da, obs_dims=None) -> xr.Dataset:
1265+
def append(self, da, obs_dims=None) -> Dataset:
12601266
"""
12611267
Append trajectories from other dataset to this.
12621268
"""
12631269

12641270
@abstractmethod
1265-
def filter(self, method='speed', max_speed=10., nsigma=5.0, side_half_width=2) -> xr.Dataset:
1271+
def filter(self, method='speed', max_speed=10., nsigma=5.0, side_half_width=2) -> Dataset:
12661272
"""Filter outlier positions from trajectories.
12671273
12681274
Parameters

0 commit comments

Comments
 (0)