Skip to content

Commit 9fb0b97

Browse files
authored
Merge pull request #963 from iiasa/enh/ixmp-strict-mypy
Adjust for strict type hinting in ixmp
2 parents 9fce44b + ae9f89d commit 9fb0b97

File tree

13 files changed

+217
-99
lines changed

13 files changed

+217
-99
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ tests/data/nightly
4343
# Python tooling, pytest, etc.
4444
.benchmarks
4545
.coverage*
46+
.dmypy*
4647
.mypy_cache
4748
.pytest_cache
4849
.ruff_cache

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/pre-commit/mirrors-mypy
3-
rev: v1.14.1
3+
rev: v1.17.0
44
hooks:
55
- id: mypy
66
pass_filenames: false
@@ -15,9 +15,9 @@ repos:
1515
- types-PyYAML
1616
- types-requests
1717
- repo: https://github.com/astral-sh/ruff-pre-commit
18-
rev: v0.9.1
18+
rev: v0.12.4
1919
hooks:
20-
- id: ruff
20+
- id: ruff-check
2121
- id: ruff-format
2222
args: [ --check ]
2323

RELEASE_NOTES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ All changes
55
-----------
66

77
- Document the :ref:`minimum version of Java <install-java>` required for :class:`ixmp.JDBCBackend <ixmp.backend.jdbc.JDBCBackend>` (:pull:`962`).
8+
- Improve type hinting (:pull:`963`).
89

910
.. _v3.11.1:
1011

doc/requirements.in

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
# Input file for pip-compile
1+
# Input file for uv pip compile
22
# Only specify the packages necessary for [docs]
33

44
gitpython==3.1.44
5+
ixmp @ git+https://github.com/iiasa/ixmp.git@main
56
numpydoc==1.8.0
67
pandas==2.2.3
78
sphinx==8.2.3

doc/requirements.txt

