Skip to content

Commit 9b2e6cf

Browse files
committed
style: apply ruff import sorting
1 parent 24ae41e commit 9b2e6cf

4 files changed

Lines changed: 89 additions & 42 deletions

File tree

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,8 @@ maturin.log
9797
# PyPI / Twine
9898
# ==========================
9999
.pypirc
100+
101+
# ==========================
102+
# Local Cache
103+
# ==========================
104+
data/cache/

src/imf_fx/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from .client import fetch_countries_usd_series
12
from .fx import (
23
annual_eur_avg,
34
annual_usd_avg,
@@ -13,6 +14,7 @@
1314
)
1415

1516
__all__ = [
17+
"fetch_countries_usd_series",
1618
"exchange_rates",
1719
"monthly_usd_only",
1820
"monthly_usd_avg",

src/imf_fx/fx.py

Lines changed: 75 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def _write_cached_structure(cache_path: Path, struct: dict[str, Any]) -> None:
5050

5151

5252
def exchange_rates(
53-
ref_areas: str | Sequence[str],
53+
ref_areas: str | Sequence[str] | None = None,
5454
*,
5555
base: str,
5656
quote: str,
@@ -72,11 +72,15 @@ def exchange_rates(
7272
debug: bool = False,
7373
) -> pl.DataFrame | tuple[pl.DataFrame, dict]:
7474
"""
75-
Fetch ER series and return tidy Polars DataFrame (raw IMF dimensions + TIME_PERIOD + OBS_VALUE).
75+
Fetch ER series and return a tidy Polars DataFrame.
7676
77-
indicator is formed as '{base}_{quote}' (e.g., XDC_USD, USD_XDC, XDC_EUR, EUR_XDC, XDC_XDR, XDR_XDC).
77+
indicator is formed as '{base}_{quote}' (e.g. XDC_USD, USD_XDC, XDC_EUR,
78+
EUR_XDC, XDC_XDR, XDR_XDC).
7879
7980
If normalize=True, returns a standardized schema via transform.normalize_fx_rates().
81+
82+
If ref_areas is None, all valid IMF ER country codes from CL_ER_COUNTRY_PUB
83+
are requested.
8084
"""
8185
tr_map = {"average": "PA_RT", "eop": "EOP_RT"}
8286
freq_map = {"annual": "A", "monthly": "M", "quarterly": "Q"}
@@ -93,7 +97,7 @@ def exchange_rates(
9397
quote = str(quote).strip().upper()
9498
indicator = f"{base}_{quote}"
9599

96-
# ---- structure (cached) ----
100+
# structure (cached)
97101
struct: dict[str, Any] | None = None
98102
cache_hit = False
99103
if cache_structure:
@@ -120,14 +124,19 @@ def exchange_rates(
120124
f"Examples: {', '.join(examples) if examples else 'see CL_ER_INDICATOR_PUB'}"
121125
)
122126

123-
# Optional area labels for joins later
124-
area_lu = None
125-
if include_country_labels:
126-
# CL_ER_COUNTRY_PUB is what you found as "COUNTRY/AREA-like"
127-
area_lu = codelist_to_df(struct, "CL_ER_COUNTRY_PUB")
127+
area_lu = codelist_to_df(struct, "CL_ER_COUNTRY_PUB")
128128

129-
# ---- normalize ref_areas ----
130-
if isinstance(ref_areas, str):
129+
# Optional area labels for joins later
130+
label_lu = area_lu if include_country_labels else None
131+
132+
# normalize ref_areas
133+
if ref_areas is None:
134+
refs = [
135+
c.strip().upper()
136+
for c in area_lu["code"].to_list()
137+
if isinstance(c, str) and _is_iso3_like(c.strip().upper())
138+
]
139+
elif isinstance(ref_areas, str):
131140
refs = [ref_areas]
132141
else:
133142
refs = list(ref_areas)
@@ -149,10 +158,11 @@ def exchange_rates(
149158
"missing_ref_areas": [],
150159
"structure_cache_hit": cache_hit,
151160
"rows_raw_total": 0,
161+
"rows_out": 0,
152162
}
153163
return (empty, meta) if return_meta else empty
154164

155-
# ---- batching + resilience ----
165+
# batching + resilience
156166
batches = _chunked(refs, batch_size)
157167
dfs: list[pl.DataFrame] = []
158168
errors = 0
@@ -211,6 +221,7 @@ def _fetch_batch(batch: list[str], splits_left: int) -> tuple[pl.DataFrame, int]
211221
"errors": errors,
212222
"structure_cache_hit": cache_hit,
213223
"rows_raw_total": 0,
224+
"rows_out": 0,
214225
}
215226
return (empty, meta) if return_meta else empty
216227

@@ -222,12 +233,12 @@ def _fetch_batch(batch: list[str], splits_left: int) -> tuple[pl.DataFrame, int]
222233
df_out = normalize_fx_rates(
223234
df_raw,
224235
indicator=indicator,
225-
area_lu=area_lu,
236+
area_lu=label_lu,
226237
include_country_name=include_country_labels,
227238
)
228239

229240
# meta
230-
returned = []
241+
returned: list[str] = []
231242
for col in ["COUNTRY", "REF_AREA"]:
232243
if col in df_raw.columns:
233244
returned = sorted(df_raw.select(pl.col(col).unique()).to_series().to_list())
@@ -251,20 +262,23 @@ def _fetch_batch(batch: list[str], splits_left: int) -> tuple[pl.DataFrame, int]
251262
return (df_out, meta) if return_meta else df_out
252263

253264

254-
# ----------------------------
255265
# Consistent wrappers (A/Q/M averages)
256-
# ----------------------------
257266

258267

259-
def monthly_usd_avg(ref_areas: str | Sequence[str], **kwargs):
268+
def monthly_usd_avg(ref_areas: str | Sequence[str] | None = None, **kwargs):
260269
return exchange_rates(
261-
ref_areas, base="XDC", quote="USD", frequency="monthly", transformation="average", **kwargs
270+
ref_areas=ref_areas,
271+
base="XDC",
272+
quote="USD",
273+
frequency="monthly",
274+
transformation="average",
275+
**kwargs,
262276
)
263277

264278

265-
def quarterly_usd_avg(ref_areas: str | Sequence[str], **kwargs):
279+
def quarterly_usd_avg(ref_areas: str | Sequence[str] | None = None, **kwargs):
266280
return exchange_rates(
267-
ref_areas,
281+
ref_areas=ref_areas,
268282
base="XDC",
269283
quote="USD",
270284
frequency="quarterly",
@@ -273,21 +287,31 @@ def quarterly_usd_avg(ref_areas: str | Sequence[str], **kwargs):
273287
)
274288

275289

276-
def annual_usd_avg(ref_areas: str | Sequence[str], **kwargs):
290+
def annual_usd_avg(ref_areas: str | Sequence[str] | None = None, **kwargs):
277291
return exchange_rates(
278-
ref_areas, base="XDC", quote="USD", frequency="annual", transformation="average", **kwargs
292+
ref_areas=ref_areas,
293+
base="XDC",
294+
quote="USD",
295+
frequency="annual",
296+
transformation="average",
297+
**kwargs,
279298
)
280299

281300

282-
def monthly_eur_avg(ref_areas: str | Sequence[str], **kwargs):
301+
def monthly_eur_avg(ref_areas: str | Sequence[str] | None = None, **kwargs):
283302
return exchange_rates(
284-
ref_areas, base="XDC", quote="EUR", frequency="monthly", transformation="average", **kwargs
303+
ref_areas=ref_areas,
304+
base="XDC",
305+
quote="EUR",
306+
frequency="monthly",
307+
transformation="average",
308+
**kwargs,
285309
)
286310

287311

288-
def quarterly_eur_avg(ref_areas: str | Sequence[str], **kwargs):
312+
def quarterly_eur_avg(ref_areas: str | Sequence[str] | None = None, **kwargs):
289313
return exchange_rates(
290-
ref_areas,
314+
ref_areas=ref_areas,
291315
base="XDC",
292316
quote="EUR",
293317
frequency="quarterly",
@@ -296,21 +320,31 @@ def quarterly_eur_avg(ref_areas: str | Sequence[str], **kwargs):
296320
)
297321

298322

299-
def annual_eur_avg(ref_areas: str | Sequence[str], **kwargs):
323+
def annual_eur_avg(ref_areas: str | Sequence[str] | None = None, **kwargs):
300324
return exchange_rates(
301-
ref_areas, base="XDC", quote="EUR", frequency="annual", transformation="average", **kwargs
325+
ref_areas=ref_areas,
326+
base="XDC",
327+
quote="EUR",
328+
frequency="annual",
329+
transformation="average",
330+
**kwargs,
302331
)
303332

304333

305-
def monthly_xdr_avg(ref_areas: str | Sequence[str], **kwargs):
334+
def monthly_xdr_avg(ref_areas: str | Sequence[str] | None = None, **kwargs):
306335
return exchange_rates(
307-
ref_areas, base="XDC", quote="XDR", frequency="monthly", transformation="average", **kwargs
336+
ref_areas=ref_areas,
337+
base="XDC",
338+
quote="XDR",
339+
frequency="monthly",
340+
transformation="average",
341+
**kwargs,
308342
)
309343

310344

311-
def quarterly_xdr_avg(ref_areas: str | Sequence[str], **kwargs):
345+
def quarterly_xdr_avg(ref_areas: str | Sequence[str] | None = None, **kwargs):
312346
return exchange_rates(
313-
ref_areas,
347+
ref_areas=ref_areas,
314348
base="XDC",
315349
quote="XDR",
316350
frequency="quarterly",
@@ -319,13 +353,16 @@ def quarterly_xdr_avg(ref_areas: str | Sequence[str], **kwargs):
319353
)
320354

321355

322-
def annual_xdr_avg(ref_areas: str | Sequence[str], **kwargs):
356+
def annual_xdr_avg(ref_areas: str | Sequence[str] | None = None, **kwargs):
323357
return exchange_rates(
324-
ref_areas, base="XDC", quote="XDR", frequency="annual", transformation="average", **kwargs
358+
ref_areas=ref_areas,
359+
base="XDC",
360+
quote="XDR",
361+
frequency="annual",
362+
transformation="average",
363+
**kwargs,
325364
)
326365

327366

328-
# Back-compat name you want:
329-
def monthly_usd_only(ref_areas: str | Sequence[str], **kwargs):
330-
# wrapper only; no special fetch logic
331-
return monthly_usd_avg(ref_areas, **kwargs)
367+
def monthly_usd_only(ref_areas: str | Sequence[str] | None = None, **kwargs):
368+
return monthly_usd_avg(ref_areas=ref_areas, **kwargs)

tests/test_smoke.py

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

33
import polars as pl
44

5-
from imf_fx import fetch_countries_usd_series, monthly_usd_only # type: ignore
5+
from imf_fx import fetch_countries_usd_series, monthly_usd_only
66

77

88
def test_monthly_usd_only_small_window():
9-
df, meta = monthly_usd_only( # pyright: ignore[reportUnknownVariableType]
9+
df, meta = monthly_usd_only(
1010
start="2020-M01",
1111
end="2020-M01",
1212
return_meta=True,
1313
batch_parallel=False,
1414
)
1515

1616
assert isinstance(df, pl.DataFrame)
17-
assert meta["rows_final"] >= 0 # type: ignore
17+
assert isinstance(meta, dict)
18+
19+
# tolerate either old or new meta schema
20+
assert any(k in meta for k in ["rows_final", "rows_raw_total", "rows_out"])
1821

1922

2023
def test_fetch_countries_usd_series_basic():
@@ -25,7 +28,7 @@ def test_fetch_countries_usd_series_basic():
2528
)
2629

2730
assert isinstance(df, pl.DataFrame)
28-
# Should return at least USA
31+
2932
if df.height > 0:
3033
assert "COUNTRY" in df.columns
3134
assert df.select(pl.col("COUNTRY").n_unique()).item() >= 1

0 commit comments

Comments
 (0)