Ticket #11342: trac11342-AttributeErrorMessage.patch

File trac11342-AttributeErrorMessage.patch, 7.1 KB (added by SimonKing, 9 years ago)

Make attribute access faster on elements and parents: Create the error message only when needed

  • sage/structure/element.pyx

    # HG changeset patch
    # User Simon King <simon.king@uni-jena.de>
    # Date 1306046266 -7200
    # Node ID 86f38a1af1e6f603f1517a7de50333aa46ddce7c
    # Parent  f6e15ae7becab871c7feadd75ec585bed13c7048
    #11342: Make getattr faster on elements and parents
    
    diff --git a/sage/structure/element.pyx b/sage/structure/element.pyx
    a b  
    187187from types import MethodType
    188188
    189189from sage.categories.category   import Category
    190 from sage.structure.parent      cimport Parent, raise_attribute_error
     190from sage.structure.parent      cimport Parent, AttributeErrorMessage
    191191from sage.structure.parent      import is_extension_type, getattr_from_other_class
    192192from sage.misc.lazy_format      import LazyFormat
    193193
     
    266266        return self
    267267
    268268
    269     def __getattr__(self, name):
     269    def __getattr__(self, str name):
    270270        """
    271271        Let cat be the category of the parent of ``self``.  This
    272272        method emulates ``self`` being an instance of both ``Element``
     
    323323            AttributeError: 'sage.rings.polynomial.polynomial_rational_flint.Polynomial_rational_flint' object has no attribute '__foo'
    324324
    325325        """
    326         if name.startswith('__') and not name.endswith('_'):
    327             raise_attribute_error(self, name)
    328         return getattr_from_other_class(self, self.parent().category().element_class, name)
     326        if (name.startswith('__') and not name.endswith('_')) or self._parent._category is None:
     327            raise AttributeError, AttributeErrorMessage(self, name)
     328        return getattr_from_other_class(self, self._parent._category.element_class, name)
    329329
    330330    def __dir__(self):
    331331        """
  • sage/structure/parent.pxd

    diff --git a/sage/structure/parent.pxd b/sage/structure/parent.pxd
    a b  
    66#                  http://www.gnu.org/licenses/
    77###############################################################################
    88
    9 #cimport sage.categories.object
    109cimport sage.structure.category_object
    1110
    12 cdef inline raise_attribute_error(self, name)
     11cdef class AttributeErrorMessage:
     12    cdef type cls
     13    cdef str name
    1314
    1415cdef class Parent(category_object.CategoryObject):
    1516
  • sage/structure/parent.pyx

    diff --git a/sage/structure/parent.pyx b/sage/structure/parent.pyx
    a b  
    149149        False
    150150    """
    151151    # Robert B claims that this should be robust
    152     return hasattr(cls, "__dictoffset__") and cls.__dictoffset__ == 0
     152    try:
     153        return cls.__dictoffset__ == 0
     154    except AttributeError:
     155        pass
     156    return False
    153157
    154158class A(object):
    155159    pass
    156160methodwrapper = type(A.__call__)
    157161
    158 cdef inline raise_attribute_error(self, name):
     162cdef class AttributeErrorMessage:
    159163    """
    160     Tries to emulate the standard Python AttributeError exception
     164    Tries to emulate the standard Python ``AttributeError`` message.
     165
     166    NOTE:
     167
     168    The typical fate of an attribute error is being caught. Hence,
     169    under normal circumstances, nobody will ever see the error
     170    message. The idea for this class is to provide an object that
     171    is fast to create and whose string representation is an attribute
     172    error's message. That string representation is only created if
     173    someone wants to see it.
    161174
    162175    EXAMPLES::
    163176
     
    169182        Traceback (most recent call last):
    170183        ...
    171184        AttributeError: 'sage.rings.polynomial.polynomial_rational_flint.Polynomial_rational_flint' object has no attribute 'bla'
     185        sage: from sage.structure.parent import AttributeErrorMessage
     186        sage: AttributeErrorMessage(int(1),'bla')
     187        'int' object has no attribute 'bla'
     188
     189    AUTHOR:
     190
     191    - Simon King (2011-05-21)
    172192    """
    173     cls = type(self)
    174     if is_extension_type(cls):
    175         raise AttributeError, "'%s.%s' object has no attribute '%s'"%(cls.__module__, cls.__name__, name)
    176     else:
    177         raise AttributeError, "'%s' object has no attribute '%s'"%(cls.__name__, name)
     193#    raise AttributeError, AttributeErrorMessage(self,name)
     194    def __init__(self, P,str name):
     195        """
     196        INPUT:
    178197
    179 def getattr_from_other_class(self, cls, name):
     198        - ``P``, any object
     199        - ``name``, a string
     200
     201        TEST::
     202
     203            sage: from sage.structure.parent import AttributeErrorMessage
     204            sage: AttributeErrorMessage(int(1),'bla')
     205            'int' object has no attribute 'bla'
     206
     207        """
     208        self.cls = type(P)
     209        self.name = name
     210    def __repr__(self):
     211        """
     212        TEST::
     213
     214            sage: from sage.structure.parent import AttributeErrorMessage
     215            sage: AttributeErrorMessage(int(1),'bla')
     216            'int' object has no attribute 'bla'
     217
     218        """
     219        cdef int dictoff
     220        try:
     221            dictoff = self.cls.__dictoffset__
     222        except AttributeError:
     223            return "'"+self.cls.__name__+"' object has no attribute '"+self.name+"'"
     224        if dictoff:
     225            return "'"+self.cls.__name__+"' object has no attribute '"+self.name+"'"
     226        return repr(self.cls)[6:-1] + " object has no attribute '"+self.name+"'"
     227
     228
     229def getattr_from_other_class(self, cls, str name):
    180230    """
    181231    INPUT::
    182232
     
    269319        ...
    270320        AttributeError: 'sage.rings.integer.Integer' object has no attribute '__call__'
    271321    """
    272     if isinstance(self, cls):
    273         raise_attribute_error(self, name)
     322    if PY_TYPE_CHECK(self, cls):
     323        raise AttributeError, AttributeErrorMessage(self, name)
    274324    try:
    275325        attribute = getattr(cls, name)
    276326    except AttributeError:
    277         raise_attribute_error(self, name)
    278     if isinstance(attribute, methodwrapper):
    279         raise_attribute_error(self, name)
     327        raise AttributeError, AttributeErrorMessage(self, name)
     328    if PY_TYPE_CHECK(attribute, methodwrapper):
     329        raise AttributeError, AttributeErrorMessage(self, name)
    280330    try:
    281331        getter = attribute.__get__
    282332    except AttributeError:
     
    289339        return getter(self, cls)
    290340    except TypeError:
    291341        pass
    292     raise_attribute_error(self, name)
     342    raise AttributeError, AttributeErrorMessage(self, name)
    293343
    294344def dir_with_other_class(self, cls):
    295345    """
     
    670720            self._convert_from_hash = {}
    671721            self._embedding = None
    672722
    673     def __getattr__(self, name):
     723    def __getattr__(self, str name):
    674724        """
    675725        Let cat be the category of ``self``. This method emulates
    676726        ``self`` being an instance of both ``Parent`` and
     
    730780            AttributeError: 'sage.structure.parent.Parent' object has no attribute '__foo'
    731781
    732782        """
    733         if name.startswith('__') and not name.endswith('_'):
    734             raise_attribute_error(self, name)
    735         if self._is_category_initialized():
    736             return getattr_from_other_class(self, self.category().parent_class, name)
    737         else:
    738             raise_attribute_error(self, name)
     783        if (name.startswith('__') and not name.endswith('_')) or self._category is None:
     784            raise AttributeError, AttributeErrorMessage(self, name)
     785        return getattr_from_other_class(self, self._category.parent_class, name)
    739786
    740787    def __dir__(self):
    741788        """