Skip to content

Commit 58e801c

Browse files
authored
Merge pull request #555 from iiasa/enh/code-quality
Update code quality configuration
2 parents 3fa1191 + 96407b5 commit 58e801c

File tree

11 files changed

+131
-157
lines changed

11 files changed

+131
-157
lines changed

.github/workflows/pytest.yaml

+32-69
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,7 @@ jobs:
3030
- "3.12"
3131
- "3.13" # Latest supported by ixmp
3232
gams-version:
33-
# Version used until 2024-07; disabled
34-
# - 25.1.1
35-
# First version including a macOS arm64 distribution
36-
- 43.4.1
37-
38-
# commented: force a specific version of pandas, for e.g. pre-release
39-
# testing
40-
# pandas-version:
41-
# - ""
42-
# - "==2.0.0rc0"
33+
- "43.4.1" # First version including a macOS arm64 distribution
4334

4435
exclude:
4536
# Specific version combinations that are invalid / not to be used
@@ -55,29 +46,29 @@ jobs:
5546

5647
runs-on: ${{ matrix.os }}
5748
name: ${{ matrix.os }}-py${{ matrix.python-version }}
58-
# commented: use with "pandas-version" in the matrix, above
59-
# name: ${{ matrix.os }}-py${{ matrix.python-version }}-pandas${{ matrix.pandas-version }}
6049

6150
steps:
6251
- uses: actions/checkout@v4
6352
with:
6453
fetch-depth: ${{ env.depth }}
6554
fetch-tags: true
6655

67-
- uses: actions/setup-python@v5
56+
- name: TEMPORARY Work around actions/checkout#2041
57+
run: git fetch --tags
58+
59+
- name: Set up uv, Python
60+
uses: astral-sh/setup-uv@v5
6861
with:
62+
cache-dependency-glob: "**/pyproject.toml"
6963
python-version: ${{ matrix.python-version }}
70-
cache: pip
71-
cache-dependency-path: "**/pyproject.toml"
7264

7365
- uses: ts-graphviz/setup-graphviz@v2
74-
# TEMPORARY Work around ts-graphviz/setup-graphviz#630
75-
if: ${{ ! startswith(matrix.os, 'macos-') }}
66+
# Work around ts-graphviz/setup-graphviz#630
67+
if: matrix.os != 'macos-13'
7668

7769
- uses: r-lib/actions/setup-r@v2
7870
id: setup-r
79-
with:
80-
r-version: "4.4.1"
71+
with: { r-version: "4.4.1" }
8172

8273
- name: Cache GAMS installer and R packages
8374
uses: actions/cache@v4
@@ -95,45 +86,21 @@ jobs:
9586
license: ${{ secrets.GAMS_LICENSE }}
9687

9788
- name: Set RETICULATE_PYTHON
98-
# Use the environment variable set by the setup-python action, above.
99-
run: echo "RETICULATE_PYTHON=$pythonLocation" >> $GITHUB_ENV
89+
# Retrieve the Python executable set up above
90+
run: echo "RETICULATE_PYTHON=$(uv python find)" >> $GITHUB_ENV
10091
shell: bash
10192

102-
- name: Install Python package and dependencies
103-
# [docs] contains [tests], which contains [report,tutorial]
104-
run: |
105-
pip install .[docs]
106-
107-
# commented: use with "pandas-version" in the matrix, above
108-
# pip install --upgrade pandas${{ matrix.pandas-version }}
109-
110-
# TEMPORARY With Python 3.13 pyam-iamc resolves to 1.3.1, which in turn
111-
# limits pint < 0.17. Override. cf. iiasa/ixmp#544
112-
pip install --upgrade pint
93+
- name: Install the package and dependencies
94+
# [docs] requires [tests] which requires [report,tutorial]
95+
run: uv pip install .[docs]
11396

114-
- name: Install R dependencies and tutorial requirements
115-
# Workaround for https://github.com/actions/runner-images/issues/11137
116-
if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12' }}
117-
run: |
118-
install.packages(c("remotes", "Rcpp"))
119-
remotes::install_cran(
120-
c("IRkernel", "reticulate"),
121-
dependencies = TRUE,
122-
# force = TRUE,
123-
)
124-
125-
reticulate::py_config()
126-
shell: Rscript {0}
97+
- name: "Install libpng-dev" # for R 'png', required by reticulate
98+
if: startsWith(matrix.os, 'ubuntu-')
99+
run: sudo apt install libpng-dev
127100

