From 6856e81570b2b6add4cbbf8b6ddad36e8e1370b1 Mon Sep 17 00:00:00 2001 From: Sajal Kumar Jana Date: Tue, 29 Sep 2026 06:04:10 +0000 Subject: [PATCH] Fix missing closing parenthesis in repr() of an untitled Game. `Game.__repr__` falls back to an identity-based form when the game has an empty title, but the f-string lacked its closing parenthesis, producing `Game(id=...` instead of `Game(id=...)`. Add regression tests for the untitled (strategic and extensive) and titled forms, and a ChangeLog entry. --- ChangeLog | 4 ++++ src/pygambit/game.pxi | 2 +- tests/test_game.py | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 61b96d1dc3..00bca61ae8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,6 +22,10 @@ and of payoffs, beliefs and regrets of mixed behavior profiles on large game trees. Path following (logit and hp) also spends about half as long factorizing Jacobians. (#1162) +### Fixed +- `repr()` of a `Game` with an empty title was missing its closing parenthesis + (`Game(id=...` instead of `Game(id=...)`). + ## [17.0.0-beta.1] - 2026-09-08 ### Added diff --git a/src/pygambit/game.pxi b/src/pygambit/game.pxi index 925256eae5..4133a7048d 100644 --- a/src/pygambit/game.pxi +++ b/src/pygambit/game.pxi @@ -102,7 +102,7 @@ class Game: if self.title: return f"Game(title='{self.title}')" else: - return f"Game(id={hash(self)}" + return f"Game(id={hash(self)})" def _repr_html_(self): if isinstance(self, ExtensiveGame): diff --git a/tests/test_game.py b/tests/test_game.py index 8fc25669ce..2cfc168c23 100644 --- a/tests/test_game.py +++ b/tests/test_game.py @@ -107,3 +107,21 @@ def test_collection_rejects_integer_indexing(getter): collection = getter(games.create_stripped_down_poker_efg()) with pytest.raises(TypeError): _ = collection[0] + + +@pytest.mark.parametrize( + "game", + [ + pytest.param(gbt.StrategicGame([2, 2], title=""), id="strategic"), + pytest.param(gbt.ExtensiveGame(["1", "2"], title=""), id="extensive"), + ], +) +def test_repr_untitled_game(game): + # A game without a title is represented by its identity; the representation + # must be well-formed (closing parenthesis included), just as for titled games. + assert repr(game) == f"Game(id={hash(game)})" + + +def test_repr_titled_game(): + game = gbt.StrategicGame([2, 2], title="Coordination") + assert repr(game) == "Game(title='Coordination')"