Skip to content

Commit 767ae0c

Browse files
committed
add CI/CD tools and linting
1 parent 91f60c4 commit 767ae0c

21 files changed

Lines changed: 258 additions & 70 deletions

.github/workflows/ci.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
lint:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
- uses: actions/setup-python@v5
15+
with:
16+
python-version: "3.13"
17+
- run: pip install ruff
18+
- run: ruff check .
19+
- run: ruff format --check .
20+
21+
typecheck:
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@v4
25+
- uses: actions/setup-python@v5
26+
with:
27+
python-version: "3.13"
28+
- run: pip install mypy numpy
29+
- run: mypy timedatamodel/
30+
31+
test:
32+
runs-on: ubuntu-latest
33+
strategy:
34+
matrix:
35+
python-version: ["3.11", "3.12", "3.13"]
36+
steps:
37+
- uses: actions/checkout@v4
38+
- uses: actions/setup-python@v5
39+
with:
40+
python-version: ${{ matrix.python-version }}
41+
- run: pip install -e ".[dev]" pytest-cov
42+
- run: pytest tests/ --cov=timedatamodel --cov-report=term-missing

.pre-commit-config.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
repos:
2+
- repo: https://github.com/astral-sh/ruff-pre-commit
3+
rev: v0.8.6
4+
hooks:
5+
- id: ruff
6+
args: [--fix]
7+
- id: ruff-format

