Skip to content

Commit af1314e

Browse files
Bump supported Python interval to [3.10, 3.14] (#680)
Bumps Python versions to those currently supported and in production. Updates from dropping 3.9 support: * Using `|` for type hints instead of union/optional * Use `strict=` flag for `zip()`. Closes #635 --------- Co-authored-by: Theodore Turocy <ted.turocy@gmail.com>
1 parent 2aedf8d commit af1314e

25 files changed

+246
-227
lines changed

.devcontainer/devcontainer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"features": {
44
"ghcr.io/devcontainers/features/python:1": {
55
"installTools": true,
6-
"version": "3.11"
6+
"version": "3.14"
77
},
88
"ghcr.io/devcontainers-contrib/features/gdbgui:2": {
99
"version": "latest"

.github/workflows/lint.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
- uses: actions/checkout@v5
4848
- uses: actions/setup-python@v6
4949
with:
50-
python-version: "3.12"
50+
python-version: "3.14"
5151
- uses: py-actions/flake8@v2
5252

5353
cython-lint:
@@ -58,7 +58,7 @@ jobs:
5858
- name: Setup Python
5959
uses: actions/setup-python@v6
6060
with:
61-
python-version: "3.12"
61+
python-version: "3.14"
6262
- name: Install Python packages
6363
run: python -m pip install cython-lint
6464
- name: cython-lint

.github/workflows/python.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
1111
strategy:
1212
matrix:
13-
python-version: ['3.9', '3.13']
13+
python-version: ['3.10', '3.14']
1414

1515
steps:
1616
- uses: actions/checkout@v5
@@ -39,7 +39,7 @@ jobs:
3939
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
4040
strategy:
4141
matrix:
42-
python-version: ['3.13']
42+
python-version: ['3.14']
4343

4444
steps:
4545
- uses: actions/checkout@v4
@@ -63,7 +63,7 @@ jobs:
6363
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
6464
strategy:
6565
matrix:
66-
python-version: ['3.13']
66+
python-version: ['3.14']
6767

6868
steps:
6969
- uses: actions/checkout@v4
@@ -88,7 +88,7 @@ jobs:
8888
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
8989
strategy:
9090
matrix:
91-
python-version: ['3.13']
91+
python-version: ['3.14']
9292

9393
steps:
9494
- uses: actions/checkout@v5

.readthedocs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ formats: all
88
build:
99
os: ubuntu-22.04
1010
tools:
11-
python: "3.13"
11+
python: "3.14"
1212
apt_packages:
1313
- libgmp-dev
1414
- pandoc

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ name = "pygambit"
77
version = "16.4.0"
88
description = "The package for computation in game theory"
99
readme = "src/README.rst"
10-
requires-python = ">=3.9"
10+
requires-python = ">=3.10"
1111
license = "GPL-2.0-or-later"
1212
authors = [
1313
{name = "Theodore Turocy", email = "ted.turocy@gmail.com"},
@@ -17,11 +17,11 @@ keywords = ["game theory", "Nash equilibrium"]
1717
classifiers=[
1818
"Development Status :: 5 - Production/Stable",
1919
"Intended Audience :: Science/Research",
20-
"Programming Language :: Python :: 3.9",
2120
"Programming Language :: Python :: 3.10",
2221
"Programming Language :: Python :: 3.11",
2322
"Programming Language :: Python :: 3.12",
2423
"Programming Language :: Python :: 3.13",
24+
"Programming Language :: Python :: 3.14",
2525
"Programming Language :: Python :: Implementation :: CPython",
2626
"Topic :: Scientific/Engineering :: Mathematics"
2727
]
@@ -41,7 +41,7 @@ Changelog = "https://github.com/gambitproject/gambit/blob/master/ChangeLog"
4141
[tool.ruff]
4242
line-length = 99
4343
indent-width = 4
44-
target-version = "py39"
44+
target-version = "py310"
4545
include = ["setup.py", "src/pygambit/**/*.py", "tests/**/*.py, doc/tutorials/*.ipynb"]
4646

4747
[tool.ruff.lint]

src/pygambit/action.pxi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class Action:
8585
return Infoset.wrap(self.action.deref().GetInfoset())
8686

8787
@property
88-
def prob(self) -> typing.Union[decimal.Decimal, Rational]:
88+
def prob(self) -> decimal.Decimal | Rational:
8989
"""
9090
Get the probability a chance action is played.
9191

@@ -108,7 +108,7 @@ class Action:
108108
return Rational(py_string.decode("ascii"))
109109

110110
@property
111-
def plays(self) -> typing.List[Node]:
111+
def plays(self) -> list[Node]:
112112
"""Returns a list of all terminal `Node` objects consistent with it.
113113
"""
114114
return [

src/pygambit/behavmixed.pxi

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class MixedAction:
8181
def __len__(self) -> len:
8282
return len(self.infoset.actions)
8383

84-
def __iter__(self) -> typing.Iterator[typing.Tuple[Action, ProfileDType], None, None]:
84+
def __iter__(self) -> typing.Iterator[tuple[Action, ProfileDType], None, None]:
8585
"""Iterate over the probabilities assigned to actions by the mixed action.
8686

8787
.. versionadded:: 16.2.0
@@ -225,7 +225,7 @@ class MixedBehavior:
225225
def __len__(self) -> int:
226226
return len(self.player.actions)
227227

228-
def mixed_actions(self) -> typing.Iterator[typing.Tuple[Infoset, MixedAction], None, None]:
228+
def mixed_actions(self) -> typing.Iterator[tuple[Infoset, MixedAction], None, None]:
229229
"""Iterate over the mixed actions specified by the mixed behavior.
230230

231231
.. versionadded:: 16.2.0
@@ -240,7 +240,7 @@ class MixedBehavior:
240240
for infoset in self.player.infosets:
241241
yield infoset, self[infoset]
242242

243-
def __iter__(self) -> typing.Iterator[typing.Tuple[Action, ProfileDType], None, None]:
243+
def __iter__(self) -> typing.Iterator[tuple[Action, ProfileDType], None, None]:
244244
"""Iterate over the probabilities assigned to actions by the mixed behavior.
245245

246246
.. versionadded:: 16.2.0
@@ -257,8 +257,8 @@ class MixedBehavior:
257257

258258
def __getitem__(
259259
self,
260-
index: typing.Union[InfosetReference, ActionReference]
261-
) -> typing.Union[MixedAction, ProfileDType]:
260+
index: InfosetReference | ActionReference
261+
) -> MixedAction | ProfileDType:
262262
"""Access a component of the mixed behavior specified by `index`.
263263

264264
Parameters
@@ -299,7 +299,7 @@ class MixedBehavior:
299299
)
300300

301301
def __setitem__(self,
302-
index: typing.Union[InfosetReference, ActionReference],
302+
index: InfosetReference | ActionReference,
303303
value: typing.Any) -> None:
304304
"""Sets a component of the mixed behavior to `value`.
305305

@@ -387,7 +387,7 @@ class MixedBehaviorProfile:
387387
"""The game on which this mixed behavior profile is defined."""
388388
return self._game
389389

390-
def mixed_behaviors(self) -> typing.Iterator[typing.Tuple[Player, MixedBehavior], None, None]:
390+
def mixed_behaviors(self) -> typing.Iterator[tuple[Player, MixedBehavior], None, None]:
391391
"""Iterate over the mixed behaviors in the profile.
392392

393393
.. versionadded:: 16.2.0
@@ -402,7 +402,7 @@ class MixedBehaviorProfile:
402402
for player in self.game.players:
403403
yield player, self[player]
404404

405-
def mixed_actions(self) -> typing.Iterator[typing.Tuple[Infoset, MixedAction], None, None]:
405+
def mixed_actions(self) -> typing.Iterator[tuple[Infoset, MixedAction], None, None]:
406406
"""Iterate over the mixed actions specified by the profile.
407407

408408
.. versionadded:: 16.2.0
@@ -417,7 +417,7 @@ class MixedBehaviorProfile:
417417
for infoset in self.game.infosets:
418418
yield infoset, self[infoset]
419419

420-
def __iter__(self) -> typing.Iterator[typing.Tuple[Action, ProfileDType], None, None]:
420+
def __iter__(self) -> typing.Iterator[tuple[Action, ProfileDType], None, None]:
421421
"""Iterate over the probabilities assigned to actions by the profile.
422422

423423
.. versionadded:: 16.2.0
@@ -434,8 +434,8 @@ class MixedBehaviorProfile:
434434

435435
def __getitem__(
436436
self,
437-
index: typing.Union[PlayerReference, InfosetReference, ActionReference]
438-
) -> typing.Union[MixedBehavior, MixedAction, ProfileDType]:
437+
index: PlayerReference | InfosetReference | ActionReference
438+
) -> MixedBehavior | MixedAction | ProfileDType:
439439
"""Access a component of the mixed behavior specified by `index`.
440440

441441
Parameters
@@ -506,7 +506,7 @@ class MixedBehaviorProfile:
506506

507507
def __setitem__(
508508
self,
509-
index: typing.Union[PlayerReference, InfosetReference, ActionReference],
509+
index: PlayerReference | InfosetReference | ActionReference,
510510
value: typing.Any
511511
) -> None:
512512
"""Sets a probability, mixed agent strategy, or mixed behavior strategy to `value`.

src/pygambit/gambit.pyx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,14 @@ def _to_number(value: typing.Any) -> c_Number:
7474
return c_Number(value.encode("ascii"))
7575

7676

77-
PlayerReference = typing.Union[Player, str]
78-
StrategyReference = typing.Union[Strategy, str]
79-
InfosetReference = typing.Union[Infoset, str]
80-
ActionReference = typing.Union[Action, str]
81-
NodeReference = typing.Union[Node, str]
77+
PlayerReference = Player | str
78+
StrategyReference = Strategy | str
79+
InfosetReference = Infoset | str
80+
ActionReference = Action | str
81+
NodeReference = Node | str
8282
NodeReferenceSet = typing.Iterable[NodeReference]
8383

84-
ProfileDType = typing.Union[float, Rational]
84+
ProfileDType = float | Rational
8585

8686

8787
######################

0 commit comments

Comments
 (0)