Ticket #14471: trac_14471_dynamic_class_hash.patch

File trac_14471_dynamic_class_hash.patch, 12.4 KB (added by vbraun, 8 years ago)

Updated patch

  • module_list.py

    # HG changeset patch
    # User Volker Braun <vbraun.name@gmail.com>
    # Date 1369863075 -3600
    #      Wed May 29 22:31:15 2013 +0100
    # Node ID 5ca91ac0788a61428369626882b3b787f05d1ea2
    # Parent  a28a0d3881bb31b71fc6f1fc7eb1af7a2a574681
    Fix invalid hash functions of a few parents. Implement a framework to
    test that category refinement does not break hash. Collect debug flags
    in sage.structure in a separate module.
    
    diff --git a/module_list.py b/module_list.py
    a b  
    18701870    Extension('sage.structure.coerce_maps',
    18711871              sources = ['sage/structure/coerce_maps.pyx']),
    18721872
     1873    Extension('sage.structure.debug_options',
     1874              sources=['sage/structure/debug_options.pyx']),
     1875
    18731876    # Compile this with -Os because it works around a bug with
    18741877    # GCC-4.7.3 + Cython 0.19 on Itanium, see Trac #14452. Moreover, it
    18751878    # actually results in faster code than -O3.
  • sage/combinat/species/recursive_species.py

    diff --git a/sage/combinat/species/recursive_species.py b/sage/combinat/species/recursive_species.py
    a b  
    7070            -826511807095108317
    7171        """
    7272        try:
    73             return hash((self.__class__, id(self._reference)))
     73            return hash(('CombinatorialSpecies', id(self._reference)))
    7474        except AttributeError:
    75             return hash(self.__class__)
     75            return hash('CombinatorialSpecies')
    7676
    7777    def __eq__(self, other):
    7878        """
  • sage/doctest/forker.py

    diff --git a/sage/doctest/forker.py b/sage/doctest/forker.py
    a b  
    9696    import sage.misc.displayhook
    9797    sys.displayhook = sage.misc.displayhook.DisplayHook(sys.displayhook)
    9898
     99    # Switch on extra debugging
     100    from sage.structure.debug_options import debug
     101    debug.refine_category_hash_check = True
     102
    99103    # Disable IPython colors during doctests
    100104    from sage.misc.interpreter import DEFAULT_SAGE_CONFIG
    101105    DEFAULT_SAGE_CONFIG['TerminalInteractiveShell']['colors'] = 'NoColor'
    102106
    103107    # We import readline before forking, otherwise Pdb doesn't work
    104     # os OS X: http://trac.sagemath.org/sage_trac/ticket/14289
     108    # os OS X: http://trac.sagemath.org/14289
    105109    import readline
    106110
    107111    # Workarounds for https://github.com/sagemath/sagenb/pull/84
  • sage/libs/coxeter3/coxeter.pyx

    diff --git a/sage/libs/coxeter3/coxeter.pyx b/sage/libs/coxeter3/coxeter.pyx
    a b  
    195195            False
    196196            sage: d = {a: 1, b: 2}                                     # optional - coxeter3
    197197        """
    198         return hash((self.__class__.__name__, self.name()))
     198        return hash(('Type', self.name()))
    199199
    200200    def __richcmp__(Type self, other, int op):
    201201        """
  • sage/structure/category_object.pyx

    diff --git a/sage/structure/category_object.pyx b/sage/structure/category_object.pyx
    a b  
    4646    ((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1))
    4747"""
    4848
    49 cdef int bad_parent_warnings = 0
    50 
    5149import generators
    5250import sage_object
    5351from sage.categories.category import Category, JoinCategory
     52from sage.structure.debug_options import debug
    5453
    5554def guess_category(obj):
    5655    # this should be obsolete if things declare their categories
     
    153152            Join of Category of semigroups and Category of commutative additive semigroups
    154153        """
    155154        if category is None:
    156             if bad_parent_warnings:
     155            if debug.bad_parent_warnings:
    157156                print "No category for %s" % type(self)
    158157            category = guess_category(self) # so generators don't crash
    159158        elif (type(category) == tuple or type(category) == list):
  • new file sage/structure/debug_options.pxd

    diff --git a/sage/structure/debug_options.pxd b/sage/structure/debug_options.pxd
    new file mode 100644
    - +  
     1
     2cdef class DebugOptions_class:
     3    cdef public bint bad_parent_warnings
     4    cdef public bint unique_parent_warnings
     5    cdef public bint refine_category_hash_check
  • new file sage/structure/debug_options.pyx

    diff --git a/sage/structure/debug_options.pyx b/sage/structure/debug_options.pyx
    new file mode 100644
    - +  
     1"""
     2Debug options for the `sage.structure` modules
     3
     4EXAMPLES::
     5
     6    sage: from sage.structure.debug_options import debug
     7    sage: debug.bad_parent_warnings
     8    False
     9    sage: debug.unique_parent_warnings
     10    False
     11    sage: debug.refine_category_hash_check
     12    True
     13"""
     14
     15#*****************************************************************************
     16#       Copyright (C) 2013 Robert Bradshaw <robertwb@gmail.com>
     17#                          William Stein <wstein@gmail.com>
     18#                          Volker Braun <vbraun.name@gmail.com>
     19#
     20#  Distributed under the terms of the GNU General Public License (GPL)
     21#  as published by the Free Software Foundation; either version 2 of
     22#  the License, or (at your option) any later version.
     23#                  http://www.gnu.org/licenses/
     24#*****************************************************************************
     25
     26
     27cdef class DebugOptions_class:
     28    def __cinit__(self):
     29        """
     30        Initializer for the debug options
     31
     32        TESTS::
     33       
     34            sage: from sage.structure.debug_options import debug
     35            sage: type(debug)
     36            <type 'sage.structure.debug_options.DebugOptions_class'>
     37        """
     38        self.bad_parent_warnings = False
     39        self.unique_parent_warnings = False
     40        # This one will be enabled during doctests
     41        self.refine_category_hash_check = False
     42
     43
     44debug = DebugOptions_class()
  • sage/structure/dynamic_class.py

    diff --git a/sage/structure/dynamic_class.py b/sage/structure/dynamic_class.py
    a b  
    119119from sage.misc.cachefunc import weak_cached_function
    120120from sage.structure.unique_representation import ClasscallMetaclass
    121121
    122 def dynamic_class(name, bases, cls = None, reduction = None, doccls = None,
    123                   prepend_cls_bases = True, cache=True):
     122def dynamic_class(name, bases, cls=None, reduction=None, doccls=None,
     123                  prepend_cls_bases=True, cache=True):
    124124    r"""
    125125    INPUT:
    126126
     
    322322
    323323
    324324@weak_cached_function
    325 def dynamic_class_internal(name, bases, cls = None, reduction = None, doccls = None, prepend_cls_bases=True):
     325def dynamic_class_internal(name, bases, cls=None, reduction=None, doccls=None, prepend_cls_bases=True):
    326326    r"""
    327327    See sage.structure.dynamic_class.dynamic_class? for indirect doctests.
    328328
  • sage/structure/parent.pyx

    diff --git a/sage/structure/parent.pyx b/sage/structure/parent.pyx
    a b  
    9898cimport element
    9999cimport sage.categories.morphism as morphism
    100100cimport sage.categories.map as map
     101from sage.structure.debug_options import debug
    101102from sage.structure.sage_object import SageObject
    102103from sage.structure.misc import (dir_with_other_class, getattr_from_other_class,
    103104                                 is_extension_type)
     
    107108from sage.misc.sage_itertools import unique_merge
    108109from sage.misc.lazy_format import LazyFormat
    109110
    110 cdef int bad_parent_warnings = 0
    111 cdef int unique_parent_warnings = 0
    112 
    113111# Create a dummy attribute error, using some kind of lazy error message,
    114112# so that neither the error itself not the message need to be created
    115113# repeatedly, which would cost time.
     
    323321        .. automethod:: _get_action_
    324322        .. automethod:: _an_element_
    325323        .. automethod:: _repr_option
     324        .. automethod:: _init_category_
    326325        """
    327326        # TODO: in the long run, we want to get rid of the element_constructor = argument
    328327        # (element_constructor would always be set by inheritance)
     
    347346        self._init_category_(category)
    348347
    349348        if len(kwds) > 0:
    350             if bad_parent_warnings:
     349            if debug.bad_parent_warnings:
    351350                print "Illegal keywords for %s: %s" % (type(self), kwds)
    352351        # TODO: many classes don't call this at all, but __new__ crashes Sage
    353         if bad_parent_warnings:
     352        if debug.bad_parent_warnings:
    354353            if element_constructor is not None and not callable(element_constructor):
    355354                print "coerce BUG: Bad element_constructor provided", type(self), type(element_constructor), element_constructor
    356355        if gens is not None:
     
    371370
    372371    def _init_category_(self, category):
    373372        """
     373        Initialize the category framework
     374
     375        Most parents initialize their category upon construction, and
     376        this is the recommended behavior. For example, this happens
     377        when the constructor calls `Parent.__init__()` directly or
     378        indirectly. However, some parents defer this for performance
     379        reasons. For example,
     380        :mod:`sage.matrix.matrix_space.MatrixSpace` does not.
     381       
    374382        EXAMPLES::
    375383
    376384            sage: P = Parent()
     
    393401            # TODO: assert that the category is consistent
    394402            if not issubclass(self.__class__, Sets_parent_class) and not is_extension_type(self.__class__):
    395403                #documentation transfer is handled by dynamic_class
    396                 self.__class__     = dynamic_class("%s_with_category"%self.__class__.__name__, (self.__class__, category.parent_class, ), doccls=self.__class__)
     404                self.__class__ = dynamic_class(
     405                    '{0}_with_category'.format(self.__class__.__name__),
     406                    (self.__class__, category.parent_class, ),
     407                    doccls=self.__class__)
    397408
    398409    def _refine_category_(self, category):
    399410        """
     
    428439            False
    429440            sage: TestSuite(Q).run()
    430441
     442
     443        TESTS:
     444
     445        Here is a test against :trac:`14471`. Refining the category will issue
     446        a warning, if this change affects the hash value (note that this will
     447        only be seen in doctest mode)::
     448
     449            sage: class MyParent(Parent):
     450            ....:     def __hash__(self):
     451            ....:         return hash(type(self))   # subtle mistake
     452            sage: a = MyParent()
     453            sage: h = hash(a)
     454            sage: a._refine_category_(Algebras(QQ))
     455            hash of <class '__main__.MyParent_with_category'> changed in
     456            Parent._refine_category_ during initialisation
     457
     458            sage: b = MyParent(category=Rings())
     459            sage: h = hash(b)
     460            sage: b._refine_category_(Algebras(QQ))
     461            hash of <class '__main__.MyParent_with_category'> changed in
     462            Parent._refine_category_ during refinement
    431463        """
     464        if debug.refine_category_hash_check:
     465            # check that the hash stays the same after refinement
     466            hash_old = hash(self)
    432467        if self._category is None:
    433468            self._init_category_(category)
     469            if debug.refine_category_hash_check and hash_old != hash(self):
     470                print 'hash of {0} changed in Parent._refine_category_ during initialisation' \
     471                    .format(str(self.__class__))
    434472            return
    435473        if category is self._category:
    436474            return
     
    455493            self.__dict__.__delitem__('element_class')
    456494        except (AttributeError, KeyError):
    457495            pass
     496        if debug.refine_category_hash_check and hash_old != hash(self):
     497            print 'hash of {0} changed in Parent._refine_category_ during refinement' \
     498                .format(str(self.__class__))
     499
    458500
    459501    # This probably should go into Sets().Parent
    460502    @lazy_attribute
     
    20522094        if S is self:
    20532095            return True
    20542096        elif S == self:
    2055             if unique_parent_warnings:
     2097            if debug.unique_parent_warnings:
    20562098                print "Warning: non-unique parents %s"%(type(S))
    20572099            return True
    20582100        return self.coerce_map_from(S) is not None
     
    21522194
    21532195        if S == self:
    21542196            # non-unique parents
    2155             if unique_parent_warnings:
     2197            if debug.unique_parent_warnings:
    21562198                print "Warning: non-unique parents %s"%(type(S))
    21572199            return self._generic_convert_map(S)
    21582200
  • sage/symbolic/callable.py

    diff --git a/sage/symbolic/callable.py b/sage/symbolic/callable.py
    a b  
    295295            sage: hash(f.parent()) #random
    296296            -8878119762643067638
    297297        """
    298         return hash((self.__class__, self._arguments))
     298        return hash(('CallableSymbolicExpressionRing', self._arguments))
    299299
    300300    def __cmp__(self, other):
    301301        """