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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
version: "0.10.9"
- run: uv python install 3.14
- run: uv sync --python 3.14 --all-extras --locked
- run: uv run ruff check --isolated --select E4,E7,E9,F src tests
- run: uv run ruff check src tests

qualify:
name: Qualify (${{ matrix.os }}, Python ${{ matrix.python-version }})
Expand Down
10 changes: 10 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,15 @@ exclude = ["/.workspace", "/publish.sh"]
[tool.ruff]
line-length = 100

[tool.ruff.lint]
# One definition, used by CI and by a bare `ruff check` alike. E4/E7/E9/F is what
# ruff enforces with no configuration at all; I, UP and B are the three that pay
# for themselves here - import order, syntax this package's floor (3.10) already
# supports, and the bugbear checks that catch a real defect rather than a style
# preference. The wider default set is not adopted: S102 and BLE001 both fire on
# deliberate choices - `exec` rebuilds a student's saved component, and a broad
# `except` is what stops one wrong component from ending their session.
select = ["E4", "E7", "E9", "F", "I", "UP", "B"]

[tool.pytest.ini_options]
testpaths = ["tests"]
8 changes: 5 additions & 3 deletions src/ml4t_coursework/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@

from __future__ import annotations

from collections.abc import Callable
from dataclasses import dataclass, field
from typing import Any, Callable
from typing import Any

import numpy as np
import pandas as pd
Expand Down Expand Up @@ -93,7 +94,8 @@ def same(left: Any, right: Any, tol: float = 1e-12) -> bool:
return bool(np.allclose(left.to_numpy(dtype=float), right.to_numpy(dtype=float),
atol=tol, rtol=0, equal_nan=True))
if isinstance(left, (list, tuple)) and isinstance(right, (list, tuple)):
return len(left) == len(right) and all(same(a, b, tol) for a, b in zip(left, right))
return len(left) == len(right) and all(
same(a, b, tol) for a, b in zip(left, right, strict=True))
if isinstance(left, pd.Index) and isinstance(right, pd.Index):
return left.equals(right)
if isinstance(left, np.ndarray) and isinstance(right, np.ndarray):
Expand Down Expand Up @@ -187,7 +189,7 @@ def evaluate(contract, obj) -> Conformance:
lambda: run_invariants(contract, obj),
]
names = ["interface", "leakage probe", "determinism", "domain invariants"]
for name, stage in zip(names, stages):
for name, stage in zip(names, stages, strict=True):
try:
result.checks.extend(stage())
except Failure as failure:
Expand Down
5 changes: 3 additions & 2 deletions src/ml4t_coursework/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
import inspect
import json
import textwrap
from typing import Any, Callable, Sequence
from collections.abc import Callable, Sequence
from typing import Any

from . import contracts, project
from .checks import Conformance, evaluate
Expand Down Expand Up @@ -187,7 +188,7 @@ def load_component(name: str, quiet: bool = False) -> Any:
def status() -> str:
"""Every component the course asks for, and where each one stands."""
lines = []
for name, contract in sorted(contracts.load_all().items()):
for name, _contract in sorted(contracts.load_all().items()):
meta = _meta(name)
if meta is None:
state = "not written yet"
Expand Down
5 changes: 3 additions & 2 deletions src/ml4t_coursework/contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@

import importlib
import pkgutil
from collections.abc import Callable
from dataclasses import dataclass
from types import ModuleType
from typing import Any, Callable
from typing import Any

CONTRACTS: dict[str, "Contract"] = {}
CONTRACTS: dict[str, Contract] = {}

_SOURCES: list[str] = [f"{__package__}.reference"]
_LOADED: set[str] = set()
Expand Down
2 changes: 1 addition & 1 deletion src/ml4t_coursework/course.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

from dataclasses import dataclass

COURSES: dict[str, "Course"] = {}
COURSES: dict[str, Course] = {}
_active: str | None = None


