Ticket #9556: trac-9556_dynamic_attributes_symbolics.patch

File trac-9556_dynamic_attributes_symbolics.patch, 10.3 KB (added by SimonKing, 9 years ago)

The patch implements dynamic attributes, but some doctests segfault

  • sage/symbolic/callable.py

    # HG changeset patch
    # User Simon King <simon.king@nuigalway.ie>
    # Date 1279628515 -3600
    # Node ID da40a0941e6a84ea792f7c1eca211fabbb72e895
    # Parent  5e9277b8e2ec15c4daae0d1e00c48276dab75540
    #9556: Implement dynamic attributes to Symbolic Expressions
    
    diff -r 5e9277b8e2ec -r da40a0941e6a sage/symbolic/callable.py
    a b  
    3737from sage.categories.pushout import ConstructionFunctor
    3838import weakref
    3939
     40#######################################################################
     41# An auxiliary lambda function that returns a lambda function,
     42# for use in expressions.pyx
     43#######################################################################
     44
     45_eval_f_at_x = lambda f,x: lambda : f(*x)
     46
    4047#########################################################################################
    4148#  Callable functions
    4249#########################################################################################
  • sage/symbolic/expression.pxd

    diff -r 5e9277b8e2ec -r da40a0941e6a sage/symbolic/expression.pxd
    a b  
    66cdef class Expression(CommutativeRingElement):
    77    cdef GEx _gobj
    88    cdef Expression coerce_in(self, z)
     9    cdef list _attrName_cache
     10    cdef object _dict_
    911    cpdef object _eval_self(self, R)
    1012    cpdef object _convert(self, R)
    1113    cpdef bint is_polynomial(self, var)
  • sage/symbolic/expression.pyx

    diff -r 5e9277b8e2ec -r da40a0941e6a sage/symbolic/expression.pyx
    a b  
    135135from sage.misc.derivative import multi_derivative
    136136from sage.rings.infinity import AnInfinity
    137137
     138#cdef set
     139_Function_dir = None
     140
     141
    138142def is_Expression(x):
    139143    """
    140144    Returns True if *x* is a symbolic Expression.
     
    313317        # extract the expression from the archive
    314318        GEx_construct_ex(&self._gobj, ar.unarchive_ex(sym_lst, <unsigned>0))
    315319
     320    def _fix_additional_attributes(self):
     321        if self._dict_ is not None:
     322            return
     323        Dic = {}
     324        from callable import _eval_f_at_x
     325        f = self.operator()
     326        D = self._getAttributeNames()
     327#        if self._dict_ is None:
     328#            self._dict_ = {}
     329        import inspect
     330        for k in D:
     331            if not Dic.has_key(k):
     332                fk = getattr(f,k)
     333                if callable(fk):
     334                    if inspect.ismethod(fk):
     335                        Dic[k] = _eval_f_at_x(fk,self.operands())
     336                    else:
     337                        Dic[k] = _eval_f_at_x(fk,[f]+self.operands())
     338        # include all attributes that are stored in the default __dict__
     339        from sage.structure.parent import getattr_from_other_class
     340        try:
     341            # get the __dict__ dictproxy
     342            D = getattr_from_other_class(self, self.parent().category().element_class, '__dict__')
     343        except AttributeError:
     344            D = {}
     345        Dic.update(D)
     346        # now, create a dictproxy, as __dict__ usually is.
     347        from ctypes import pythonapi, py_object
     348        from _ctypes import PyObj_FromPtr
     349        PyDictProxy_New = pythonapi.PyDictProxy_New
     350        PyDictProxy_New.argtypes = (py_object,)
     351        PyDictProxy_New.rettype = py_object
     352        self._dict_ = PyObj_FromPtr(PyDictProxy_New(Dic))
     353
     354    def _getAttributeNames(self):
     355        """
     356        Implement tab completion and introspection.
     357
     358        See :meth:`trait_names` for further documentation.
     359
     360        TESTS::
     361
     362            sage: def some_function(slf, *L): print slf,'called with',L
     363            sage: from sage.symbolic.function import BuiltinFunction
     364            sage: class ExampleBuiltin(BuiltinFunction): pass
     365            sage: ex_func = ExampleBuiltin('example',nargs=2)
     366            sage: ex_func
     367            example
     368            sage: e = ex_func(x+1, x+2)
     369            sage: ex_func.foo_bar = some_function
     370            sage: e.foo_bar()
     371            example called with (x + 1, x + 2)
     372            sage: 'foo_bar' in dir(e)    # indirect doctest
     373            True
     374            sage: import sagenb.misc.support as s
     375            sage: s.completions('e.foo', globals(), system='python') # indirect doctest
     376            ['e.foo_bar']
     377
     378        AUTHORS:
     379
     380        - Simon King (2010-07)
     381
     382        """
     383        if self._attrName_cache is not None:
     384            return self._attrName_cache
     385        # self accepts also attributes that stem from
     386        # *custom* attributes of its operator. So, we
     387        # return all names that come from the operator,
     388        # but not from the *class* of its operator.
     389        f = self.operator()
     390        D = dir(f)
     391        D = [n for n in D if callable(getattr(f,n))]
     392        if _Function_dir is None:
     393            from sage.symbolic.function import Function
     394            _Function_dir = set(dir(Function))
     395        self._attrName_cache = [n for n in D if n not in _Function_dir]
     396        return self._attrName_cache
     397
     398    def trait_names(self):
     399        """
     400        Implement tab completion and introspection.
     401
     402        Any callable attribute ``foo`` of ``self.operator()``
     403        is available as an attribute of ``self``, so that
     404        ``self.foo()`` yields ``self.operator().foo(*self.operand())``.
     405
     406        EXAMPLES:
     407
     408        We subclass Function, adding one method::
     409
     410            sage: from sage.symbolic.function import BuiltinFunction
     411            sage: class ExampleBuiltin(BuiltinFunction):
     412            ...     def __init__(self):
     413            ...         BuiltinFunction.__init__(self, 'ex_func', nargs=0) #arbitrary no of args
     414            ...     def some_function_name(self, *args):
     415            ...         return len(args)
     416            ...
     417            sage: ex_func = ExampleBuiltin()
     418            sage: ex_func
     419            ex_func
     420
     421        We obtain a symbolic expression by calling {{{ex_func}}}::
     422
     423            sage: e = ex_func(x,x+1, x+2)
     424            sage: type(e)
     425            <type 'sage.symbolic.expression.Expression'>
     426
     427        We add a callable and a non-callable attribute to {{{ex_func}}}::
     428
     429            sage: def some_function(slf, *L): print slf,'called with',L
     430            ...
     431            sage: ex_func.foo_bar = some_function
     432            sage: ex_func.bar_foo = 4
     433
     434        Now, both the new method and the callable attribute {{{foo_bar}}} of
     435        {{{ex_func}}} are available from {{{e}}}, but not the non-callable::
     436
     437            sage: e.some_function_name()
     438            3
     439            sage: e.foo_bar()
     440            ex_func called with (x, x + 1, x + 2)
     441            sage: e.bar_foo
     442            Traceback (most recent call last):
     443            ...
     444            AttributeError: <type 'sage.symbolic.expression.Expression'> has no attribute 'bar_foo'
     445
     446        Tab completion  and introspection work::
     447
     448            sage: 'foo_bar' in dir(e)     # indirect doctest
     449            True
     450            sage: 'some_function_name' in dir(e)
     451            True
     452            sage: 'bar_foo' in dir(e)
     453            False
     454            sage: import sagenb.misc.support as s
     455            sage: s.completions('e.some', globals(), system='python')
     456            ['e.some_function_name']
     457            sage: s.completions('e.foo', globals(), system='python') # indirect doctest
     458            ['e.foo_bar']
     459            sage: s.completions('e.bar', globals(), system='python') # indirect doctest
     460            []
     461
     462        Note that the methods of {{{ex_func}}} are available for {{{e}}} even
     463        though they were created *after* creating {{{e}}}.  However, adding
     464        further attributes at running time is not the intended use. Thus when
     465        accessing an additional attribute, using tab completion or trying to
     466        access a non-existing attribute, the list of additional attributes of
     467        {{{e}}} is fixed once and for all. So, further callable attributes of
     468        {{{ex_func}}} are not available for {{{e}}}::
     469
     470            sage: ex_func.blabla = some_function
     471            sage: e.blabla()
     472            Traceback (most recent call last):
     473            ...
     474            AttributeError: <type 'sage.symbolic.expression.Expression'> has no attribute 'blabla'
     475            sage: 'blabla' in dir(e)
     476            False
     477
     478        AUTHORS:
     479
     480        - Simon King (2010-07)
     481
     482        """
     483        return self._getAttributeNames()
     484
     485    def __getattr__(self, key):
     486        """
     487        Access to dynamic attributes.
     488
     489        See :meth:`trait_names` for further documentation.
     490
     491        TESTS::
     492
     493            sage: def some_function(slf, *L): print slf,'called with',L
     494            ...
     495            sage: from sage.symbolic.function import BuiltinFunction
     496            sage: class ExampleBuiltin(BuiltinFunction): pass
     497            ...
     498            sage: ex_func = ExampleBuiltin('example',nargs=2)
     499            sage: ex_func
     500            example
     501            sage: e = ex_func(x+1, x+2)
     502            sage: ex_func.foo_bar = some_function
     503            sage: e.foo_bar()      # indirect doctest
     504            example called with (x + 1, x + 2)
     505            sage: 'foo_bar' in dir(e)
     506            True
     507            sage: import sagenb.misc.support as s
     508            sage: s.completions('e.foo', globals(), system='python')
     509            ['e.foo_bar']
     510
     511        AUTHORS:
     512
     513        - Simon King (2010-07)
     514
     515        """
     516        from sage.structure.parent import getattr_from_other_class
     517        # something for introspection: As soon as we updated _dict_,
     518        # it takes the role of __dict__
     519        if (self._dict_ is not None) and (key == '__dict__'):
     520            return self._dict_
     521        # first method: the usual way for elements.
     522        try:
     523            A = getattr_from_other_class(self, self.parent().category().element_class, key)
     524        except AttributeError:
     525            pass
     526        # second method: if _dict_ is not None then the "dynamic" attributes
     527        # are initialized. So, if it is not known then key error.
     528        self._fix_additional_attributes()
     529        # now _dict_ is initialised and contains all of __dict__
     530        try:
     531            return self._dict_[key]
     532        except KeyError:
     533            raise AttributeError, "%s has no attribute '%s'"%(type(self),key)
     534
    316535    def _repr_(self):
    317536        """
    318537        Return string representation of this symbolic expression.