Ticket #12357: trac12357_internal_strong_groupoid_cache.patch

File trac12357_internal_strong_groupoid_cache.patch, 4.3 KB (added by SimonKing, 8 years ago)

Make groupoids garbage collectable, but still use a cache by strong references. Relative #11943

  • module_list.py

    # HG changeset patch
    # User Simon King <simon.king@uni-jena.de>
    # Date 1327494591 -3600
    # Node ID 847528f625b0fa8e48e31649c798d760db873a68
    # Parent  bb939e8739f4ec074118fb866becf0f5674f052e
    #12357: Make groupoids garbage collectable
    
    diff --git a/module_list.py b/module_list.py
    a b  
    154154    Extension('sage.categories.functor',
    155155              sources = ['sage/categories/functor.pyx']),
    156156             
     157    Extension('sage.categories.groupoid',
     158              sources = ['sage/categories/groupoid.pyx']),
     159             
    157160    Extension('sage.categories.map',
    158161              sources = ['sage/categories/map.pyx']),
    159162             
  • (a) a/sage/categories/groupoid.py vs. (b) b/sage/categories/groupoid.pyx

    diff --git a/sage/categories/groupoid.py b/sage/categories/groupoid.pyx
    rename from sage/categories/groupoid.py
    rename to sage/categories/groupoid.pyx
    a b  
    1313from sage.categories.category import Category
    1414from sage.misc.cachefunc import cached_method
    1515from sets_cat import Sets
     16from sage.structure.unique_representation import UniqueRepresentation
     17from sage.structure.parent cimport Parent
    1618
    1719class Groupoid(Category):
    1820    """
     
    3032        Groupoid with underlying set Dihedral group of order 6 as a permutation group
    3133
    3234    """
     35    @staticmethod
     36    def __classcall__(cls, S):
     37        """
     38        In contrast to usual instances of
     39        :class:`~sage.structure.unique_representation.UniqueRepresentation`,
     40        the cache is implemented in the given argument.
     41       
     42        In that way, no "external" strong references to the groupoid are created
     43        by caching -- there only is a reference from ``Groupoid(G)`` to ``G``
     44        and from ``G`` to ``Groupoid(G)``, but Python's garbage collection can
     45        easily deal with those circular references.
     46
     47        EXAMPLES::
     48
     49            sage: P = GF(151)['x','y']
     50            sage: n = id(Groupoid(P))
     51            sage: import gc
     52            sage: _ = gc.collect()
     53            sage: n in map(id, gc.get_objects())   # indirect doctest
     54            True
     55
     56        Thus, the groupoid is cached. But when deleting ``P``, its
     57        groupoid can be garbage collected as well. This fixes a
     58        memory leak, by trac ticket #12357::
     59
     60            sage: del P
     61            sage: _ = gc.collect()
     62            sage: n in map(id, gc.get_objects())
     63            False
     64
     65        TESTS:
     66
     67        We test against some corner cases::
     68
     69            sage: Groupoid(None)
     70            Traceback (most recent call last):
     71            ...
     72            TypeError: Groupoid of None is not defined
     73            sage: Groupoid(1)
     74            Traceback (most recent call last):
     75            ...
     76            TypeError: 1 must either allow attribute assignment or be instances of <type 'sage.structure.parent.Parent'>
     77            sage: class A: pass
     78            sage: a = A()
     79            sage: Groupoid(a)
     80            Groupoid with underlying set <__main__.A instance at ...>
     81            sage: Groupoid(a) is Groupoid(a)
     82            True
     83
     84        """
     85        try:
     86            return getattr(S, '_cached_'+cls.__name__)
     87        except AttributeError:
     88            pass
     89        cdef Parent G
     90        cdef dict D
     91        if S is None:
     92            raise TypeError, "Groupoid of None is not defined"
     93
     94        instance = type.__call__(cls, S)
     95        assert(isinstance(instance,cls))
     96        if instance.__class__.__reduce__ == UniqueRepresentation.__reduce__:
     97            instance._reduction = (cls, (S,), {})
     98        try:
     99            setattr(S, '_cached_'+cls.__name__, instance)
     100            return instance
     101        except AttributeError:
     102            pass
     103        if S is not None:
     104            try:
     105                # Parents have a cpdef dict __cached_methods attribute, by #11115
     106                G = S
     107                if G is None:
     108                    raise ValueError
     109                D = G.__cached_methods
     110                if D is None:
     111                    D = G.__cached_methods = {}
     112                D['_cached_'+cls.__name__] = instance
     113                return instance
     114            except TypeError:
     115                pass
     116        raise TypeError, "%s must either allow attribute assignment or be instances of %s"%(S, Parent)
    33117
    34118    def __init__(self, G = None):
    35119        """