Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/sage/combinat/species/species.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def __ne__(self, other):
"""
return not (self == other)

def __getstate__(self):
def _getstate_(self):
Copy link
Member

@vincentmacri vincentmacri Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change?

r"""
This is used during the pickling process and returns a dictionary
of the data needed to create this object during the unpickling
Expand All @@ -194,7 +194,7 @@ def __getstate__(self):
EXAMPLES::

sage: C = species.CharacteristicSpecies(5)
sage: args, kwds = C.__getstate__()
sage: args, kwds = C._getstate_()
sage: args
{0: 5}
sage: sorted(kwds.items())
Expand All @@ -206,18 +206,18 @@ def __getstate__(self):
except AttributeError:
return ({}, kwds)

def __setstate__(self, state):
def _setstate_(self, state):
"""
This is used during unpickling to recreate this object from the
data provided by the ``__getstate__`` method.
data provided by the ``_getstate_`` method.

TESTS::

sage: C2 = species.CharacteristicSpecies(2)
sage: C4 = species.CharacteristicSpecies(4)
sage: C2
Characteristic species of order 2
sage: C2.__setstate__(C4.__getstate__()); C2
sage: C2._setstate_(C4._getstate_()); C2
Characteristic species of order 4
"""
args_dict, kwds = state
Expand All @@ -235,7 +235,7 @@ def weighted(self, weight):
sage: C.weighted(t)
Cyclic permutation species with weight=t
"""
args_dict, kwds = self.__getstate__()
args_dict, kwds = self._getstate_()
kwds.update({'weight': weight})
return self.__class__(*[args_dict[i] for i in range(len(args_dict))], **kwds)

Expand Down
46 changes: 33 additions & 13 deletions src/sage/misc/cachefunc.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,6 @@
r"""
Cached Functions and Methods

AUTHORS:

- William Stein: initial version, (inspired by conversation with Justin Walker)
- Mike Hansen: added doctests and made it work with class methods.
- Willem Jan Palenstijn: add CachedMethodCaller for binding cached methods to
instances.
- Tom Boothby: added DiskCachedFunction.
- Simon King: improved performance, more doctests, cython version,
CachedMethodCallerNoArgs, weak cached function, cached special methods.
- Julian Rueth (2014-03-19, 2014-05-09, 2014-05-12): added ``key`` parameter, allow caching
for unhashable elements, added ``do_pickle`` parameter

EXAMPLES:

By :issue:`11115`, cached functions and methods are now also
Expand Down Expand Up @@ -430,8 +418,19 @@ Note that shallow copy of mutable objects may behave unexpectedly::
2
sage: b.f is a.f
False
"""

AUTHORS:

- William Stein: initial version, (inspired by conversation with Justin Walker)
- Mike Hansen: added doctests and made it work with class methods.
- Willem Jan Palenstijn: add CachedMethodCaller for binding cached methods to
instances.
- Tom Boothby: added DiskCachedFunction.
- Simon King: improved performance, more doctests, cython version,
CachedMethodCallerNoArgs, weak cached function, cached special methods.
- Julian Rueth (2014-03-19, 2014-05-09, 2014-05-12): added ``key`` parameter, allow caching
for unhashable elements, added ``do_pickle`` parameter
"""
# ****************************************************************************
# Copyright (C) 2008 William Stein <[email protected]>
# Mike Hansen <[email protected]>
Expand Down Expand Up @@ -864,6 +863,27 @@ cdef class CachedFunction():
"""
return _cached_function_unpickle, (self.__cached_module__, self.__name__, self.cache)

def is_pickled_with_cache(self):
"""
Return ``True`` if this cached function is pickled with the cache.

EXAMPLES::

sage: @cached_function
....: def f(x):
....: return x
....:
sage: f.is_pickled_with_cache()
False
sage: @cached_function(do_pickle=True)
....: def f(x):
....: return x
....:
sage: f.is_pickled_with_cache()
True
"""
return self.do_pickle

#########
# Introspection
#
Expand Down
27 changes: 14 additions & 13 deletions src/sage/structure/factory.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
r"""
Factory for cached representations

.. SEEALSO::

:mod:`sage.structure.unique_representation`

Using a :class:`UniqueFactory` is one way of implementing a *cached
representation behaviour*. In spite of its name, using a
:class:`UniqueFactory` is not enough to ensure the *unique representation
Expand Down Expand Up @@ -36,13 +32,16 @@ sophisticated, then generally
:class:`~sage.structure.unique_representation.CachedRepresentation` is much
easier to use than a factory.

.. SEEALSO::

:mod:`sage.structure.unique_representation`

AUTHORS:

- Robert Bradshaw (2008): initial version.
- Simon King (2013): extended documentation.
- Robert Bradshaw (2008): initial version
- Simon King (2013): extended documentation
- Julian Rueth (2014-05-09): use ``_cache_key`` if parameters are unhashable
"""

#*****************************************************************************
# Copyright (C) 2008 Robert Bradshaw <[email protected]>
# 2014 Julian Rueth <[email protected]>
Expand Down Expand Up @@ -423,13 +422,11 @@ cdef class UniqueFactory(SageObject):
self._cache[version, _cache_key(key)] = obj
obj._factory_data = self, version, key, extra_args

# Install a custom __reduce__ method on the instance "obj"
# that we just created. We only do this if the class of
# "obj" has a generic __reduce__ method, which is either
# object.__reduce__ or __reduce_cython__, the
# auto-generated pickling function for Cython.
# Install a custom __reduce_ex__ method (that overrides __reduce__
# method) on the instance obj that we just created. We only do this
# if the class of obj has a generic object.__reduce__ method.
f = obj.__class__.__reduce__
if f.__objclass__ is object or f.__name__ == "__reduce_cython__":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change?

if f.__objclass__ is object:
obj.__reduce_ex__ = types.MethodType(generic_factory_reduce, obj)
except AttributeError:
pass
Expand Down Expand Up @@ -734,6 +731,10 @@ def generic_factory_reduce(self, proto):
"""
Used to provide a ``__reduce__`` method if one does not already exist.

