From 4466deb1fb4e3e67d24c422e1900afe7ca5f4620 Mon Sep 17 00:00:00 2001 From: Paul Natsuo Kishimoto Date: Sun, 13 Jul 2025 23:03:30 +0200 Subject: [PATCH 01/12] Remove deleted .testing.nightly from .cli --- message_ix/cli.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/message_ix/cli.py b/message_ix/cli.py index 0f1db755b..0abd6aec2 100644 --- a/message_ix/cli.py +++ b/message_ix/cli.py @@ -130,11 +130,3 @@ def dl(branch, tag, path): # Add subcommands main.add_command(message_ix.tools.add_year.cli.main) main.add_command(message_ix.tools.lp_diag.cli.main) - -try: - import message_ix.testing.nightly -except ImportError: - # Dependencies of testing.nightly are missing; don't show the command - pass -else: - main.add_command(message_ix.testing.nightly.cli) From f57d41ffe9d98f08934bf2ff6e7e7d8c208c9c0f Mon Sep 17 00:00:00 2001 From: Paul Natsuo Kishimoto Date: Sun, 13 Jul 2025 23:05:19 +0200 Subject: [PATCH 02/12] Simplify .util.copy_model() --- message_ix/util/__init__.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/message_ix/util/__init__.py b/message_ix/util/__init__.py index d1795c9d5..0986f27b2 100644 --- a/message_ix/util/__init__.py +++ b/message_ix/util/__init__.py @@ -60,15 +60,13 @@ def output(str): ) def _exclude_path(p: Path) -> bool: - # Skip certain files + """Skip certain files.""" if p.suffix in (".gdx", ".log", ".lst") or re.search("225[a-z]+", str(p)): return False return True - paths = filter(_exclude_path, list(src_dir.rglob("*"))) - # Iterate over pre-filtered paths in `src_dir` - for original_path in paths: + for original_path in filter(_exclude_path, src_dir.rglob("*")): # Construct the destination path dst = path / original_path.relative_to(src_dir) From eaa4acd54c53172b26ffef956d73e792a8c183a8 Mon Sep 17 00:00:00 2001 From: Paul Natsuo Kishimoto Date: Sun, 13 Jul 2025 23:10:41 +0200 Subject: [PATCH 03/12] Use upstream ixmp.util.ixmp4.is_ixmp4backend() Drop message_ix.util.ixmp4.on_ixmp4backend(). --- message_ix/core.py | 5 ++--- message_ix/models.py | 4 ++-- message_ix/util/ixmp4.py | 21 ++------------------- message_ix/util/scenario_setup.py | 11 +++++------ 4 files changed, 11 insertions(+), 30 deletions(-) diff --git a/message_ix/core.py b/message_ix/core.py index b6ba47762..9818e9f5c 100755 --- a/message_ix/core.py +++ b/message_ix/core.py @@ -12,8 +12,7 @@ from ixmp.backend import ItemType from ixmp.backend.jdbc import JDBCBackend from ixmp.util import as_str_list, maybe_check_out, maybe_commit - -from message_ix.util.ixmp4 import on_ixmp4backend +from ixmp.util.ixmp4 import is_ixmp4backend # from message_ix.util.scenario_data import PARAMETERS @@ -266,7 +265,7 @@ def add_par( # failures. # TODO Move this upstream, to ixmp - if on_ixmp4backend(self): + if is_ixmp4backend(self.platform._backend): from message_ix.util.scenario_setup import check_existence_of_units # Check for existence of required units diff --git a/message_ix/models.py b/message_ix/models.py index 8d17a3018..b4709d413 100644 --- a/message_ix/models.py +++ b/message_ix/models.py @@ -15,8 +15,8 @@ from ixmp.backend import ItemType from ixmp.backend.jdbc import JDBCBackend from ixmp.util import maybe_check_out, maybe_commit +from ixmp.util.ixmp4 import is_ixmp4backend -from message_ix.util.ixmp4 import on_ixmp4backend from message_ix.util.scenario_data import REQUIRED_EQUATIONS, REQUIRED_VARIABLES if TYPE_CHECKING: @@ -476,7 +476,7 @@ def run(self, scenario: "ixmp.core.scenario.Scenario") -> None: compose_maps(scenario=scenario) - if on_ixmp4backend(scenario): + if is_ixmp4backend(scenario.platform._backend): # ixmp.model.gams.GAMSModel.__init__() creates the container_data attribute # from its .defaults and any user kwargs diff --git a/message_ix/util/ixmp4.py b/message_ix/util/ixmp4.py index d1e7f6a1c..de680af54 100644 --- a/message_ix/util/ixmp4.py +++ b/message_ix/util/ixmp4.py @@ -1,23 +1,6 @@ -from typing import Union - import ixmp import ixmp.backend - - -def on_ixmp4backend(obj: Union["ixmp.Platform", "ixmp.TimeSeries"]) -> bool: - """:any:`True` if `ts` is stored on :class:`IXMP4Backend`. - - .. todo:: Move upstream, to :mod:`ixmp`. - """ - if "ixmp4" not in ixmp.backend.available(): - return False - - from ixmp.backend.ixmp4 import IXMP4Backend - - return isinstance( - (obj if isinstance(obj, ixmp.Platform) else obj.platform)._backend, - IXMP4Backend, - ) +from ixmp.util.ixmp4 import is_ixmp4backend def platform_compat(platform: "ixmp.Platform") -> None: @@ -28,7 +11,7 @@ def platform_compat(platform: "ixmp.Platform") -> None: """ from message_ix.util.scenario_data import REQUIRED_UNITS - if not on_ixmp4backend(platform): + if not is_ixmp4backend(platform._backend): return if not platform._units_to_warn_about: diff --git a/message_ix/util/scenario_setup.py b/message_ix/util/scenario_setup.py index 393c61d6b..2e1863ec7 100644 --- a/message_ix/util/scenario_setup.py +++ b/message_ix/util/scenario_setup.py @@ -10,8 +10,7 @@ from message_ix.core import Scenario from ixmp import Platform - -from message_ix.util.ixmp4 import on_ixmp4backend +from ixmp.util.ixmp4 import is_ixmp4backend from .scenario_data import ( DEFAULT_INDEXSET_DATA, @@ -76,7 +75,7 @@ def add_default_data(scenario: "Scenario") -> None: """Add default data expected in a MESSAGEix Scenario.""" - if not on_ixmp4backend(scenario): + if not is_ixmp4backend(scenario.platform._backend): return # Get the Run associated with the Scenario @@ -128,7 +127,7 @@ def ensure_required_indexsets_have_data(scenario: "Scenario") -> None: ValueError If the required IndexSets are empty. """ - if not on_ixmp4backend(scenario): + if not is_ixmp4backend(scenario.platform._backend): return indexsets_to_check = ("node", "technology", "year", "time") @@ -175,7 +174,7 @@ def compose_dimension_map( dimension: 'node' or 'time' Whether to handle the spatial or temporal dimension. """ - if not on_ixmp4backend(scenario): + if not is_ixmp4backend(scenario.platform._backend): return # Get the Run associated with the Scenario @@ -325,7 +324,7 @@ def compose_period_map(scenario: "Scenario") -> None: This covers `assignPeriodMaps()` from ixmp_source. """ - if not on_ixmp4backend(scenario): + if not is_ixmp4backend(scenario.platform._backend): return # Get the Run associated with the Scenario From 76d28130054c4762420dec2777ad7f61d4bba4f8 Mon Sep 17 00:00:00 2001 From: Paul Natsuo Kishimoto Date: Sun, 13 Jul 2025 23:13:41 +0200 Subject: [PATCH 04/12] Satisfy mypy post- iiasa/ixmp#581 - Adjust hints in .core and .models. - Ignore missing type hints for pooch. - Remove outdated ignores for jpype, memory_profiler. --- message_ix/core.py | 21 +++++++++++++-------- message_ix/models.py | 43 ++++++++++++++++++++----------------------- pyproject.toml | 4 +--- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/message_ix/core.py b/message_ix/core.py index 9818e9f5c..9052796cc 100755 --- a/message_ix/core.py +++ b/message_ix/core.py @@ -1,7 +1,7 @@ import logging import os from collections.abc import Iterable, Mapping, Sequence -from functools import lru_cache, partial +from functools import lru_cache from itertools import chain, product, zip_longest from typing import Optional, TypeVar, Union from warnings import warn @@ -256,9 +256,9 @@ def add_par( key_or_data: Optional[ Union[int, str, Sequence[Union[int, str]], dict, pd.DataFrame] ] = None, - value=None, - unit: Optional[str] = None, - comment: Optional[str] = None, + value: Union[float, Iterable[float], None] = None, + unit: Union[str, Iterable[str], None] = None, + comment: Union[str, Iterable[str], None] = None, ) -> None: # ixmp.Scenario.add_par() is typed as accepting only str, but this method also # accepts int for "year"-like dimensions. Proxy the call to avoid type check @@ -293,7 +293,13 @@ def add_par( def add_set( self, name: str, - key: Union[int, str, Sequence[Union[str, int]], dict, pd.DataFrame], + key: Union[ + int, + str, + Iterable[object], + dict[str, Union[Sequence[int], Sequence[str]]], + pd.DataFrame, + ], comment: Union[str, Sequence[str], None] = None, ) -> None: # ixmp.Scenario.add_par() is typed as accepting only str, but this method also @@ -840,10 +846,9 @@ def rename(self, name: str, mapping: Mapping[str, str], keep: bool = False) -> N # - Iterate over tuples of (item_name, ix_type); only those indexed by `name`. # - First all sets indexed sets; then all parameters. - items = partial(self.items, indexed_by=name, par_data=False) for item_name, ix_type in chain( - zip_longest(items(ItemType.SET), [], fillvalue="set"), - zip_longest(items(ItemType.PAR), [], fillvalue="par"), + zip_longest(self.items(ItemType.SET, indexed_by=name), [], fillvalue="set"), + zip_longest(self.items(ItemType.PAR, indexed_by=name), [], fillvalue="par"), ): # Identify some index names of this set; only those where the corresponding # index set is `name` diff --git a/message_ix/models.py b/message_ix/models.py index b4709d413..7bbe888c6 100644 --- a/message_ix/models.py +++ b/message_ix/models.py @@ -11,6 +11,7 @@ from warnings import warn import ixmp.model.gams +import pandas as pd from ixmp import config from ixmp.backend import ItemType from ixmp.backend.jdbc import JDBCBackend @@ -22,9 +23,7 @@ if TYPE_CHECKING: from logging import LogRecord - import ixmp.core.scenario - - import message_ix.core + from ixmp.types import InitializeItemsKwargs log = logging.getLogger(__name__) @@ -127,19 +126,13 @@ def ix_type(self) -> str: """ return str(self.type.name).lower() - def to_dict(self) -> dict: - """Return the :class:`dict` representation used internally in :mod:`ixmp`. - - This has the keys: - - - :py:`ix_type`: same as :attr:`ix_type`. - - :py:`idx_sets`: same as :attr:`coords`. - - :py:`idx_names`: same as :attr:`dims`, but only included if these are (a) - non-empty and (b) different from :attr:`coords`. - """ - result = dict(ix_type=self.ix_type, idx_sets=self.coords) + def to_dict(self) -> "InitializeItemsKwargs": + """Return the :class:`dict` representation used internally in :mod:`ixmp`.""" + result: "InitializeItemsKwargs" = dict( + ix_type=self.ix_type, idx_sets=self.coords + ) if self.dims: - result.update(idx_names=self.dims) + result["idx_names"] = self.dims return result @@ -149,7 +142,7 @@ def _item_shorthand(cls, type, name, expr="", description=None, **kwargs): cls.items[name] = Item(name, type, expr, description=description, **kwargs) -def item(ix_type, expr, description: Optional[str] = None) -> dict: +def item(ix_type, expr, description: Optional[str] = None) -> "InitializeItemsKwargs": """Return a dict with idx_sets and idx_names, given a string `expr`. .. deprecated:: 3.8.0 @@ -209,7 +202,7 @@ def __init__(self, name=None, **model_options): super().__init__(name, **model_options) - def run(self, scenario: "ixmp.core.scenario.Scenario") -> None: + def run(self, scenario: "ixmp.Scenario") -> None: """Execute the model. GAMSModel creates a file named ``cplex.opt`` in the model directory containing @@ -248,7 +241,7 @@ def run(self, scenario: "ixmp.core.scenario.Scenario") -> None: return result -def _check_structure(scenario: "ixmp.core.scenario.Scenario"): +def _check_structure(scenario: "ixmp.Scenario"): """Check dimensionality of some items related to the storage representation. Yields a sequence of 4-tuples: @@ -338,7 +331,7 @@ class MESSAGE(GAMSModel): items: MutableMapping[str, Item] = dict() @staticmethod - def enforce(scenario: "message_ix.core.Scenario") -> None: + def enforce(scenario: "ixmp.Scenario") -> None: """Enforce data consistency in `scenario`.""" # Raise an exception if any of the storage items have incorrect dimensions, i.e. # non-empty error messages @@ -359,7 +352,11 @@ def enforce(scenario: "message_ix.core.Scenario") -> None: # Existing and expected contents existing = scenario.set(set_name) - expected = scenario.par(par_name).drop(columns=["value", "unit"]) + par_data = scenario.par(par_name) + assert isinstance(par_data, pd.DataFrame) and isinstance( + existing, pd.DataFrame + ) + expected = par_data.drop(columns=["value", "unit"]) if existing.equals(expected): continue # Contents are as expected; do nothing @@ -370,7 +367,7 @@ def enforce(scenario: "message_ix.core.Scenario") -> None: scenario.add_set(set_name, expected) @classmethod - def initialize(cls, scenario: "ixmp.core.scenario.Scenario") -> None: + def initialize(cls, scenario: "ixmp.Scenario") -> None: """Set up `scenario` with required sets and parameters for MESSAGE. See Also @@ -455,9 +452,9 @@ def initialize(cls, scenario: "ixmp.core.scenario.Scenario") -> None: # compose_maps(scenario=scenario) # Commit if anything was removed - maybe_commit(scenario, state, f"{cls.__name__}.initialize") + maybe_commit(scenario, bool(state), f"{cls.__name__}.initialize") - def run(self, scenario: "ixmp.core.scenario.Scenario") -> None: + def run(self, scenario: "ixmp.Scenario") -> None: from message_ix.core import Scenario from message_ix.util.gams_io import ( add_auxiliary_items_to_container_data_list, diff --git a/pyproject.toml b/pyproject.toml index d71b572f2..55a10b0f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -97,11 +97,9 @@ local_partial_types = true [[tool.mypy.overrides]] # Packages/modules for which no type hints are available. module = [ + "pooch", "pyam.*", "scipy.*", - # Indirectly via ixmp; this should be a subset of the list in ixmp's pyproject.toml - "jpype", - "memory_profiler", ] ignore_missing_imports = true From 143c0dc39d40da47fd54a1a6c963351e9cf2d5d7 Mon Sep 17 00:00:00 2001 From: Paul Natsuo Kishimoto Date: Fri, 18 Jul 2025 14:09:14 +0200 Subject: [PATCH 05/12] Avoid circular import in .models --- message_ix/models.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/message_ix/models.py b/message_ix/models.py index 7bbe888c6..4ac812422 100644 --- a/message_ix/models.py +++ b/message_ix/models.py @@ -18,8 +18,6 @@ from ixmp.util import maybe_check_out, maybe_commit from ixmp.util.ixmp4 import is_ixmp4backend -from message_ix.util.scenario_data import REQUIRED_EQUATIONS, REQUIRED_VARIABLES - if TYPE_CHECKING: from logging import LogRecord @@ -461,6 +459,7 @@ def run(self, scenario: "ixmp.Scenario") -> None: add_default_data_to_container_data_list, store_message_version, ) + from message_ix.util.scenario_data import REQUIRED_EQUATIONS, REQUIRED_VARIABLES from message_ix.util.scenario_setup import ( compose_maps, ensure_required_indexsets_have_data, From b1e6e3ab62ba6b6f3daa7676fc3220c91c10d911 Mon Sep 17 00:00:00 2001 From: Paul Natsuo Kishimoto Date: Sat, 19 Jul 2025 20:40:13 +0200 Subject: [PATCH 06/12] Call ixmp.Backend methods directly in .core Remove all uses of deprecated ixmp.TimeSeries._backend("method", ...) shorthand. --- message_ix/core.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/message_ix/core.py b/message_ix/core.py index 9052796cc..7ebdde762 100755 --- a/message_ix/core.py +++ b/message_ix/core.py @@ -209,7 +209,7 @@ def cat_list(self, name: str) -> list[str]: ------- list of str """ - return self._backend("cat_list", name) + return self.platform._backend.cat_list(self, name) def add_cat(self, name, cat, keys, is_unique=False): """Map elements from *keys* to category *cat* within set *name*. @@ -226,7 +226,9 @@ def add_cat(self, name, cat, keys, is_unique=False): If `True`, then *cat* must have only one element. An exception is raised if *cat* already has an element, or if ``len(keys) > 1``. """ - self._backend("cat_set_elements", name, str(cat), as_str_list(keys), is_unique) + self.platform._backend.cat_set_elements( + self, name, str(cat), as_str_list(keys), is_unique + ) def cat(self, name, cat): """Return a list of all set elements mapped to a category. @@ -246,7 +248,7 @@ def cat(self, name, cat): return list( map( int if name == "year" else lambda v: v, - self._backend("cat_get_elements", name, cat), + self.platform._backend.cat_get_elements(self, name, cat), ) ) From b27950d6c107b414dd1cedc98512cb4017111c6b Mon Sep 17 00:00:00 2001 From: Paul Natsuo Kishimoto Date: Sat, 19 Jul 2025 20:51:28 +0200 Subject: [PATCH 07/12] Bump mypy, ruff versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - mypy v1.14.1 → v0.17 - ruff v0.9.1 → v0.12.4 - Replace legacy "ruff" hook with "ruff-check". --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bae41928f..516be0e50 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.14.1 + rev: v1.17.0 hooks: - id: mypy pass_filenames: false @@ -15,9 +15,9 @@ repos: - types-PyYAML - types-requests - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.1 + rev: v0.12.4 hooks: - - id: ruff + - id: ruff-check - id: ruff-format args: [ --check ] From 4358e73c1700d332e9b70cdeae3279958a0f8763 Mon Sep 17 00:00:00 2001 From: Paul Natsuo Kishimoto Date: Sun, 20 Jul 2025 23:47:35 +0200 Subject: [PATCH 08/12] Gitignore .dmypy* --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 79655609c..13efd6122 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ tests/data/nightly # Python tooling, pytest, etc. .benchmarks .coverage* +.dmypy* .mypy_cache .pytest_cache .ruff_cache From 1a6e72b7c3324d173de23750b4679013de093cc8 Mon Sep 17 00:00:00 2001 From: Paul Natsuo Kishimoto Date: Sun, 20 Jul 2025 23:49:43 +0200 Subject: [PATCH 09/12] Check result contents in test_report() Distinguish current expected contents for ixmp.IXMP4Backend vs. JDBCBackend. --- message_ix/tests/test_report.py | 78 +++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 3 deletions(-) diff --git a/message_ix/tests/test_report.py b/message_ix/tests/test_report.py index b1af82844..d8fc549ab 100644 --- a/message_ix/tests/test_report.py +++ b/message_ix/tests/test_report.py @@ -1,6 +1,7 @@ import logging import re import sys +from collections import defaultdict from functools import partial from pathlib import Path @@ -13,6 +14,7 @@ from ixmp.backend.jdbc import JDBCBackend from ixmp.report import Reporter as ixmp_Reporter from ixmp.testing import assert_logs +from ixmp.util.ixmp4 import is_ixmp4backend from numpy.testing import assert_allclose from pandas.testing import assert_frame_equal, assert_series_equal @@ -131,6 +133,28 @@ def test_reporter_from_scenario( assert_qty_equal(vom, rep.get(vom_key)) +#: Expected number of data points for items in the make_dantzig() scenario. +EXP_LEN_DANTZIG = defaultdict( + lambda: 0, + ACT=8, + COMMODITY_BALANCE_GT=5, + COST_NODAL=6, + DEMAND=3, + OBJ=1, + OBJECTIVE=1, + PRICE_COMMODITY=3, + bound_activity_up=2, + demand=3, + duration_period=2, + duration_time=1, + input=6, + output=8, + ref_activity=2, + var_cost=6, +) +EXP_LEN_DANTZIG.update({"COMMODITY_BALANCE_GT-margin": 5, "OBJECTIVE-margin": 1}) + + def test_reporter_from_dantzig( request: pytest.FixtureRequest, test_mp: Platform ) -> None: @@ -140,7 +164,47 @@ def test_reporter_from_dantzig( rep = Reporter.from_scenario(scen) # Default target can be calculated - rep.get("all") + result = rep.get("all") + + # Result contains data for expected number of model data items + # With IXMP4Backend, `scen` and thus `result` does not include variables 'C' and 'I' + assert 268 + (0 if is_ixmp4backend(test_mp._backend) else 2) == len(result) + + # Items have expected length + for qty in result: + assert EXP_LEN_DANTZIG[qty.name] == len(qty) + + +#: Expected number of data points for items in the make_westeros() scenario. +EXP_LEN_WESTEROS = defaultdict( + lambda: 0, + ACT=23, + CAP=23, + CAP_NEW=12, + COST_NODAL=6, + COST_NODAL_NET=3, + DEMAND=3, + EMISS=6, + OBJ=1, + OBJECTIVE=1, + PRICE_COMMODITY=9, + capacity_factor=48, + demand=5, + duration_period=5, + duration_time=1, + emission_factor=12, + fix_cost=36, + growth_activity_up=6, + historical_activity=3, + historical_new_capacity=3, + input=24, + interestrate=5, + inv_cost=12, + output=48, + technical_lifetime=20, + var_cost=12, +) +EXP_LEN_WESTEROS.update({"OBJECTIVE-margin": 1}) def test_reporter_from_westeros( @@ -155,10 +219,18 @@ def test_reporter_from_westeros( configure(units={"replace": {"-": ""}}) # Default target can be calculated - rep.get("all") + result = rep.get("all") + + # Result contains data for expected number of model data items + # With IXMP4Backend, `scen` and thus `result` does not include variables 'C' and 'I' + assert 266 + (0 if is_ixmp4backend(test_mp._backend) else 2) == len(result) + + # Items have expected length + for qty in result: + assert EXP_LEN_WESTEROS[qty.name] == len(qty) # message default target can be calculated - # TODO if df is empty, year is cast to float + # FIXME if df is empty, year is cast to float obs = rep.get("message::default") # all expected reporting exists From 16d805d3a0450633124e70c5df0feb9a8740d71e Mon Sep 17 00:00:00 2001 From: Paul Natsuo Kishimoto Date: Sun, 20 Jul 2025 23:50:33 +0200 Subject: [PATCH 10/12] Always read "OBJECTIVE" equ on IXMP4Backend --- message_ix/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/message_ix/models.py b/message_ix/models.py index 4ac812422..3f9fa5f17 100644 --- a/message_ix/models.py +++ b/message_ix/models.py @@ -494,6 +494,7 @@ def run(self, scenario: "ixmp.Scenario") -> None: # Request only required Equations per default self.equ_list = self.equ_list or [] self.equ_list.extend(equation.gams_name for equation in REQUIRED_EQUATIONS) + self.equ_list.append("OBJECTIVE") # Request only required Variables per default self.var_list = self.var_list or [] From d09370981a4c06cdc439e9148f5c81caa22a15d5 Mon Sep 17 00:00:00 2001 From: Paul Natsuo Kishimoto Date: Mon, 21 Jul 2025 00:03:45 +0200 Subject: [PATCH 11/12] Use ixmp `main` branch in RTD build Temporary addition between the merge of iiasa/ixmp#581 and the next release. --- doc/requirements.in | 3 +- doc/requirements.txt | 92 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 83 insertions(+), 12 deletions(-) diff --git a/doc/requirements.in b/doc/requirements.in index d7e3b9593..06a4a0cec 100644 --- a/doc/requirements.in +++ b/doc/requirements.in @@ -1,7 +1,8 @@ -# Input file for pip-compile +# Input file for uv pip compile # Only specify the packages necessary for [docs] gitpython==3.1.44 +ixmp @ git+https://github.com/iiasa/ixmp.git@main numpydoc==1.8.0 pandas==2.2.3 sphinx==8.2.3 diff --git a/doc/requirements.txt b/doc/requirements.txt index 709dcc43e..cbd6eb578 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,5 +1,5 @@ # This file was autogenerated by uv via the following command: -# uv pip compile --strip-extras doc/requirements.in -o doc/requirements.txt +# uv pip compile --strip-extras requirements.in -o requirements.txt alabaster==0.7.16 # via sphinx babel==2.16.0 @@ -8,34 +8,85 @@ certifi==2024.7.4 # via requests charset-normalizer==3.2.0 # via requests +click==8.2.1 + # via + # dask + # ixmp +cloudpickle==3.1.1 + # via dask +dask==2025.7.0 + # via genno docutils==0.20.1 # via # pybtex-docutils # sphinx # sphinx-rtd-theme # sphinxcontrib-bibtex +et-xmlfile==2.0.0 + # via openpyxl +flexcache==0.3 + # via pint +flexparser==0.4 + # via pint +fsspec==2025.7.0 + # via dask +genno==1.28.2 + # via ixmp gitdb==4.0.10 # via gitpython gitpython==3.1.44 - # via -r doc/requirements.in + # via -r requirements.in idna==3.7 # via requests imagesize==1.4.1 # via sphinx +importlib-metadata==8.7.0 + # via ixmp +ixmp @ git+https://github.com/iiasa/ixmp.git@1730afd24238b4dad88a89c2d58d0fed2cd19dd0 + # via -r requirements.in jinja2==3.1.6 # via sphinx +jpype1==1.6.0 + # via ixmp latexcodec==2.0.1 # via pybtex +locket==1.0.0 + # via partd markupsafe==2.1.3 # via jinja2 numpy==2.3.0 - # via pandas + # via + # dask + # pandas + # xarray numpydoc==1.8.0 - # via -r doc/requirements.in -packaging==23.1 - # via sphinx + # via -r requirements.in +openpyxl==3.1.5 + # via ixmp +packaging==25.0 + # via + # dask + # jpype1 + # sphinx + # xarray pandas==2.2.3 - # via -r doc/requirements.in + # via + # -r requirements.in + # genno + # ixmp + # xarray +partd==1.4.2 + # via dask +pint==0.24.4 + # via + # genno + # ixmp +platformdirs==4.3.8 + # via + # genno + # pint +pyarrow==21.0.0 + # via pandas pybtex==0.24.0 # via # pybtex-docutils @@ -49,7 +100,11 @@ python-dateutil==2.9.0.post0 pytz==2025.2 # via pandas pyyaml==6.0.1 - # via pybtex + # via + # dask + # genno + # ixmp + # pybtex requests==2.32.4 # via sphinx roman-numerals-py==3.1.0 @@ -67,18 +122,18 @@ snowballstemmer==2.2.0 # via sphinx sphinx==8.2.3 # via - # -r doc/requirements.in + # -r requirements.in # numpydoc # sphinx-rtd-theme # sphinxcontrib-bibtex # sphinxcontrib-jquery # sphinxcontrib-serializinghtml sphinx-rtd-theme==3.0.2 - # via -r doc/requirements.in + # via -r requirements.in sphinxcontrib-applehelp==2.0.0 # via sphinx sphinxcontrib-bibtex==2.6.3 - # via -r doc/requirements.in + # via -r requirements.in sphinxcontrib-devhelp==2.0.0 # via sphinx sphinxcontrib-htmlhelp==2.1.0 @@ -93,7 +148,22 @@ sphinxcontrib-serializinghtml==1.1.9 # via sphinx tabulate==0.9.0 # via numpydoc +toolz==1.0.0 + # via + # dask + # partd +typing-extensions==4.14.1 + # via + # flexcache + # flexparser + # pint tzdata==2025.2 # via pandas urllib3==2.5.0 # via requests +xarray==2025.7.1 + # via + # genno + # ixmp +zipp==3.23.0 + # via importlib-metadata From ae9f89d8e085627b9ab68eba8e2495258f6ddefc Mon Sep 17 00:00:00 2001 From: Paul Natsuo Kishimoto Date: Mon, 21 Jul 2025 09:02:40 +0200 Subject: [PATCH 12/12] Add #963 to release notes --- RELEASE_NOTES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index 70bf85ad7..e6c5d7034 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -5,6 +5,7 @@ All changes ----------- - Document the :ref:`minimum version of Java ` required for :class:`ixmp.JDBCBackend ` (:pull:`962`). +- Improve type hinting (:pull:`963`). .. _v3.11.1: