From e5d8d48fca8a6e32510feeaf9b574ec601206b6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Forr=C3=B3?= Date: Wed, 4 Sep 2024 19:36:57 +0200 Subject: [PATCH] Explicitly invalidate the global parse hash in `SpecParser` constructor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Python can reuse id of objects after they are garbage-collected, however the global parse hash contains an id of `SpecParser` instance in order to force parsing on context switches, and when a different instance has the same id, it has no way to detect that. Explicitly invalidate the global parse hash when a `SpecParser` instance is created to prevent this issue. Signed-off-by: Nikola Forró --- specfile/spec_parser.py | 11 +++++++++-- tests/integration/test_specfile.py | 10 ++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/specfile/spec_parser.py b/specfile/spec_parser.py index d169194..0dfb2fc 100644 --- a/specfile/spec_parser.py +++ b/specfile/spec_parser.py @@ -57,6 +57,10 @@ def __init__( self.force_parse = force_parse self.spec = None self.tainted = False + # explicitly invalidate the global parse hash, this `SpecParser` instance could have + # been assigned the same id as a previously deleted one and parsing could be + # improperly skipped + SpecParser._last_parse_hash = None def __eq__(self, other: object) -> bool: if not isinstance(other, SpecParser): @@ -71,9 +75,12 @@ def __eq__(self, other: object) -> bool: def __repr__(self) -> str: return f"SpecParser({self.sourcedir!r}, {self.macros!r}, {self.force_parse!r})" + def id(self) -> int: + return id(self) + def __deepcopy__(self, memo: Dict[int, Any]) -> "SpecParser": result = self.__class__.__new__(self.__class__) - memo[id(self)] = result + memo[self.id()] = result for k, v in self.__dict__.items(): if k in ["spec", "tainted"]: continue @@ -359,7 +366,7 @@ def parse( """ # calculate hash of all input parameters payload = ( - id(self), + self.id(), self.sourcedir, self.macros, self.force_parse, diff --git a/tests/integration/test_specfile.py b/tests/integration/test_specfile.py index 10afd79..a5c4c37 100644 --- a/tests/integration/test_specfile.py +++ b/tests/integration/test_specfile.py @@ -580,6 +580,16 @@ def test_parse_if_necessary(spec_macros): flexmock(SpecParser).should_call("_do_parse").once() assert spec1.expanded_name == "test" assert spec1.expanded_version == "28.1.2~rc2" + flexmock(SpecParser).should_receive("id").and_return(12345) + flexmock(SpecParser).should_call("_do_parse").once() + spec = Specfile(spec_macros) + flexmock(SpecParser).should_call("_do_parse").never() + assert spec.expanded_name == "test" + spec = None + flexmock(SpecParser).should_call("_do_parse").once() + spec = Specfile(spec_macros) + flexmock(SpecParser).should_call("_do_parse").never() + assert spec.expanded_name == "test" @pytest.mark.skipif(