Lines changed: 81 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# This file was autogenerated by uv via the following command:
2-
# uv pip compile --strip-extras doc/requirements.in -o doc/requirements.txt
2+
# uv pip compile --strip-extras requirements.in -o requirements.txt
33
alabaster==0.7.16
44
# via sphinx
55
babel==2.16.0
@@ -8,34 +8,85 @@ certifi==2024.7.4
88
# via requests
99
charset-normalizer==3.2.0
1010
# via requests
11+
click==8.2.1
12+
# via
13+
# dask
14+
# ixmp
15+
cloudpickle==3.1.1
16+
# via dask
17+
dask==2025.7.0
18+
# via genno
1119
docutils==0.20.1
1220
# via
1321
# pybtex-docutils
1422
# sphinx
1523
# sphinx-rtd-theme
1624
# sphinxcontrib-bibtex
25+
et-xmlfile==2.0.0
26+
# via openpyxl
27+
flexcache==0.3
28+
# via pint
29+
flexparser==0.4
30+
# via pint
31+
fsspec==2025.7.0
32+
# via dask
33+
genno==1.28.2
34+
# via ixmp
1735
gitdb==4.0.10
1836
# via gitpython
1937
gitpython==3.1.44
20-
# via -r doc/requirements.in
38+
# via -r requirements.in
2139
idna==3.7
2240
# via requests
2341
imagesize==1.4.1
2442
# via sphinx
43+
importlib-metadata==8.7.0
44+
# via ixmp
45+
ixmp @ git+https://github.com/iiasa/ixmp.git@1730afd24238b4dad88a89c2d58d0fed2cd19dd0
46+
# via -r requirements.in
2547
jinja2==3.1.6
2648
# via sphinx
49+
jpype1==1.6.0
50+
# via ixmp
2751
latexcodec==2.0.1
2852
# via pybtex
53+
locket==1.0.0
54+
# via partd
2955
markupsafe==2.1.3
3056
# via jinja2
3157
numpy==2.3.0
32-
# via pandas
58+
# via
59+
# dask
60+
# pandas
61+
# xarray
3362
numpydoc==1.8.0
34-
# via -r doc/requirements.in
35-
packaging==23.1
36-
# via sphinx
63+
# via -r requirements.in
64+
openpyxl==3.1.5
65+
# via ixmp
66+
packaging==25.0
67+
# via
68+
# dask
69+
# jpype1
70+
# sphinx
71+
# xarray
3772
pandas==2.2.3
38-
# via -r doc/requirements.in
73+
# via
74+
# -r requirements.in
75+
# genno
76+
# ixmp
77+
# xarray
78+
partd==1.4.2
79+
# via dask
80+
pint==0.24.4
81+
# via
82+
# genno
83+
# ixmp
84+
platformdirs==4.3.8
85+
# via
86+
# genno
87+
# pint
88+
pyarrow==21.0.0
89+
# via pandas
3990
pybtex==0.24.0
4091
# via
4192
# pybtex-docutils
@@ -49,7 +100,11 @@ python-dateutil==2.9.0.post0
49100
pytz==2025.2
50101
# via pandas
51102
pyyaml==6.0.1
52-
# via pybtex
103+
# via
104+
# dask
105+
# genno
106+
# ixmp
107+
# pybtex
53108
requests==2.32.4
54109
# via sphinx
55110
roman-numerals-py==3.1.0
@@ -67,18 +122,18 @@ snowballstemmer==2.2.0
67122
# via sphinx
68123
sphinx==8.2.3
69124
# via
70-
# -r doc/requirements.in
125+
# -r requirements.in
71126
# numpydoc
72127
# sphinx-rtd-theme
73128
# sphinxcontrib-bibtex
74129
# sphinxcontrib-jquery
75130
# sphinxcontrib-serializinghtml
76131
sphinx-rtd-theme==3.0.2
77-
# via -r doc/requirements.in
132+
# via -r requirements.in
78133
sphinxcontrib-applehelp==2.0.0
79134
# via sphinx
80135
sphinxcontrib-bibtex==2.6.3
81-
# via -r doc/requirements.in
136+
# via -r requirements.in
82137
sphinxcontrib-devhelp==2.0.0
83138
# via sphinx
84139
sphinxcontrib-htmlhelp==2.1.0
@@ -93,7 +148,22 @@ sphinxcontrib-serializinghtml==1.1.9
93148
# via sphinx
94149
tabulate==0.9.0
95150
# via numpydoc
151+
toolz==1.0.0
152+
# via
153+
# dask
154+
# partd
155+
typing-extensions==4.14.1
156+
# via
157+
# flexcache
158+
# flexparser
159+
# pint
96160
tzdata==2025.2
97161
# via pandas
98162
urllib3==2.5.0
99163
# via requests
164+
xarray==2025.7.1
165+
# via
166+
# genno
167+
# ixmp
168+
zipp==3.23.0
169+
# via importlib-metadata

message_ix/cli.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,3 @@ def dl(branch, tag, path):
130130
# Add subcommands
131131
main.add_command(message_ix.tools.add_year.cli.main)
132132
main.add_command(message_ix.tools.lp_diag.cli.main)
133-
134-
try:
135-
import message_ix.testing.nightly
136-
except ImportError:
137-
# Dependencies of testing.nightly are missing; don't show the command
138-
pass
139-
else:
140-
main.add_command(message_ix.testing.nightly.cli)

message_ix/core.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logging
22
import os
33
from collections.abc import Iterable, Mapping, Sequence
4-
from functools import lru_cache, partial
4+
from functools import lru_cache
55
from itertools import chain, product, zip_longest
66
from typing import Optional, TypeVar, Union
77
from warnings import warn
@@ -12,8 +12,7 @@
1212
from ixmp.backend import ItemType
1313
from ixmp.backend.jdbc import JDBCBackend
1414
from ixmp.util import as_str_list, maybe_check_out, maybe_commit
15-
16-
from message_ix.util.ixmp4 import on_ixmp4backend
15+
from ixmp.util.ixmp4 import is_ixmp4backend
1716

