From b7e2daf2ffa555fa0a017be3c23a5abd538a8764 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Fri, 3 Jul 2026 17:46:36 -0400 Subject: [PATCH 1/3] atom: don't clobber portage's cp slot on init New portage's Atom class stores _cp in __slots__ as the backing store for its own read-only cp/category properties. Atom.__init__ was resetting self._cp to None right after portage.dep.Atom.__init__ set it, since gentoolkit previously needed to reinitialize CPV's private attrs of the same name. With the new portage that reset corrupts the shared slot instead, breaking cp/category (and anything built on them, like intersects() and comparisons). Old (pre-__slots__) portage never touches self._cp at all, so it's still unset at this point and CPV.cp still needs it seeded to None to enable its own lazy computation. Only skip the reset when portage already populated it. Bug: https://bugs.gentoo.org/978428 Signed-off-by: Matt Turner --- pym/gentoolkit/atom.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pym/gentoolkit/atom.py b/pym/gentoolkit/atom.py index b529c2f5..35f70f77 100644 --- a/pym/gentoolkit/atom.py +++ b/pym/gentoolkit/atom.py @@ -64,15 +64,14 @@ def __init__(self, atom): except portage.exception.InvalidAtom: raise errors.GentoolkitInvalidAtom(atom) - # cpv is already managed by portage.dep.Atom; initialize CPV's - # private attrs directly to avoid writing to portage's read-only - # cpv property (new portage) or redundantly overwriting __dict__ - # (old portage). + # Seed _cp only if portage didn't already set it (old portage never + # sets it; new portage's __slots__ value must not be clobbered). + if not hasattr(self, "_cp"): + self._cp = None self._category = None self._name = None self._version = None self._revision = None - self._cp = None self._fullversion = None self.validate = False From d940d27d305ac8e38867b62514439675557d7a37 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Fri, 3 Jul 2026 17:47:06 -0400 Subject: [PATCH 2/3] cpv: rename private version cache to avoid slot collision with Atom CPV._version and portage.dep.Atom's __slots__ entry _version share the same name. Since gentoolkit's Atom subclasses both, writing to self._version from CPV's lazy version-splitting logic overwrote portage's real slot value instead of a separate attribute, corrupting the raw version string portage's own properties depend on (e.g. for wildcard atom matching in intersects()). Rename to _bare_version, a name that can't collide with any portage Atom slot. Bug: https://bugs.gentoo.org/978428 Signed-off-by: Matt Turner --- pym/gentoolkit/atom.py | 2 +- pym/gentoolkit/cpv.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pym/gentoolkit/atom.py b/pym/gentoolkit/atom.py index 35f70f77..05184864 100644 --- a/pym/gentoolkit/atom.py +++ b/pym/gentoolkit/atom.py @@ -70,7 +70,7 @@ def __init__(self, atom): self._cp = None self._category = None self._name = None - self._version = None + self._bare_version = None self._revision = None self._fullversion = None self.validate = False diff --git a/pym/gentoolkit/cpv.py b/pym/gentoolkit/cpv.py index c58243e3..882cddb1 100644 --- a/pym/gentoolkit/cpv.py +++ b/pym/gentoolkit/cpv.py @@ -56,7 +56,7 @@ def __init__(self, cpv, validate=False): self.cpv = cpv self._category = None self._name = None - self._version = None + self._bare_version = None self._revision = None self._cp = None self._fullversion = None @@ -79,9 +79,9 @@ def name(self): @property def version(self): - if self._version is None: + if self._bare_version is None: self._set_cpv_chunks() - return self._version + return self._bare_version @property def revision(self): @@ -107,7 +107,7 @@ def _set_cpv_chunks(self): chunks = split_cpv(self.cpv, validate=self.validate) self._category = chunks[0] self._name = chunks[1] - self._version = chunks[2] + self._bare_version = chunks[2] self._revision = chunks[3] def __eq__(self, other): From 5a239d8fb391a7253cf5143cb901cee5dbb543ca Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Fri, 3 Jul 2026 17:47:30 -0400 Subject: [PATCH 3/3] atom: override version to strip revision like CPV intends New portage's Atom.version property returns the raw parsed version string with any revision still embedded (e.g. "2-r1" for "cat/pkg-2-r1"), unlike gentoolkit's CPV.version which is meant to exclude the revision (kept separately in .revision). Since portage.dep.Atom is earlier in Atom's MRO, its version property was shadowing CPV's, breaking anything relying on the split (e.g. fullversion, and ~ / range comparisons in intersects()). Add an explicit override so Atom.version keeps CPV's semantics regardless of what portage does internally. Bug: https://bugs.gentoo.org/978428 Signed-off-by: Matt Turner --- pym/gentoolkit/atom.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pym/gentoolkit/atom.py b/pym/gentoolkit/atom.py index 05184864..166859ae 100644 --- a/pym/gentoolkit/atom.py +++ b/pym/gentoolkit/atom.py @@ -56,6 +56,13 @@ def operator(self): op = super().operator return "" if op is None else op + @property + def version(self): + # New portage's version property includes the revision; ours doesn't. + if self._bare_version is None: + self._set_cpv_chunks() + return self._bare_version + def __init__(self, atom): self.atom = atom