pyproject.toml

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,17 @@ pint = ["pint>=0.24"]
3737
geo = ["shapely>=2.0"]
3838
polars = ["polars>=1.0"]
3939
all = ["pandas>=2.0", "pint>=0.24", "shapely>=2.0", "polars>=1.0"]
40-
dev = ["pytest>=8.0", "pandas>=2.0", "pint>=0.24", "shapely>=2.0", "polars>=1.0"]
40+
dev = [
41+
"pytest>=8.0",
42+
"pytest-cov>=5.0",
43+
"ruff>=0.8",
44+
"mypy>=1.13",
45+
"pre-commit>=4.0",
46+
"pandas>=2.0",
47+
"pint>=0.24",
48+
"shapely>=2.0",
49+
"polars>=1.0",
50+
]
4151
docs = [
4252
"sphinx>=7.4",
4353
"myst-parser>=3.0",
@@ -50,3 +60,61 @@ docs = [
5060
[build-system]
5161
requires = ["hatchling"]
5262
build-backend = "hatchling.build"
63+
64+
[tool.ruff]
65+
target-version = "py311"
66+
line-length = 99
67+
68+
[tool.ruff.lint]
69+
select = [
70+
"E", # pycodestyle errors
71+
"W", # pycodestyle warnings
72+
"F", # pyflakes
73+
"I", # isort
74+
"UP", # pyupgrade
75+
"B", # flake8-bugbear
76+
"SIM", # flake8-simplify
77+
"TCH", # flake8-type-checking
78+
]
79+
ignore = [
80+
"E501", # line too long (handled by formatter)
81+
"E402", # module level import not at top (circular import workarounds)
82+
"F821", # undefined name (quoted forward references)
83+
"F841", # local variable assigned but unused (intentional unpacking)
84+
"UP007", # use X | Y for union types (NamedTuple compat)
85+
"UP017", # use datetime.UTC alias (keep timezone.utc for broader compat)
86+
"UP035", # import from collections.abc (keep typing imports)
87+
"UP037", # remove quotes from type annotation (forward references)
88+
"B007", # unused loop variable (intentional unpacking)
89+
"B905", # zip() without strict= (Python 3.10+ style, keep compat)
90+
"E741", # ambiguous variable name (single-letter in comprehensions)
91+
"SIM105", # contextlib.suppress (explicit try/except preferred for clarity)
92+
"SIM108", # ternary operator (readability preference)
93+
"SIM114", # combine if branches (readability preference)
94+
"TC001", # move import into TYPE_CHECKING (circular import issues)
95+
"TC002", # move third-party import into TYPE_CHECKING
96+
"TC003", # move stdlib import into TYPE_CHECKING (keep simple)
97+
]
98+
99+
[tool.ruff.lint.per-file-ignores]
100+
"tests/**" = ["B015", "B018"]
101+
102+
[tool.ruff.lint.isort]
103+
known-first-party = ["timedatamodel"]
104+
105+
[tool.mypy]
106+
python_version = "3.11"
107+
warn_return_any = true
108+
warn_unused_configs = true
109+
disallow_untyped_defs = false
110+
ignore_missing_imports = true
111+
112+
[tool.pytest.ini_options]
113+
testpaths = ["tests"]
114+
115+
[tool.coverage.run]
116+
source = ["timedatamodel"]
117+
118+
[tool.coverage.report]
119+
show_missing = true
120+
skip_empty = true

tests/conftest.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from __future__ import annotations
2+
3+
from datetime import UTC, datetime, timedelta
4+
5+
import pytest
6+
7+
8+
@pytest.fixture
9+
def utc_timestamps():
10+
"""Five hourly UTC timestamps starting 2024-01-01."""
11+
base = datetime(2024, 1, 1, tzinfo=UTC)
12+
return [base + timedelta(hours=i) for i in range(5)]
13+
14+
15+
@pytest.fixture
16+
def sample_values():
17+
"""Simple float values with one None (missing)."""
18+
return [1.0, 2.0, 3.0, None, 5.0]

tests/test_array.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
import timedatamodel as tdm
99

10-
1110
# ---------------------------------------------------------------------------
1211
# Fixtures
1312
# ---------------------------------------------------------------------------

tests/test_backend.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
from datetime import datetime, timedelta, timezone
1+
from datetime import datetime, timedelta
22

33
import pandas as pd
44
import polars as pl
55
import pytest
66

77
import timedatamodel as tdm
8-
from timedatamodel._base import _default_dataframe_backend
98

109

1110
@pytest.fixture(autouse=True)

tests/test_geospatial.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import pytest
21
from datetime import datetime
32

4-
import timedatamodel as tdm
3+
import pytest
54

5+
import timedatamodel as tdm
66

77
# ---- GeoLocation methods --------------------------------------------------
88

tests/test_hierarchy.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import pytest
21
from datetime import datetime
32

4-
import timedatamodel as tdm
3+
import pytest
54

5+
import timedatamodel as tdm
66

77
# ---- fixtures --------------------------------------------------------------
88

tests/test_theme.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44

55
import pytest
66

7+
from timedatamodel._repr import _get_repr_css
78
from timedatamodel._theme import (
89
_DEFAULT_THEME,
910
get_theme,
1011
get_theme_version,
1112
reset_theme,
1213
set_theme,
1314
)
14-
from timedatamodel._repr import _get_repr_css
1515

1616

1717
@pytest.fixture(autouse=True)

timedatamodel/__init__.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,32 @@
33
from ._base import get_default_df, set_default_df
44
from ._repr import CoverageBar, HierarchyTree, get_repr_width, set_repr_width
55
from ._theme import get_theme, reset_theme, set_theme
6+
from .array import Dimension, NDTimeSeries, TimeSeriesArray
7+
from .collection import TimeSeriesCollection
8+
from .datapoint import DataPoint
69
from .enums import DataType, Frequency, TimeSeriesType
10+
from .hierarchy import AggregationMethod, HierarchicalTimeSeries, HierarchyNode
711
from .location import GeoArea, GeoLocation, Location
8-
from .datapoint import DataPoint
12+
from .table import TimeSeriesTable
913
from .timeseries import TimeSeriesList
10-
from .table import TimeSeriesTable, MultivariateTimeSeries, MultiTimeSeries
11-
from .collection import TimeSeriesCollection
12-
from .array import Dimension, NDTimeSeries, TimeSeriesArray
13-
from .hierarchy import AggregationMethod, HierarchicalTimeSeries, HierarchyNode
1414

1515
__version__ = version("timedatamodel")
16+
17+
18+
def __getattr__(name: str):
19+
import warnings
20+
_deprecated_aliases = {
21+
"MultivariateTimeSeries": "TimeSeriesTable",
22+
"MultiTimeSeries": "TimeSeriesTable",
23+
}
24+
if name in _deprecated_aliases:
25+
warnings.warn(
26+
f"{name} is deprecated, use {_deprecated_aliases[name]} instead.",
27+
DeprecationWarning,
28+
stacklevel=2,
29+
)
30+
return TimeSeriesTable
31+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
1632
__all__ = [
1733
"AggregationMethod",
1834
"get_default_df",

0 commit comments

Comments
 (0)