Skip to content

Commit 729d097

Browse files
committed
CI
1 parent 550ca2a commit 729d097

9 files changed

Lines changed: 971 additions & 703 deletions

File tree

.github/workflows/documentation.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
- uses: actions/checkout@v4
1717

1818
- name: Install uv
19-
uses: astral-sh/setup-uv@v4
19+
uses: astral-sh/setup-uv@v5
2020

2121
- name: Set up Python 3.10
2222
run: uv python install 3.10

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
uses: actions/checkout@v4
1717

1818
- name: Install uv
19-
uses: astral-sh/setup-uv@v4
19+
uses: astral-sh/setup-uv@v5
2020

2121
- name: Set up Python
2222
run: uv python install 3.11

.github/workflows/tests.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
- uses: actions/checkout@v4
2929

3030
- name: Install uv
31-
uses: astral-sh/setup-uv@v4
31+
uses: astral-sh/setup-uv@v5
3232

3333
- name: Set up Python ${{ matrix.config.py }}
3434
run: uv python install ${{ matrix.config.py }}
@@ -40,12 +40,14 @@ jobs:
4040
run: uv run pytest tests/ --cov=imf_reader --cov-report=xml
4141

4242
- name: Use Codecov to track coverage
43-
uses: codecov/codecov-action@v3
43+
uses: codecov/codecov-action@v5
4444
with:
4545
token: ${{ secrets.CODECOV_TOKEN }}
4646
files: ./coverage.xml
4747
fail_ci_if_error: true
4848
verbose: true
4949

5050
- name: Check code style
51-
run: uv run black src tests --check
51+
run: |
52+
uv run ruff format src tests --check
53+
uv run ruff check src tests

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ dependencies = [
2020
[project.optional-dependencies]
2121
dev = [
2222
"pytest>=8.2.0",
23-
"black>=24.4.2",
23+
"ruff>=0.6",
2424
"sphinx>=7.3.7",
2525
"myst-nb>=1.1.0",
2626
"autoapi>=2.0.1",

src/imf_reader/cache/dataframe.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ def decorator(fn: Any) -> Any:
4141
# Capture signature once at decoration time (P2 — avoids per-call overhead).
4242
sig = inspect.signature(fn)
4343

44+
# Sanitize fn.__qualname__ for use in filenames: nested-defined functions
45+
# produce names like "outer.<locals>.fetch" which contain "<" and ">",
46+
# both illegal on Windows (NTFS reserved characters → ENOTSUP/EINVAL on
47+
# write). Stripping them keeps cache keys portable across platforms.
48+
safe_qualname = fn.__qualname__.replace("<", "_").replace(">", "_")
49+
4450
def _get_sublayer_dir() -> Path:
4551
# Always re-resolve so set_cache_dir() takes effect even if the
4652
# listener registry has been cleared by a test fixture or other
@@ -53,7 +59,7 @@ def _make_cache_key(*args: Any, **kwargs: Any) -> str:
5359
key_repr = repr(sorted(bound.arguments.items()))
5460
digest = hashlib.sha256(key_repr.encode()).hexdigest()[:16]
5561
module = fn.__module__ or ""
56-
return f"{module}.{fn.__qualname__}__{digest}"
62+
return f"{module}.{safe_qualname}__{digest}"
5763

5864
def _cache_path(key: str, result: Any) -> Path:
5965
ext = ".parquet" if isinstance(result, pd.DataFrame) else ".pkl"
@@ -91,7 +97,7 @@ def _do_cache_clear() -> None:
9197
if not d.exists():
9298
return
9399
module = fn.__module__ or ""
94-
prefix = f"{module}.{fn.__qualname__}__"
100+
prefix = f"{module}.{safe_qualname}__"
95101
for p in d.iterdir():
96102
if p.name.startswith(prefix):
97103
p.unlink(missing_ok=True)

src/imf_reader/weo/api.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
from imf_reader.config import logger
1010
from imf_reader.weo import ValidMonths, Version
1111
from imf_reader.cache.dataframe import dataframe_cache
12-
from imf_reader.cache.legacy import _legacy_weo_api_clear_cache as clear_cache # noqa: F401
12+
from imf_reader.cache.legacy import (
13+
_legacy_weo_api_clear_cache as clear_cache, # noqa: F401
14+
)
1315
from imf_reader.utils import make_get_request
1416

1517
# Standard scale labels

tests/test_cache/test_config.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,31 @@ def _reset_config():
2323
cfg._cache_enabled = True
2424

2525

26-
def test_default_root_uses_platformdirs(monkeypatch: pytest.MonkeyPatch) -> None:
26+
def test_default_root_uses_platformdirs(
27+
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
28+
) -> None:
2729
"""get_active_root() uses platformdirs when no override or env var is set."""
2830
monkeypatch.delenv(cfg.ENV_VAR, raising=False)
29-
fake_base = "/fake/cache/dir"
31+
fake_base = str(tmp_path / "fake_cache")
3032

3133
with patch("platformdirs.user_cache_dir", return_value=fake_base) as mock_pdir:
3234
root = cfg.get_active_root()
3335

3436
mock_pdir.assert_called_once_with("imf_reader", appauthor=False)
35-
assert str(root).startswith(fake_base)
37+
# Compare via Path semantics so the test is portable across separator conventions.
38+
assert root.is_relative_to(Path(fake_base))
3639

3740

38-
def test_env_var_overrides_default(monkeypatch: pytest.MonkeyPatch) -> None:
41+
def test_env_var_overrides_default(
42+
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
43+
) -> None:
3944
"""IMF_READER_CACHE_DIR env var overrides the platformdirs default."""
40-
env_path = "/tmp/imf_cache_env"
45+
env_path = str(tmp_path / "env_cache")
4146
monkeypatch.setenv(cfg.ENV_VAR, env_path)
4247

4348
root = cfg.get_active_root()
4449

45-
assert str(root).startswith(env_path)
50+
assert root.is_relative_to(Path(env_path))
4651

4752

4853
def test_version_segment_in_path(monkeypatch: pytest.MonkeyPatch) -> None:

tests/test_cache/test_legacy_shims.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ def test_weo_clear_cache_no_warning_at_import() -> None:
6161

6262
with warnings.catch_warnings():
6363
warnings.simplefilter("error", DeprecationWarning)
64-
from imf_reader.weo import clear_cache # noqa: F401 — import itself is under test
64+
from imf_reader.weo import (
65+
clear_cache, # noqa: F401 — import itself is under test
66+
)
6567

6668

6769
def test_weo_clear_cache_signature() -> None:

0 commit comments

Comments
 (0)