Skip to content

Commit 9ae58a8

Browse files
committed
move to uv, ruff
1 parent ad33196 commit 9ae58a8

15 files changed

Lines changed: 1474 additions & 118 deletions

.github/workflows/ci.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: ["3.11", "3.12", "3.13"]
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Install uv
20+
uses: astral-sh/setup-uv@v4
21+
with:
22+
version: "latest"
23+
24+
- name: Set up Python ${{ matrix.python-version }}
25+
run: uv python install ${{ matrix.python-version }}
26+
27+
- name: Install dependencies
28+
run: uv sync --group dev --group test
29+
30+
- name: Run ruff linting
31+
run: uv run ruff check .
32+
33+
- name: Run ruff formatting check
34+
run: uv run ruff format --check .
35+
36+
- name: Run type checking
37+
run: uv run mypy fairlex/
38+
39+
- name: Run tests
40+
run: uv run pytest -q --color=yes
41+
42+
build:
43+
runs-on: ubuntu-latest
44+
needs: test
45+
steps:
46+
- uses: actions/checkout@v4
47+
48+
- name: Install uv
49+
uses: astral-sh/setup-uv@v4
50+
with:
51+
version: "latest"
52+
53+
- name: Set up Python
54+
run: uv python install
55+
56+
- name: Build package
57+
run: uv build

.github/workflows/docs.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Documentation
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
permissions:
10+
contents: read
11+
pages: write
12+
id-token: write
13+
14+
concurrency:
15+
group: "pages"
16+
cancel-in-progress: false
17+
18+
jobs:
19+
build:
20+
runs-on: ubuntu-latest
21+
22+
steps:
23+
- uses: actions/checkout@v4
24+
25+
- name: Install uv
26+
uses: astral-sh/setup-uv@v4
27+
with:
28+
version: "latest"
29+
30+
- name: Set up Python
31+
run: uv python install
32+
33+
- name: Install dependencies
34+
run: uv sync --group docs
35+
36+
- name: Build documentation
37+
run: |
38+
cd docs
39+
uv run sphinx-build -b html . _build/html
40+
41+
- name: Setup Pages
42+
uses: actions/configure-pages@v5
43+
44+
- name: Upload artifact
45+
uses: actions/upload-pages-artifact@v3
46+
with:
47+
path: docs/_build/html
48+
49+
deploy:
50+
environment:
51+
name: github-pages
52+
url: ${{ steps.deployment.outputs.page_url }}
53+
runs-on: ubuntu-latest
54+
needs: build
55+
if: github.ref == 'refs/heads/main'
56+
steps:
57+
- name: Deploy to GitHub Pages
58+
id: deployment
59+
uses: actions/deploy-pages@v4

.github/workflows/python-package.yml

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Publish Python Package
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version:
7+
description: 'Version to publish'
8+
required: false
9+
type: string
10+
11+
permissions:
12+
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
13+
14+
jobs:
15+
publish:
16+
name: Publish to PyPI
17+
runs-on: ubuntu-latest
18+
environment:
19+
name: pypi
20+
url: https://pypi.org/p/fairlex
21+
22+
steps:
23+
- uses: actions/checkout@v4
24+
25+
- name: Install uv
26+
uses: astral-sh/setup-uv@v4
27+
with:
28+
version: "latest"
29+
30+
- name: Set up Python
31+
run: uv python install
32+
33+
- name: Install dependencies
34+
run: uv sync --group dev
35+
36+
- name: Run linting
37+
run: uv run ruff check .
38+
39+
- name: Run formatting check
40+
run: uv run ruff format --check .
41+
42+
- name: Run type checking
43+
run: uv run mypy fairlex/
44+
45+
- name: Run tests
46+
run: uv run pytest -q --color=yes
47+
48+
- name: Build package
49+
run: uv build
50+
51+
- name: Publish to PyPI
52+
uses: pypa/gh-action-pypi-publish@release/v1
53+
with:
54+
verbose: true

.pre-commit-config.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
repos:
2+
- repo: https://github.com/astral-sh/ruff-pre-commit
3+
rev: v0.8.4
4+
hooks:
5+
- id: ruff
6+
args: [--fix]
7+
- id: ruff-format
8+
9+
- repo: https://github.com/pre-commit/mirrors-mypy
10+
rev: v1.12.1
11+
hooks:
12+
- id: mypy
13+
args: [--ignore-missing-imports]
14+
15+
- repo: https://github.com/pre-commit/pre-commit-hooks
16+
rev: v5.0.0
17+
hooks:
18+
- id: trailing-whitespace
19+
- id: end-of-file-fixer
20+
- id: check-yaml
21+
- id: check-toml
22+
- id: check-merge-conflict
23+
- id: check-case-conflict
24+
- id: check-docstring-first
25+
- id: debug-statements
26+
- id: check-added-large-files

Makefile

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
.PHONY: help clean install test test-cov lint format type-check docs docs-serve build upload dev-install
2+
3+
help: ## Show this help message
4+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
5+
6+
clean: ## Clean build artifacts
7+
rm -rf build/
8+
rm -rf dist/
9+
rm -rf *.egg-info/
10+
rm -rf .pytest_cache/
11+
rm -rf .coverage
12+
rm -rf htmlcov/
13+
rm -rf docs/build/
14+
find . -type d -name __pycache__ -delete
15+
find . -type f -name "*.pyc" -delete
16+
17+
install: ## Install package
18+
uv pip install -e .
19+
20+
dev-install: ## Install package with development dependencies
21+
uv sync --dev
22+
pre-commit install
23+
24+
test: ## Run tests
25+
pytest
26+
27+
test-cov: ## Run tests with coverage
28+
pytest --cov=fairlex --cov-report=html --cov-report=term
29+
30+
lint: ## Run linter
31+
ruff check .
32+
33+
format: ## Format code
34+
ruff format .
35+
ruff check --fix .
36+
37+
type-check: ## Run type checker
38+
mypy fairlex/
39+
40+
build: ## Build package
41+
uv build
42+
43+
upload: ## Upload to PyPI
44+
uv publish
45+
46+
ci: lint type-check test ## Run CI checks

examples/basic_example.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import numpy as np
99

10-
from fairlex import leximin_weight_fair, evaluate_solution
10+
from fairlex import evaluate_solution, leximin_weight_fair
1111

1212

1313
def main() -> None:
@@ -40,4 +40,4 @@ def main() -> None:
4040

4141

4242
if __name__ == "__main__":
43-
main()
43+
main()

fairlex/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@
2929
]
3030

3131
# Public API
32-
from .calibration import leximin_residual, leximin_weight_fair, CalibrationResult
32+
from .calibration import CalibrationResult, leximin_residual, leximin_weight_fair
3333
from .metrics import evaluate_solution
3434

3535
# Expose the package version at runtime
3636
try: # pragma: no cover - metadata may not be present in editable installs
3737
__version__ = version("fairlex")
3838
except Exception:
39-
__version__ = "0.0.0"
39+
__version__ = "0.0.0"

fairlex/calibration.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,13 @@
3535
from __future__ import annotations
3636

3737
from dataclasses import dataclass
38-
from typing import Callable, Optional, Tuple
3938

4039
import numpy as np
4140

4241
try:
4342
# SciPy is used for linear programming; HiGHS is fast and reliable.
4443
from scipy.optimize import linprog # type: ignore
45-
except Exception as e: # pragma: no cover
44+
except Exception: # pragma: no cover
4645
linprog = None # type: ignore
4746

4847

@@ -68,12 +67,12 @@ class CalibrationResult:
6867

6968
w: np.ndarray
7069
epsilon: float
71-
t: Optional[float]
70+
t: float | None
7271
status: int
7372
message: str
7473

7574

76-
def _validate_inputs(A: np.ndarray, b: np.ndarray, w0: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
75+
def _validate_inputs(A: np.ndarray, b: np.ndarray, w0: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
7776
"""Validate and coerce input arrays to ensure they have compatible shapes.
7877
7978
Parameters
@@ -233,7 +232,7 @@ def leximin_weight_fair(
233232
max_ratio: float = 10.0,
234233
slack: float = 0.0,
235234
return_stages: bool = False,
236-
) -> CalibrationResult | Tuple[CalibrationResult, CalibrationResult]:
235+
) -> CalibrationResult | tuple[CalibrationResult, CalibrationResult]:
237236
r"""Compute weights via residual leximin followed by weight-fair refinement.
238237
239238
This function first solves the residual minimisation problem as in
@@ -341,4 +340,4 @@ def leximin_weight_fair(
341340
stage2 = CalibrationResult(w=w, epsilon=epsilon_opt, t=t_opt, status=res.status, message=res.message)
342341
if return_stages:
343342
return stage1, stage2
344-
return stage2
343+
return stage2

fairlex/metrics.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ def evaluate_solution(
135135
# Example: for quantiles (0.99, 0.95, 0.5) we get q_vals[0]=0th?? Actually quantiles appended in order: (0.99, 0.95, 0.5, 0.0, 1.0)
136136
# We'll map them explicitly.
137137
# Build a dict for clarity
138-
q_map = {q: v for q, v in zip(quantiles + (0.0, 1.0), q_vals)}
138+
q_map = dict(zip(quantiles + (0.0, 1.0), q_vals, strict=False))
139139
weight_max = float(q_map[1.0])
140140
weight_min = float(q_map[0.0])
141141
# Sort the requested quantiles for deterministic mapping
@@ -170,4 +170,4 @@ def evaluate_solution(
170170
p95_rel_dev=float(np.percentile(rel_dev, 95)),
171171
median_rel_dev=float(np.median(rel_dev)),
172172
)
173-
return result
173+
return result

0 commit comments

Comments
 (0)