From 2d45f528599b29671eb0043bbc06bcb2b409a1a0 Mon Sep 17 00:00:00 2001 From: Paul Natsuo Kishimoto Date: Thu, 16 Jan 2025 10:41:10 +0100 Subject: [PATCH 1/6] Bump mypy, ruff versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ruff v1.9.0 → v1.14.1. - Drop custom hook entry that uses PRE_COMMIT_MYPY_VENV. - Use config suggested at python/mypy#13916. - ruff v0.3.4 → v0.9.1 - Reformat 4 files. - Temporarily exclude *.ipynb files covered by new version. --- .pre-commit-config.yaml | 15 ++++----------- ixmp/backend/jdbc.py | 5 ++--- ixmp/cli.py | 7 +++---- ixmp/tests/backend/test_jdbc.py | 5 ++--- ixmp/tests/core/test_scenario.py | 8 ++++---- pyproject.toml | 9 +++++++-- 6 files changed, 22 insertions(+), 27 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 89b28ca82..8cb52c583 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,27 +1,20 @@ repos: -- repo: local +- repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.14.1 hooks: - id: mypy - name: mypy - always_run: true - require_serial: true pass_filenames: false - - language: python - entry: bash -c ". ${PRE_COMMIT_MYPY_VENV:-/dev/null}/bin/activate 2>/dev/null; mypy $0 $@" additional_dependencies: - - mypy >= 1.9.0 - genno - GitPython - nbclient - pandas-stubs - pytest - - sphinx + - Sphinx - werkzeug - xarray - args: ["."] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.3.4 + rev: v0.9.1 hooks: - id: ruff - id: ruff-format diff --git a/ixmp/backend/jdbc.py b/ixmp/backend/jdbc.py index 5c4e83ad8..ac8c864dc 100644 --- a/ixmp/backend/jdbc.py +++ b/ixmp/backend/jdbc.py @@ -170,7 +170,7 @@ def _domain_enum(domain): return domain_enum.valueOf(domain.upper()) except java.IllegalArgumentException: domains = ", ".join([d.name().lower() for d in domain_enum.values()]) - raise ValueError(f"No such domain: {domain}, " f"existing domains: {domains}") + raise ValueError(f"No such domain: {domain}, existing domains: {domains}") def _unwrap(v): @@ -886,8 +886,7 @@ def clone( # Raise exceptions for limitations of JDBCBackend if not isinstance(platform_dest._backend, self.__class__): raise NotImplementedError( # pragma: no cover - f"Clone between {self.__class__} and" - f"{platform_dest._backend.__class__}" + f"Clone between {self.__class__} and{platform_dest._backend.__class__}" ) elif platform_dest._backend is not self: package = s.__class__.__module__.split(".")[0] diff --git a/ixmp/cli.py b/ixmp/cli.py index d4649d28b..9fa61d28c 100644 --- a/ixmp/cli.py +++ b/ixmp/cli.py @@ -91,7 +91,7 @@ def report(context, config, key): if not context: raise click.UsageError( - "give either --url, --platform or --dbprops " "before command report" + "give either --url, --platform or --dbprops before command report" ) # Instantiate the Reporter with the Scenario loaded by main() @@ -227,8 +227,7 @@ def import_group(context): """ if not context or "scen" not in context: raise click.UsageError( - "give --url, or --platform, --model, and " - "--scenario, before command import" + "give --url, or --platform, --model, and --scenario, before command import" ) @@ -407,7 +406,7 @@ def list_scenarios(context, **kwargs): if not context: raise click.UsageError( - "give either --url, --platform or --dbprops " "before command list" + "give either --url, --platform or --dbprops before command list" ) print( diff --git a/ixmp/tests/backend/test_jdbc.py b/ixmp/tests/backend/test_jdbc.py index efc765c27..3642ee80c 100644 --- a/ixmp/tests/backend/test_jdbc.py +++ b/ixmp/tests/backend/test_jdbc.py @@ -373,8 +373,7 @@ def test_verbose_exception(test_mp, exception_verbose_true): exc_msg = exc_info.value.args[0] assert ( - "There exists no Scenario 'foo|bar' " - "(version: -1) in the database!" in exc_msg + "There exists no Scenario 'foo|bar' (version: -1) in the database!" in exc_msg ) assert "at.ac.iiasa.ixmp.database.DbDAO.getRunId" in exc_msg assert "at.ac.iiasa.ixmp.Platform.getScenario" in exc_msg @@ -595,7 +594,7 @@ def test_reload_cycle( mp = ixmp.Platform(**platform_args) # Load existing Scenario - s0 = ixmp.Scenario(mp, model="foo", scenario=f"bar {i-1}", version=1) + s0 = ixmp.Scenario(mp, model="foo", scenario=f"bar {i - 1}", version=1) memory_usage(f"pass {i} -- platform instantiated") diff --git a/ixmp/tests/core/test_scenario.py b/ixmp/tests/core/test_scenario.py index 3592280f0..f2b17b1ef 100644 --- a/ixmp/tests/core/test_scenario.py +++ b/ixmp/tests/core/test_scenario.py @@ -137,7 +137,7 @@ def test_init_set(self, scen): # Add set on a locked scenario with pytest.raises( RuntimeError, - match="This Scenario cannot be edited" ", do a checkout first!", + match="This Scenario cannot be edited, do a checkout first!", ): scen.init_set("foo") @@ -399,7 +399,7 @@ def test_excel_io(self, scen, scen_empty, tmp_path, caplog): # With init_items=False, can't be read into an empty Scenario. # Exception raised is the first index set, alphabetically - with pytest.raises(ValueError, match="no set 'i'; " "try init_items=True"): + with pytest.raises(ValueError, match="no set 'i'; try init_items=True"): scen_empty.read_excel(tmp_path) # File can be read with init_items=True @@ -441,7 +441,7 @@ def test_excel_io(self, scen, scen_empty, tmp_path, caplog): # Fails with add_units=False with pytest.raises( - ValueError, match="The unit 'pounds' does not exist" " in the database!" + ValueError, match="The unit 'pounds' does not exist in the database!" ): s.read_excel(tmp_path, init_items=True) @@ -623,7 +623,7 @@ def test_set(scen_empty) -> None: scen.add_set("i", ["i9", "extra"], ["i9 comment"]) # Missing element in the index set with pytest.raises( - ValueError, match="The index set 'i' does not have an " "element 'bar'!" + ValueError, match="The index set 'i' does not have an element 'bar'!" ): scen.add_set("foo", "bar") diff --git a/pyproject.toml b/pyproject.toml index ede4e147c..3c322b8f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -82,8 +82,9 @@ exclude_also = [ omit = ["ixmp/util/sphinx_linkcode_github.py"] [tool.mypy] -exclude = [ - "build/", +files = [ + "doc", + "ixmp", ] [[tool.mypy.overrides]] @@ -111,6 +112,10 @@ markers = [ ] tmp_path_retention_policy = "none" +[tool.ruff] +# TEMPORARY Exclude tutorial files +extend-exclude = ["*.ipynb"] + [tool.ruff.lint] select = ["C9", "E", "F", "I", "W"] # FIXME the following exceed this limit From 48989cc7e1d6e017ebe6f634dd69a184123eddba Mon Sep 17 00:00:00 2001 From: Paul Natsuo Kishimoto Date: Thu, 16 Jan 2025 10:49:17 +0100 Subject: [PATCH 2/6] Include *.ipynb in ruff format checking Format 2 files. --- pyproject.toml | 4 - tutorial/transport/py_transport.ipynb | 74 +++++++++++-------- .../transport/py_transport_scenario.ipynb | 64 +++++++++------- 3 files changed, 79 insertions(+), 63 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3c322b8f8..13989297e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -112,10 +112,6 @@ markers = [ ] tmp_path_retention_policy = "none" -[tool.ruff] -# TEMPORARY Exclude tutorial files -extend-exclude = ["*.ipynb"] - [tool.ruff.lint] select = ["C9", "E", "F", "I", "W"] # FIXME the following exceed this limit diff --git a/tutorial/transport/py_transport.ipynb b/tutorial/transport/py_transport.ipynb index efac6e40d..a36cd9fa6 100644 --- a/tutorial/transport/py_transport.ipynb +++ b/tutorial/transport/py_transport.ipynb @@ -54,8 +54,9 @@ "metadata": {}, "outputs": [], "source": [ - "# load required packages \n", + "# Import required packages\n", "import pandas as pd\n", + "\n", "import ixmp" ] }, @@ -65,7 +66,7 @@ "metadata": {}, "outputs": [], "source": [ - "# launch the ix modeling platform using the default back end\n", + "# Launch the ix modeling platform using the default back end\n", "mp = ixmp.Platform()\n", "\n", "# The following lines have the same effect:\n", @@ -79,14 +80,14 @@ "metadata": {}, "outputs": [], "source": [ - "# details for creating a new scenario in the ix modeling platform \n", + "# details for creating a new scenario in the ix modeling platform\n", "model = \"transport problem\"\n", "scenario = \"standard\"\n", - "annot = \"Dantzig's transportation problem for illustration and testing\" \n", + "annot = \"Dantzig's transportation problem for illustration and testing\"\n", "\n", "# initialize a new ixmp.Scenario\n", "# the parameter version='new' indicates that this is a new scenario instamce\n", - "scen = ixmp.Scenario(mp, model, scenario, version='new', annotation=annot)" + "scen = ixmp.Scenario(mp, model, scenario, version=\"new\", annotation=annot)" ] }, { @@ -114,11 +115,11 @@ "metadata": {}, "outputs": [], "source": [ - "# define the sets of locations of canning plants and markets \n", + "# define the sets of locations of canning plants and markets\n", "scen.init_set(\"i\")\n", "scen.add_set(\"i\", [\"seattle\", \"san-diego\"])\n", "scen.init_set(\"j\")\n", - "scen.add_set(\"j\", [\"new-york\", \"chicago\", \"topeka\"]) " + "scen.add_set(\"j\", [\"new-york\", \"chicago\", \"topeka\"])" ] }, { @@ -128,7 +129,7 @@ "outputs": [], "source": [ "# display the set 'i'\n", - "scen.set('i')" + "scen.set(\"i\")" ] }, { @@ -161,19 +162,19 @@ "metadata": {}, "outputs": [], "source": [ - "# capacity of plant i in cases \n", - "# add parameter elements one-by-one (string and value) \n", + "# capacity of plant i in cases\n", + "# add parameter elements one-by-one (string and value)\n", "scen.init_par(\"a\", idx_sets=\"i\")\n", "scen.add_par(\"a\", \"seattle\", 350, \"cases\")\n", "scen.add_par(\"a\", \"san-diego\", 600, \"cases\")\n", "\n", - "# demand at market j in cases \n", - "# add parameter elements as dataframe (with index names) \n", + "# demand at market j in cases\n", + "# add parameter elements as dataframe (with index names)\n", "scen.init_par(\"b\", idx_sets=\"j\")\n", "b_data = [\n", - " {'j': \"new-york\", 'value': 325, 'unit': \"cases\"},\n", - " {'j': \"chicago\", 'value': 300, 'unit': \"cases\"},\n", - " {'j': \"topeka\", 'value': 275, 'unit': \"cases\"}\n", + " {\"j\": \"new-york\", \"value\": 325, \"unit\": \"cases\"},\n", + " {\"j\": \"chicago\", \"value\": 300, \"unit\": \"cases\"},\n", + " {\"j\": \"topeka\", \"value\": 275, \"unit\": \"cases\"},\n", "]\n", "b = pd.DataFrame(b_data)\n", "scen.add_par(\"b\", b)" @@ -185,7 +186,7 @@ "metadata": {}, "outputs": [], "source": [ - "scen.par('b')" + "scen.par(\"b\")" ] }, { @@ -204,14 +205,14 @@ "metadata": {}, "outputs": [], "source": [ - "# distance in thousands of miles \n", + "# distance in thousands of miles\n", "scen.init_par(\"d\", idx_sets=[\"i\", \"j\"])\n", - "# add more parameter elements as dataframe by index names \n", + "# add more parameter elements as dataframe by index names\n", "d_data = [\n", - " {'i': \"seattle\", 'j': \"new-york\", 'value': 2.5, 'unit': \"km\"},\n", - " {'i': \"seattle\", 'j': \"chicago\", 'value': 1.7, 'unit': \"km\"},\n", - " {'i': \"seattle\", 'j': \"topeka\", 'value': 1.8, 'unit': \"km\"},\n", - " {'i': \"san-diego\", 'j': \"new-york\", 'value': 2.5, 'unit': \"km\"},\n", + " {\"i\": \"seattle\", \"j\": \"new-york\", \"value\": 2.5, \"unit\": \"km\"},\n", + " {\"i\": \"seattle\", \"j\": \"chicago\", \"value\": 1.7, \"unit\": \"km\"},\n", + " {\"i\": \"seattle\", \"j\": \"topeka\", \"value\": 1.8, \"unit\": \"km\"},\n", + " {\"i\": \"san-diego\", \"j\": \"new-york\", \"value\": 2.5, \"unit\": \"km\"},\n", "]\n", "d = pd.DataFrame(d_data)\n", "scen.add_par(\"d\", d)\n", @@ -223,9 +224,13 @@ }, { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ - "Scalar f freight in dollars per case per thousand miles /90/ ; " + "Scalar f freight in dollars per case per thousand miles /90/ ;" ] }, { @@ -234,8 +239,8 @@ "metadata": {}, "outputs": [], "source": [ - "# cost per case per 1000 miles \n", - "# initialize scalar with a value and a unit (and optionally a comment) \n", + "# cost per case per 1000 miles\n", + "# initialize scalar with a value and a unit (and optionally a comment)\n", "scen.init_scalar(\"f\", 90.0, \"USD/km\")" ] }, @@ -255,8 +260,8 @@ "# commit new scenario to the database\n", "# no changes can then be made to the scenario data until a check-out is performed\n", "comment = \"importing Dantzig's transport problem for illustration\"\n", - "comment += \" and testing of the Python interface using a generic datastructure\" \n", - "scen.commit(comment) \n", + "comment += \" and testing of the Python interface using a generic datastructure\"\n", + "scen.commit(comment)\n", "\n", "# set this new scenario as the default version for the model/scenario name\n", "scen.set_as_default()" @@ -273,12 +278,16 @@ }, { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "Variables\n", " x(i,j) shipment quantities in cases\n", " z total transportation costs in thousands of dollars ;\n", - " \n", + "\n", "Equations\n", " cost define objective function\n", " supply(i) observe supply limit at plant i\n", @@ -321,7 +330,7 @@ "metadata": {}, "outputs": [], "source": [ - "scen.solve(model='dantzig')" + "scen.solve(model=\"dantzig\")" ] }, { @@ -357,7 +366,8 @@ "metadata": {}, "outputs": [], "source": [ - "# display the quantities and marginals (=shadow prices) of the demand balance constraints\n", + "# Display the quantities and marginals (=shadow prices) of the demand balance\n", + "# constraints\n", "scen.equ(\"demand\")" ] }, diff --git a/tutorial/transport/py_transport_scenario.ipynb b/tutorial/transport/py_transport_scenario.ipynb index 23d2e075c..e75cd95ba 100644 --- a/tutorial/transport/py_transport_scenario.ipynb +++ b/tutorial/transport/py_transport_scenario.ipynb @@ -38,8 +38,7 @@ "metadata": {}, "outputs": [], "source": [ - "# load required packages\n", - "import pandas as pd\n", + "# Import required packages\n", "import ixmp" ] }, @@ -60,8 +59,8 @@ "outputs": [], "source": [ "# Model and scenario name for Dantzig's transport problem\n", - "model = 'canning problem'\n", - "scenario = 'standard'" + "model = \"canning problem\"\n", + "scenario = \"standard\"" ] }, { @@ -106,7 +105,8 @@ "outputs": [], "source": [ "from ixmp.testing import make_dantzig\n", - "scen = make_dantzig(mp, solve='.')" + "\n", + "scen = make_dantzig(mp, solve=\".\")" ] }, { @@ -135,8 +135,9 @@ "metadata": {}, "outputs": [], "source": [ - "# show only the distances for connections from Seattle by filtering the pandas.DataFrame returned above\n", - "d[d['i'] == \"seattle\"]" + "# Show only the distances for connections from Seattle by filtering the pandas.DataFrame\n", + "# returned above\n", + "d[d[\"i\"] == \"seattle\"]" ] }, { @@ -148,8 +149,8 @@ "# for faster access or more complex filtering,\n", "# it may be easier to only load specific parameter elements using a dictionary\n", "ele_filter = {}\n", - "ele_filter['i'] = ['seattle']\n", - "ele_filter['j'] = ['chicago', 'topeka']\n", + "ele_filter[\"i\"] = [\"seattle\"]\n", + "ele_filter[\"j\"] = [\"chicago\", \"topeka\"]\n", "\n", "d_filtered = scen.par(\"d\", ele_filter)\n", "d_filtered" @@ -172,7 +173,12 @@ "outputs": [], "source": [ "# create a new scenario by cloning the scenario (without keeping the solution)\n", - "scen_detroit = scen.clone(model=model, scenario='detroit', annotation='extend the Transport problem by a new city', keep_solution=False)" + "scen_detroit = scen.clone(\n", + " model=model,\n", + " scenario=\"detroit\",\n", + " annotation=\"extend the Transport problem by a new city\",\n", + " keep_solution=False,\n", + ")" ] }, { @@ -192,13 +198,13 @@ "outputs": [], "source": [ "# reduce demand in chicago\n", - "scen_detroit.add_par('b', 'chicago', 200, 'cases')\n", + "scen_detroit.add_par(\"b\", \"chicago\", 200, \"cases\")\n", "\n", "# add a new city with demand and distances\n", - "scen_detroit.add_set('j', 'detroit')\n", - "scen_detroit.add_par('b', 'detroit', 150, 'cases')\n", - "scen_detroit.add_par('d', ['seattle', 'detroit'], 1.7, 'cases')\n", - "scen_detroit.add_par('d', ['san-diego', 'detroit'], 1.9, 'cases')" + "scen_detroit.add_set(\"j\", \"detroit\")\n", + "scen_detroit.add_par(\"b\", \"detroit\", 150, \"cases\")\n", + "scen_detroit.add_par(\"d\", [\"seattle\", \"detroit\"], 1.7, \"cases\")\n", + "scen_detroit.add_par(\"d\", [\"san-diego\", \"detroit\"], 1.9, \"cases\")" ] }, { @@ -226,7 +232,7 @@ "metadata": {}, "outputs": [], "source": [ - "scen_detroit.solve(model='dantzig')" + "scen_detroit.solve(model=\"dantzig\")" ] }, { @@ -249,21 +255,21 @@ "outputs": [], "source": [ "# display the objective value of the solution in the baseline scenario\n", - "scen.var('z')" + "scen.var(\"z\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { - "jupyter": { - "name": "scen-detroit-z" - } + "jupyter": { + "name": "scen-detroit-z" + } }, "outputs": [], "source": [ "# display the objective value of the solution in the \"detroit\" scenario\n", - "scen_detroit.var('z')" + "scen_detroit.var(\"z\")" ] }, { @@ -272,8 +278,9 @@ "metadata": {}, "outputs": [], "source": [ - "# display the quantities transported from canning plants to demand locations in the baseline scenario\n", - "scen.var('x')" + "# Display the quantities transported from canning plants to demand locations in the\n", + "# baseline scenario\n", + "scen.var(\"x\")" ] }, { @@ -282,8 +289,9 @@ "metadata": {}, "outputs": [], "source": [ - "# display the quantities transported from canning plants to demand locations in the \"detroit\" scenario\n", - "scen_detroit.var('x')" + "# Display the quantities transported from canning plants to demand locations in the\n", + "# \"detroit\" scenario\n", + "scen_detroit.var(\"x\")" ] }, { @@ -292,7 +300,8 @@ "metadata": {}, "outputs": [], "source": [ - "# display the quantities and marginals (=shadow prices) of the demand balance constraints in the baseline scenario\n", + "# Display the quantities and marginals (=shadow prices) of the demand balance\n", + "# constraints in the baseline scenario\n", "scen.equ(\"demand\")" ] }, @@ -302,7 +311,8 @@ "metadata": {}, "outputs": [], "source": [ - "# display the quantities and marginals (=shadow prices) of the demand balance constraints in the \"detroit\" scenario\n", + "# Display the quantities and marginals (=shadow prices) of the demand balance\n", + "# constraints in the \"detroit\" scenario\n", "scen_detroit.equ(\"demand\")" ] }, From 5bf0bfc1dcbfb969b40aada731a27fc8006f2b6a Mon Sep 17 00:00:00 2001 From: Paul Natsuo Kishimoto Date: Thu, 16 Jan 2025 10:50:36 +0100 Subject: [PATCH 3/6] =?UTF-8?q?Update=20copyright=20year=202024=20?= =?UTF-8?q?=E2=86=92=202025?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- doc/conf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 359a98659..5bcb4e5a9 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ See [`doc/README.rst`](doc/README.rst) for further details. ## License -Copyright © 2017–2024 IIASA Energy, Climate, and Environment (ECE) program +Copyright © 2017–2025 IIASA Energy, Climate, and Environment (ECE) program `ixmp` is licensed under the Apache License, Version 2.0 (the "License"); you may not use the files in this repository except in compliance with the License. diff --git a/doc/conf.py b/doc/conf.py index a705e4c2c..57c34ddfa 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -10,7 +10,7 @@ # -- Project information --------------------------------------------------------------- project = "ixmp" -copyright = "2017–2024, IIASA Energy, Climate, and Environment (ECE) program" +copyright = "2017–%Y, IIASA Energy, Climate, and Environment (ECE) program" author = "ixmp Developers" From d0efe1456135dc8ed2bea674efeebddd57a005b6 Mon Sep 17 00:00:00 2001 From: Paul Natsuo Kishimoto Date: Mon, 20 Jan 2025 10:39:12 +0100 Subject: [PATCH 4/6] Use astral-sh/setup-uv in "pytest" GHA workflow - Discard commented workflow contents for pandas 2.0.0 pre-release. - Reduce scope of exclusion for ts-graphviz/setup-graphviz#630 workaround. - Use Python location from setup-uv instead of $pythonLocation. - Drop workaround for pyam-iamc exclusion of Python 3.13. - Add --durations=20 to pytest invocation. - Drop workaround for codecov/codecov-action#1316. --- .github/workflows/pytest.yaml | 74 ++++++++++++----------------------- 1 file changed, 25 insertions(+), 49 deletions(-) diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index cae59ac5d..7e69c5ba3 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -30,16 +30,7 @@ jobs: - "3.12" - "3.13" # Latest supported by ixmp gams-version: - # Version used until 2024-07; disabled - # - 25.1.1 - # First version including a macOS arm64 distribution - - 43.4.1 - - # commented: force a specific version of pandas, for e.g. pre-release - # testing - # pandas-version: - # - "" - # - "==2.0.0rc0" + - "43.4.1" # First version including a macOS arm64 distribution exclude: # Specific version combinations that are invalid / not to be used @@ -55,8 +46,6 @@ jobs: runs-on: ${{ matrix.os }} name: ${{ matrix.os }}-py${{ matrix.python-version }} - # commented: use with "pandas-version" in the matrix, above - # name: ${{ matrix.os }}-py${{ matrix.python-version }}-pandas${{ matrix.pandas-version }} steps: - uses: actions/checkout@v4 @@ -64,20 +53,19 @@ jobs: fetch-depth: ${{ env.depth }} fetch-tags: true - - uses: actions/setup-python@v5 + - name: Set up uv, Python + uses: astral-sh/setup-uv@v5 with: + cache-dependency-glob: "**/pyproject.toml" python-version: ${{ matrix.python-version }} - cache: pip - cache-dependency-path: "**/pyproject.toml" - uses: ts-graphviz/setup-graphviz@v2 - # TEMPORARY Work around ts-graphviz/setup-graphviz#630 - if: ${{ ! startswith(matrix.os, 'macos-') }} + # Work around ts-graphviz/setup-graphviz#630 + if: matrix.os != 'macos-13' - uses: r-lib/actions/setup-r@v2 id: setup-r - with: - r-version: "4.4.1" + with: { r-version: "4.4.1" } - name: Cache GAMS installer and R packages uses: actions/cache@v4 @@ -95,21 +83,13 @@ jobs: license: ${{ secrets.GAMS_LICENSE }} - name: Set RETICULATE_PYTHON - # Use the environment variable set by the setup-python action, above. - run: echo "RETICULATE_PYTHON=$pythonLocation" >> $GITHUB_ENV + # Retrieve the Python executable set up above + run: echo "RETICULATE_PYTHON=$(uv python find)" >> $GITHUB_ENV shell: bash - - name: Install Python package and dependencies - # [docs] contains [tests], which contains [report,tutorial] - run: | - pip install .[docs] - - # commented: use with "pandas-version" in the matrix, above - # pip install --upgrade pandas${{ matrix.pandas-version }} - - # TEMPORARY With Python 3.13 pyam-iamc resolves to 1.3.1, which in turn - # limits pint < 0.17. Override. cf. iiasa/ixmp#544 - pip install --upgrade pint + - name: Install the package and dependencies + # [docs] requires [tests] which requires [report,tutorial] + run: uv pip install .[docs] - name: Install R dependencies and tutorial requirements # Workaround for https://github.com/actions/runner-images/issues/11137 @@ -142,23 +122,20 @@ jobs: IRkernel::installspec() shell: Rscript {0} - - name: Run test suite using pytest + - name: Run tests run: | - pytest ixmp \ + uv run --no-sync \ + pytest ixmp \ -m "not performance" \ - --color=yes -rA --verbose \ + --color=yes --durations=20 -rA --verbose \ --cov-report=xml \ --numprocesses=auto --dist=loadgroup shell: bash - name: Upload test coverage to Codecov.io uses: codecov/codecov-action@v5 - # FIXME Limit runtime until - # https://github.com/codecov/codecov-action/issues/1316 is resolved - timeout-minutes: 1 - continue-on-error: true with: - token: ${{ secrets.CODECOV_TOKEN }} # required + token: ${{ secrets.CODECOV_TOKEN}} pre-commit: name: Code quality @@ -167,12 +144,11 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: { python-version: "3.12" } - - - name: Force recreation of pre-commit virtual environment for mypy - if: github.event_name == 'schedule' - run: gh cache list -L 999 | cut -f2 | grep pre-commit | xargs -I{} gh cache delete "{}" || true - env: { GH_TOKEN: "${{ github.token }}" } - - - uses: pre-commit/action@v3.0.1 + - uses: astral-sh/setup-uv@v5 + with: { cache-dependency-glob: "**/pyproject.toml" } + - uses: actions/cache@v4 + with: + path: ~/.cache/pre-commit + key: pre-commit|${{ env.UV_PYTHON }}|${{ hashFiles('.pre-commit-config.yaml') }} + lookup-only: ${{ github.event_name == 'schedule' }} # Set 'true' to recreate cache + - run: uvx pre-commit run --all-files --color=always --show-diff-on-failure From 1e260eb9b1a0efda92c9df762d56e288ab6a6b0e Mon Sep 17 00:00:00 2001 From: Paul Natsuo Kishimoto Date: Mon, 20 Jan 2025 11:04:34 +0100 Subject: [PATCH 5/6] Work around actions/checkout#2041 Borrowed from celeritas-project/celeritas#1580 --- .github/workflows/pytest.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index 7e69c5ba3..553c49c08 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -53,6 +53,9 @@ jobs: fetch-depth: ${{ env.depth }} fetch-tags: true + - name: TEMPORARY Work around actions/checkout#2041 + run: git fetch --tags + - name: Set up uv, Python uses: astral-sh/setup-uv@v5 with: From 96407b5056eabb3cb533886c2ab399cc22f42641 Mon Sep 17 00:00:00 2001 From: Paul Natsuo Kishimoto Date: Mon, 20 Jan 2025 12:08:18 +0100 Subject: [PATCH 6/6] Simplify installation of reticulate - Drop use of "remotes"; call install.packages() directly. - Install libpng-dev for compiling R 'png' package from source on Linux. - Drop workaround for actions/runner-images#11137; appears no longer necessary. --- .github/workflows/pytest.yaml | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index 553c49c08..0fff3445d 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -94,29 +94,13 @@ jobs: # [docs] requires [tests] which requires [report,tutorial] run: uv pip install .[docs] - - name: Install R dependencies and tutorial requirements - # Workaround for https://github.com/actions/runner-images/issues/11137 - if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12' }} - run: | - install.packages(c("remotes", "Rcpp")) - remotes::install_cran( - c("IRkernel", "reticulate"), - dependencies = TRUE, - # force = TRUE, - ) - - reticulate::py_config() - shell: Rscript {0} + - name: "Install libpng-dev" # for R 'png', required by reticulate + if: startsWith(matrix.os, 'ubuntu-') + run: sudo apt install libpng-dev - name: Install R dependencies and tutorial requirements - if: ${{ ! (matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12') }} run: | - install.packages(c("remotes", "Rcpp")) - remotes::install_cran( - c("IRkernel", "reticulate"), - dependencies = TRUE, - # force = TRUE, - ) + install.packages(c("IRkernel", "reticulate")) # commented: for debugging # print(reticulate::py_config())