proj/pkgcore/pkgcore:master commit in: src/pkgcore/restrictions/, tests/restrictions/, src/pkgcore/package/, /

"Arthur Zamarin" <[email protected]>
Newsgroups gmane.linux.gentoo.cvs
Message-ID <1786134074.ccb30228a1b07b158f20ec1d7256a2796dc84b59.arthurzam@gentoo>
commit:     ccb30228a1b07b158f20ec1d7256a2796dc84b59
Author:     Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
AuthorDate: Fri Aug  7 20:21:14 2026 +0000
Commit:     Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
CommitDate: Fri Aug  7 20:21:14 2026 +0000
URL:        https://gitweb.gentoo.org/proj/pkgcore/pkgcore.git/commit/?id=ccb30228

restrictions, package: drop SlotsPicklingMixin

Python pickles slotted classes natively; the mixin only added an uncached
MRO walk and two python frames per serialization. snakeoil deprecated it
and moved the state restoration that Simple and Strict genuinely need
onto those classes, so both base classes just inherit it now.

Pickling the restriction tuples pkgcheck pushes across its work queue is
4.3x faster as a result (19602 items, one per gentoo package: 0.392s ->
0.092s), and state for mangled private slots is no longer silently
dropped.

AlwaysBool's __getstate__/__setstate__ went with it. They existed only to
sidestep the mixin's flat dict, and their two tuple is exactly the shape
snakeoil now reserves for the (__dict__, slots) protocol - a footgun
better removed than documented.

Note this changes the on the wire format: restrictions pickled by older
pkgcore raise TypeError instead of loading. pkgcheck discards and
regenerates a cache on any load failure, so it needs no cache version
bump, but other consumers holding restriction pickles must regenerate
them.

Relates: https://github.com/pkgcore/snakeoil/issues/117
Signed-off-by: Arthur Zamarin <arthurzam <AT> gentoo.org>

 NEWS.rst                                | 10 ++++++++++
 pyproject.toml                          |  4 ++--
 src/pkgcore/package/base.py             |  2 +-
 src/pkgcore/restrictions/restriction.py |  9 +--------
 tests/restrictions/test_restriction.py  | 31 ++++++++++++++++++++++++++++++-
 5 files changed, 44 insertions(+), 12 deletions(-)

diff --git a/NEWS.rst b/NEWS.rst
index 6cdf28118..7c9ad54c8 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -17,6 +17,16 @@ Fixes
   (Arthur Zamarin)
 
 
+Changes
+~~~~~~~
+
+- ``pkgcore.package.base`` and ``pkgcore.restrictions.restriction.base``: drop
+  the deprecated ``snakeoil.klass.SlotsPicklingMixin``.  Python pickles
+  ``__slots__`` natively, making pickling of restrictions ~4x faster.  Note
+  restrictions pickled by older pkgcore no longer load; regenerate any caches
+  holding them (Arthur Zamarin)
+
+
 ----------------------------
 pkgcore 0.12.37 (2026-07-31)
 ----------------------------

diff --git a/pyproject.toml b/pyproject.toml
index 4c8b9a135..de0b6c069 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
 [build-system]
 requires = [
 	"flit_core >=3.8,<4",
-	"snakeoil >= 0.11.2",
+	"snakeoil@git+https://github.com/pkgcore/snakeoil.git#master",
 ]
 build-backend = "py_build"
 backend-path = ["."]
@@ -32,7 +32,7 @@ classifiers = [
 dynamic = ["version"]
 
 dependencies = [
-	"snakeoil >= 0.11.2",
+	"snakeoil@git+https://github.com/pkgcore/snakeoil.git#master",
 	"lxml",
 ]
 

diff --git a/src/pkgcore/package/base.py b/src/pkgcore/package/base.py
index a3fda4592..cdc3bcb98 100644
--- a/src/pkgcore/package/base.py
+++ b/src/pkgcore/package/base.py
@@ -16,7 +16,7 @@ from ..operations import format
 from . import errors
 
 
-class base(klass.SlotsPicklingMixin, immutable.Strict):
+class base(immutable.Strict):
     built = False
     configurable = False
     _operations = format.operations

diff --git a/src/pkgcore/restrictions/restriction.py b/src/pkgcore/restrictions/restriction.py
index 95aeff207..1e4713f4b 100644
--- a/src/pkgcore/restrictions/restriction.py
+++ b/src/pkgcore/restrictions/restriction.py
@@ -6,7 +6,6 @@ import abc
 import functools
 import typing
 
-from snakeoil import klass
 from snakeoil.currying import pretty_docs
 from snakeoil.klass import immutable, memoize
 
@@ -16,7 +15,7 @@ package_type = "package"
 valid_types = (value_type, package_type)
 
 
-class base(klass.SlotsPicklingMixin, immutable.Simple, memoize.WeaklyCachedABC):
+class base(immutable.Simple, memoize.WeaklyCachedABC):
     """base restriction matching object.
 
     all derivatives *should* be __slots__ based (lot of instances may
@@ -80,12 +79,6 @@ class AlwaysBool(base):
     def __repr__(self):
         return f"<{self.__class__.__name__} always {self.negate!r} @{id(self):#8x}>"
 
-    def __getstate__(self):
-        return self.negate, self.type
-
-    def __setstate__(self, state):
-        self.negate, self.type = state
-
 
 # TODO: fix this so it's cachable.  It *is* cachable.
 class Negate(base, caching=False):

diff --git a/tests/restrictions/test_restriction.py b/tests/restrictions/test_restriction.py
index 5576bd6bc..bc93f8161 100644
--- a/tests/restrictions/test_restriction.py
+++ b/tests/restrictions/test_restriction.py
@@ -1,8 +1,10 @@
+import copy
+import pickle
 from functools import partial
 
 import pytest
 
-from pkgcore.restrictions import restriction
+from pkgcore.restrictions import packages, restriction, values
 
 from .utils import TestRestriction
 
@@ -96,3 +98,30 @@ class TestAnyMatch(TestRestriction):
             # just test these do not traceback
             assert repr(inst)
             assert str(inst)
+
+
+class TestPickling:
+    """Restrictions are pushed across the multiprocessing queues, thus must marshal"""
+
+    def test_always_bool(self):
+        for negate in (False, True):
+            obj = restriction.AlwaysBool("package", negate)
+            new = pickle.loads(pickle.dumps(obj))
+            assert (new.negate, new.type) == (negate, "package")
+
+    def test_slotted_restriction(self):
+        obj = SillyBool(negate=True)
+        assert pickle.loads(pickle.dumps(obj)).negate is True
+
+    @pytest.mark.parametrize("protocol", (2, pickle.HIGHEST_PROTOCOL))
+    def test_restriction_tree(self, protocol):
+        obj = packages.OrRestriction(
+            packages.PackageRestriction("category", values.StrExactMatch("dev-util")),
+            packages.PackageRestriction("package", values.StrRegex("diff.*")),
+        )
+        assert pickle.loads(pickle.dumps(obj, protocol)) == obj
+
+    def test_copy(self):
+        obj = packages.PackageRestriction("category", values.StrExactMatch("dev-util"))
+        assert copy.copy(obj) == obj
+        assert copy.deepcopy(obj) == obj
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.