INPUT:

- ``proto`` -- positive integer; protocol number

EXAMPLES::

sage: V = QQ^6 # needs sage.modules
Expand Down
2 changes: 1 addition & 1 deletion src/sage/structure/parent_gens.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ cdef class ParentWithGens(ParentWithBase):
return parent.Parent.__getstate__(self)
d = []
try:
d = list(self.__dict__.copy().iteritems()) # so we can add elements
d = self.__dict__.copy().items() # so we can add elements
except AttributeError:
pass
d = dict(d)
Expand Down
103 changes: 88 additions & 15 deletions src/sage/structure/unique_representation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,7 @@
r"""
Unique Representation

Abstract classes for cached and unique representation behavior.

.. SEEALSO::

:class:`sage.structure.factory.UniqueFactory`

AUTHORS:

- Nicolas M. Thiery (2008): Original version.
- Simon A. King (2013-02): Separate cached and unique representation.
- Simon A. King (2013-08): Extended documentation.

This modules defines abstract classes for cached and unique representation behavior.

What is a cached representation?
================================
Expand Down Expand Up @@ -537,6 +526,16 @@ class that inherits from :class:`UniqueRepresentation`: By adding
unique representation behaviour, one has to implement hash and equality test
accordingly, for example by inheriting from
:class:`~sage.misc.fast_methods.WithEqualityById`.

.. SEEALSO::

:class:`sage.structure.factory.UniqueFactory`

AUTHORS:

- Nicolas M. Thiery (2008): initial version
- Simon A. King (2013-02): separated cached and unique representation
- Simon A. King (2013-08): extended documentation
"""
# ****************************************************************************
# Copyright (C) 2008 Nicolas M. Thiery <nthiery at users.sf.net>
Expand Down Expand Up @@ -598,6 +597,8 @@ def __classcall__(cls, *args, **options):
assert isinstance(instance, cls)
if instance.__class__.__reduce__ == WithPicklingByInitArgs.__reduce__:
instance._reduction = (cls, args, options)
instance.__class__.__getstate__ = WithPicklingByInitArgs.__getstate__
instance.__class__.__setstate__ = WithPicklingByInitArgs.__setstate__
return instance

def __reduce__(self):
Expand All @@ -613,9 +614,9 @@ def __reduce__(self):

sage: x = UniqueRepresentation()
sage: x.__reduce__() # indirect doctest
(<function unreduce at ...>, (<class 'sage.structure.unique_representation.UniqueRepresentation'>, (), {}))
(<function unreduce at ...>, (<class 'sage.structure.unique_representation.UniqueRepresentation'>, (), {}), {})
"""
return (unreduce, self._reduction)
return (unreduce, self._reduction, self.__getstate__())

def __copy__(self):
"""
Expand Down Expand Up @@ -646,6 +647,78 @@ def __deepcopy__(self, memo):
"""
return self

def __getstate__(self):
"""
Used for pickling.

An object of :class:`WithPicklingByInitArgs` does not keep its dictionary
when pickled, because the object is constructed anew upon unpickling.

Here we make some exceptions so that cached functions with
``do_pickle=True`` attached to an object are kept in the pickle.

EXAMPLES::

sage: from sage.structure.unique_representation import WithPicklingByInitArgs
sage: class X(WithPicklingByInitArgs):
....: def __init__(self, x):
....: self._x = x
....: @cached_method(do_pickle=True)
....: def genus(self):
....: return len(self._x)
....:
sage: import __main__; __main__.X = X # not needed in an interactive session
sage: a = X((1,2,3))
sage: a.genus()
3
sage: a.genus.cache
3
sage: s = dumps(a)
sage: a.genus.clear_cache()
sage: a.genus.cache
sage: b = loads(s)
sage: b.genus.cache
3
"""
from sage.misc.cachefunc import CachedFunction
d = {}
try:
for key, value in self.__dict__.items():
if isinstance(value, CachedFunction) and value.is_pickled_with_cache():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have to use CachedFunction here? I would have thought it would be CachedMethod, but I tested and cached methods are instances of CachedFunction and not CachedMethod.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up question: What happens with static methods? Are they pickled? I don't think they should be but we should check if they are.

d[key] = value
except AttributeError:
pass

return d

def __setstate__(self, d):
"""
Used for unpickling.

EXAMPLES::

sage: from sage.structure.unique_representation import WithPicklingByInitArgs
sage: class X(WithPicklingByInitArgs):
....: def __init__(self, x):
....: self._x = x
....: @cached_method(do_pickle=False)
....: def genus(self):
....: return len(self._x)
....:
sage: import __main__; __main__.X = X # not needed in an interactive session
sage: a = X((1,2,3))
sage: a.genus()
3
sage: a.genus.cache
3
sage: s = dumps(a)
sage: a.genus.clear_cache()
sage: a.genus.cache
sage: b = loads(s)
sage: b.genus.cache
"""
self.__dict__.update(d)


def unreduce(cls, args, keywords):
"""
Expand Down Expand Up @@ -952,7 +1025,7 @@ class CachedRepresentation(WithPicklingByInitArgs):

sage: x = MyClass(value = 1)
sage: x.__reduce__()
(<function unreduce at ...>, (<class '__main__.MyClass'>, (), {'value': 1}))
(<function unreduce at ...>, (<class '__main__.MyClass'>, (), {'value': 1}), {})
sage: x is loads(dumps(x))
True

Expand Down
Loading