Expand Down
4 changes: 2 additions & 2 deletions src/ml4t_coursework/markets.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@

from __future__ import annotations

from collections.abc import Callable
from dataclasses import dataclass, field
from pathlib import Path
from typing import Callable

MARKETS: dict[str, "MarketSpec"] = {}
MARKETS: dict[str, MarketSpec] = {}

BARS_PER_YEAR = {"daily": 252, "8h": 3 * 365, "hourly": 24 * 365, "weekly": 52}

Expand Down
3 changes: 2 additions & 1 deletion src/ml4t_coursework/reference/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

from __future__ import annotations

from typing import Any, Callable
from collections.abc import Callable
from typing import Any

from ..checks import require

Expand Down
2 changes: 1 addition & 1 deletion src/ml4t_coursework/reference/fold_splitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def _interface(obj) -> None:
for k, fold in enumerate(folds):
require(isinstance(fold, tuple) and len(fold) == 2, "interface",
"each fold to be a (train, validation) pair", f"fold {k} is {type(fold).__name__}")
for part, label in zip(fold, ("train", "validation")):
for part, label in zip(fold, ("train", "validation"), strict=True):
require(len(part) > 0, "interface", f"a non-empty {label} block",
f"fold {k}'s {label} block is empty")

Expand Down
2 changes: 1 addition & 1 deletion src/ml4t_coursework/reference/model_gbm.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def __init__(self, seed: int = seed, max_depth: int = max_depth,
self.model_ = None
self.columns_ = None

def fit(self, X: pd.DataFrame, y: pd.Series) -> "GbmModel":
def fit(self, X: pd.DataFrame, y: pd.Series) -> GbmModel:
from sklearn.ensemble import HistGradientBoostingRegressor

self.columns_ = list(X.columns)
Expand Down
2 changes: 1 addition & 1 deletion src/ml4t_coursework/reference/model_linear.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def __init__(self, alpha: float = alpha):
self.intercept_ = None
self.columns_ = None

def fit(self, X: pd.DataFrame, y: pd.Series) -> "LinearModel":
def fit(self, X: pd.DataFrame, y: pd.Series) -> LinearModel:
self.columns_ = list(X.columns)
design = X.to_numpy(dtype=float)
centre = design.mean(axis=0)
Expand Down
2 changes: 1 addition & 1 deletion src/ml4t_coursework/reference/preprocessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def __init__(self, clip: float = clip):
self.spread_ = None
self.fill_ = None

def fit(self, X: pd.DataFrame) -> "Preprocessor":
def fit(self, X: pd.DataFrame) -> Preprocessor:
self.lower_ = X.quantile(self.clip)
self.upper_ = X.quantile(1 - self.clip)
trimmed = X.clip(self.lower_, self.upper_, axis=1)
Expand Down
3 changes: 2 additions & 1 deletion src/ml4t_coursework/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
from __future__ import annotations

import datetime as dt
from collections.abc import Callable
from dataclasses import dataclass
from typing import Any, Callable
from typing import Any

import numpy as np
import pandas as pd
Expand Down
13 changes: 11 additions & 2 deletions tests/test_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,17 @@
import pandas as pd
import pytest

from ml4t_coursework import (append_result, contract, load_component, project, report, results,
save_component, source_of, status)
from ml4t_coursework import (
append_result,
contract,
load_component,
project,
report,
results,
save_component,
source_of,
status,
)
from ml4t_coursework.components import _meta


Expand Down
4 changes: 3 additions & 1 deletion tests/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
import pytest

from ml4t_coursework import fixtures, markets, project, runner

# `ml4t_coursework.results` is shadowed by the function of the same name that the
# package re-exports, so the module's own names are imported directly.
from ml4t_coursework.results import REQUIRED, results as results_log
from ml4t_coursework.results import REQUIRED
from ml4t_coursework.results import results as results_log


@pytest.fixture
Expand Down
Loading