128101
- name: Install R dependencies and tutorial requirements
129-
if: ${{ ! (matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12') }}
130102
run: |
131-
install.packages(c("remotes", "Rcpp"))
132-
remotes::install_cran(
133-
c("IRkernel", "reticulate"),
134-
dependencies = TRUE,
135-
# force = TRUE,
136-
)
103+
install.packages(c("IRkernel", "reticulate"))
137104
138105
# commented: for debugging
139106
# print(reticulate::py_config())
@@ -142,23 +109,20 @@ jobs:
142109
IRkernel::installspec()
143110
shell: Rscript {0}
144111

145-
- name: Run test suite using pytest
112+
- name: Run tests
146113
run: |
147-
pytest ixmp \
114+
uv run --no-sync \
115+
pytest ixmp \
148116
-m "not performance" \
149-
--color=yes -rA --verbose \
117+
--color=yes --durations=20 -rA --verbose \
150118
--cov-report=xml \
151119
--numprocesses=auto --dist=loadgroup
152120
shell: bash
153121

154122
- name: Upload test coverage to Codecov.io
155123
uses: codecov/codecov-action@v5
156-
# FIXME Limit runtime until
157-
# https://github.com/codecov/codecov-action/issues/1316 is resolved
158-
timeout-minutes: 1
159-
continue-on-error: true
160124
with:
161-
token: ${{ secrets.CODECOV_TOKEN }} # required
125+
token: ${{ secrets.CODECOV_TOKEN}}
162126

163127
pre-commit:
164128
name: Code quality
@@ -167,12 +131,11 @@ jobs:
167131

168132
steps:
169133
- uses: actions/checkout@v4
170-
- uses: actions/setup-python@v5
171-
with: { python-version: "3.12" }
172-
173-
- name: Force recreation of pre-commit virtual environment for mypy
174-
if: github.event_name == 'schedule'
175-
run: gh cache list -L 999 | cut -f2 | grep pre-commit | xargs -I{} gh cache delete "{}" || true
176-
env: { GH_TOKEN: "${{ github.token }}" }
177-
178-
- uses: pre-commit/action@v3.0.1
134+
- uses: astral-sh/setup-uv@v5
135+
with: { cache-dependency-glob: "**/pyproject.toml" }
136+
- uses: actions/cache@v4
137+
with:
138+
path: ~/.cache/pre-commit
139+
key: pre-commit|${{ env.UV_PYTHON }}|${{ hashFiles('.pre-commit-config.yaml') }}
140+
lookup-only: ${{ github.event_name == 'schedule' }} # Set 'true' to recreate cache
141+
- run: uvx pre-commit run --all-files --color=always --show-diff-on-failure

.pre-commit-config.yaml

+4-11
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,20 @@
11
repos:
2-
- repo: local
2+
- repo: https://github.com/pre-commit/mirrors-mypy
3+
rev: v1.14.1
34
hooks:
45
- id: mypy
5-
name: mypy
6-
always_run: true
7-
require_serial: true
86
pass_filenames: false
9-
10-
language: python
11-
entry: bash -c ". ${PRE_COMMIT_MYPY_VENV:-/dev/null}/bin/activate 2>/dev/null; mypy $0 $@"
127
additional_dependencies:
13-
- mypy >= 1.9.0
148
- genno
159
- GitPython
1610
- nbclient
1711
- pandas-stubs
1812
- pytest
19-
- sphinx
13+
- Sphinx
2014
- werkzeug
2115
- xarray
22-
args: ["."]
2316
- repo: https://github.com/astral-sh/ruff-pre-commit
24-
rev: v0.3.4
17+
rev: v0.9.1
2518
hooks:
2619
- id: ruff
2720
- id: ruff-format

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ See [`doc/README.rst`](doc/README.rst) for further details.
4545

4646
## License
4747

48-
Copyright © 2017–2024 IIASA Energy, Climate, and Environment (ECE) program
48+
Copyright © 2017–2025 IIASA Energy, Climate, and Environment (ECE) program
4949

5050
`ixmp` is licensed under the Apache License, Version 2.0 (the "License"); you
5151
may not use the files in this repository except in compliance with the License.

doc/conf.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# -- Project information ---------------------------------------------------------------
1111

1212
project = "ixmp"
13-
copyright = "2017–2024, IIASA Energy, Climate, and Environment (ECE) program"
13+
copyright = "2017–%Y, IIASA Energy, Climate, and Environment (ECE) program"
1414
author = "ixmp Developers"
1515

1616

ixmp/backend/jdbc.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ def _domain_enum(domain):
170170
return domain_enum.valueOf(domain.upper())
171171
except java.IllegalArgumentException:
172172
domains = ", ".join([d.name().lower() for d in domain_enum.values()])
173-
raise ValueError(f"No such domain: {domain}, " f"existing domains: {domains}")
173+
raise ValueError(f"No such domain: {domain}, existing domains: {domains}")
174174

175175

176176
def _unwrap(v):
@@ -886,8 +886,7 @@ def clone(
886886
# Raise exceptions for limitations of JDBCBackend
887887
if not isinstance(platform_dest._backend, self.__class__):
888888
raise NotImplementedError( # pragma: no cover
889-
f"Clone between {self.__class__} and"
890-
f"{platform_dest._backend.__class__}"
889+
f"Clone between {self.__class__} and{platform_dest._backend.__class__}"
891890
)
892891
elif platform_dest._backend is not self:
893892
package = s.__class__.__module__.split(".")[0]

ixmp/cli.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def report(context, config, key):
9191

9292
if not context:
9393
raise click.UsageError(
94-
"give either --url, --platform or --dbprops " "before command report"
94+
"give either --url, --platform or --dbprops before command report"
9595
)
9696

9797
# Instantiate the Reporter with the Scenario loaded by main()
@@ -227,8 +227,7 @@ def import_group(context):
227227
"""
228228
if not context or "scen" not in context:
229229
raise click.UsageError(
230-
"give --url, or --platform, --model, and "
231-
"--scenario, before command import"
230+
"give --url, or --platform, --model, and --scenario, before command import"
232231
)
233232

234233

@@ -407,7 +406,7 @@ def list_scenarios(context, **kwargs):
407406

408407
if not context:
409408
raise click.UsageError(
410-
"give either --url, --platform or --dbprops " "before command list"
409+
"give either --url, --platform or --dbprops before command list"
411410
)
412411

413412
print(

ixmp/tests/backend/test_jdbc.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,7 @@ def test_verbose_exception(test_mp, exception_verbose_true):
373373

374374
exc_msg = exc_info.value.args[0]
375375
assert (
376-
"There exists no Scenario 'foo|bar' "
377-
"(version: -1) in the database!" in exc_msg
376+
"There exists no Scenario 'foo|bar' (version: -1) in the database!" in exc_msg
378377
)
379378
assert "at.ac.iiasa.ixmp.database.DbDAO.getRunId" in exc_msg
380379
assert "at.ac.iiasa.ixmp.Platform.getScenario" in exc_msg
@@ -595,7 +594,7 @@ def test_reload_cycle(
595594
mp = ixmp.Platform(**platform_args)
596595

597596
# Load existing Scenario
598-
s0 = ixmp.Scenario(mp, model="foo", scenario=f"bar {i-1}", version=1)
597+
s0 = ixmp.Scenario(mp, model="foo", scenario=f"bar {i - 1}", version=1)
599598

600599
memory_usage(f"pass {i} -- platform instantiated")
601600

ixmp/tests/core/test_scenario.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def test_init_set(self, scen):
137137
# Add set on a locked scenario
138138
with pytest.raises(
139139
RuntimeError,
140-
match="This Scenario cannot be edited" ", do a checkout first!",
140+
match="This Scenario cannot be edited, do a checkout first!",
141141
):
142142
scen.init_set("foo")
143143

@@ -399,7 +399,7 @@ def test_excel_io(self, scen, scen_empty, tmp_path, caplog):
399399

400400
# With init_items=False, can't be read into an empty Scenario.
401401
# Exception raised is the first index set, alphabetically
402-
with pytest.raises(ValueError, match="no set 'i'; " "try init_items=True"):
402+
with pytest.raises(ValueError, match="no set 'i'; try init_items=True"):
403403
scen_empty.read_excel(tmp_path)
404404

405405
# File can be read with init_items=True
@@ -441,7 +441,7 @@ def test_excel_io(self, scen, scen_empty, tmp_path, caplog):
441441

442442
# Fails with add_units=False
443443
with pytest.raises(
444-
ValueError, match="The unit 'pounds' does not exist" " in the database!"
444+
ValueError, match="The unit 'pounds' does not exist in the database!"
445445
):
446446
s.read_excel(tmp_path, init_items=True)
447447

@@ -623,7 +623,7 @@ def test_set(scen_empty) -> None:
623623
scen.add_set("i", ["i9", "extra"], ["i9 comment"])
624624
# Missing element in the index set
625625
with pytest.raises(
626-
ValueError, match="The index set 'i' does not have an " "element 'bar'!"
626+
ValueError, match="The index set 'i' does not have an element 'bar'!"
627627
):
628628
scen.add_set("foo", "bar")
629629

pyproject.toml

+3-2
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,9 @@ exclude_also = [
8282
omit = ["ixmp/util/sphinx_linkcode_github.py"]
8383

8484
[tool.mypy]
85-
exclude = [
86-
"build/",
85+
files = [
86+
"doc",
87+
"ixmp",
8788
]
8889

8990
[[tool.mypy.overrides]]

0 commit comments

Comments
 (0)