1817
# from message_ix.util.scenario_data import PARAMETERS
1918

@@ -210,7 +209,7 @@ def cat_list(self, name: str) -> list[str]:
210209
-------
211210
list of str
212211
"""
213-
return self._backend("cat_list", name)
212+
return self.platform._backend.cat_list(self, name)
214213

215214
def add_cat(self, name, cat, keys, is_unique=False):
216215
"""Map elements from *keys* to category *cat* within set *name*.
@@ -227,7 +226,9 @@ def add_cat(self, name, cat, keys, is_unique=False):
227226
If `True`, then *cat* must have only one element. An exception is
228227
raised if *cat* already has an element, or if ``len(keys) > 1``.
229228
"""
230-
self._backend("cat_set_elements", name, str(cat), as_str_list(keys), is_unique)
229+
self.platform._backend.cat_set_elements(
230+
self, name, str(cat), as_str_list(keys), is_unique
231+
)
231232

232233
def cat(self, name, cat):
233234
"""Return a list of all set elements mapped to a category.
@@ -247,7 +248,7 @@ def cat(self, name, cat):
247248
return list(
248249
map(
249250
int if name == "year" else lambda v: v,
250-
self._backend("cat_get_elements", name, cat),
251+
self.platform._backend.cat_get_elements(self, name, cat),
251252
)
252253
)
253254

@@ -257,16 +258,16 @@ def add_par(
257258
key_or_data: Optional[
258259
Union[int, str, Sequence[Union[int, str]], dict, pd.DataFrame]
259260
] = None,
260-
value=None,
261-
unit: Optional[str] = None,
262-
comment: Optional[str] = None,
261+
value: Union[float, Iterable[float], None] = None,
262+
unit: Union[str, Iterable[str], None] = None,
263+
comment: Union[str, Iterable[str], None] = None,
263264
) -> None:
264265
# ixmp.Scenario.add_par() is typed as accepting only str, but this method also
265266
# accepts int for "year"-like dimensions. Proxy the call to avoid type check
266267
# failures.
267268
# TODO Move this upstream, to ixmp
268269

269-
if on_ixmp4backend(self):
270+
if is_ixmp4backend(self.platform._backend):
270271
from message_ix.util.scenario_setup import check_existence_of_units
271272

272273
# Check for existence of required units
@@ -294,7 +295,13 @@ def add_par(
294295
def add_set(
295296
self,
296297
name: str,
297-
key: Union[int, str, Sequence[Union[str, int]], dict, pd.DataFrame],
298+
key: Union[
299+
int,
300+
str,
301+
Iterable[object],
302+
dict[str, Union[Sequence[int], Sequence[str]]],
303+
pd.DataFrame,
304+
],
298305
comment: Union[str, Sequence[str], None] = None,
299306
) -> None:
300307
# ixmp.Scenario.add_par() is typed as accepting only str, but this method also
@@ -841,10 +848,9 @@ def rename(self, name: str, mapping: Mapping[str, str], keep: bool = False) -> N
841848

842849
# - Iterate over tuples of (item_name, ix_type); only those indexed by `name`.
843850
# - First all sets indexed sets; then all parameters.
844-
items = partial(self.items, indexed_by=name, par_data=False)
845851
for item_name, ix_type in chain(
846-
zip_longest(items(ItemType.SET), [], fillvalue="set"),
847-
zip_longest(items(ItemType.PAR), [], fillvalue="par"),
852+
zip_longest(self.items(ItemType.SET, indexed_by=name), [], fillvalue="set"),
853+
zip_longest(self.items(ItemType.PAR, indexed_by=name), [], fillvalue="par"),
848854
):
849855
# Identify some index names of this set; only those where the corresponding
850856
# index set is `name`

0 commit comments

Comments
 (0)