diff options
| author | Jason Kirtland <jek@discorporate.us> | 2008-03-06 14:12:22 +0000 |
|---|---|---|
| committer | Jason Kirtland <jek@discorporate.us> | 2008-03-06 14:12:22 +0000 |
| commit | 4f6d0ff71e72fc4f5171a062eda6b577475bb3b1 (patch) | |
| tree | 9503dfd9d214348f2dedc723d2a925732e6e7615 /lib/sqlalchemy | |
| parent | 06d55b8e1daf43ff91b81d23c59ddf11c17ace52 (diff) | |
| download | sqlalchemy-4f6d0ff71e72fc4f5171a062eda6b577475bb3b1.tar.gz | |
- Synonyms riding on top of existing descriptors are now full proxies
to those descriptors.
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/orm/attributes.py | 63 | ||||
| -rw-r--r-- | lib/sqlalchemy/util.py | 29 |
2 files changed, 88 insertions, 4 deletions
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index 44f6a77b4..35936186c 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -4,7 +4,7 @@ # This module is part of SQLAlchemy and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php -import weakref, threading, operator +import weakref, threading, operator, inspect from itertools import chain import UserDict from sqlalchemy import util @@ -67,8 +67,9 @@ class InstrumentedAttribute(interfaces.PropComparator): property = property(_property, doc="the MapperProperty object associated with this attribute") class ProxiedAttribute(InstrumentedAttribute): - """a 'proxy' attribute which adds InstrumentedAttribute - class-level behavior to any user-defined class property. + """Adds InstrumentedAttribute class-level behavior to a regular descriptor. + + Obsoleted by proxied_attribute_factory. """ class ProxyImpl(object): @@ -101,6 +102,59 @@ class ProxiedAttribute(InstrumentedAttribute): def __delete__(self, instance): return self.user_prop.__delete__(instance) +def proxied_attribute_factory(descriptor): + """Create an InstrumentedAttribute / user descriptor hybrid. + + Returns a new InstrumentedAttribute type that delegates + descriptor behavior and getattr() to the given descriptor. + """ + + class ProxyImpl(object): + accepts_scalar_loader = False + def __init__(self, key): + self.key = key + + class Proxy(InstrumentedAttribute): + """A combination of InsturmentedAttribute and a regular descriptor.""" + + def __init__(self, key, descriptor, comparator): + self.key = key + # maintain ProxiedAttribute.user_prop compatability. + self.descriptor = self.user_prop = descriptor + self._comparator = comparator + self.impl = ProxyImpl(key) + + def comparator(self): + if callable(self._comparator): + self._comparator = self._comparator() + return self._comparator + comparator = property(comparator) + + def __get__(self, instance, owner): + """Delegate __get__ to the original descriptor.""" + if instance is None: + descriptor.__get__(instance, owner) + return self + return descriptor.__get__(instance, owner) + + def __set__(self, instance, value): + """Delegate __set__ to the original descriptor.""" + return descriptor.__set__(instance, value) + + def __delete__(self, instance): + """Delegate __delete__ to the original descriptor.""" + return descriptor.__delete__(instance) + + def __getattr__(self, attribute): + """Delegate __getattr__ to the original descriptor.""" + return getattr(descriptor, attribute) + Proxy.__name__ = type(descriptor).__name__ + 'Proxy' + + util.monkeypatch_proxied_specials(Proxy, type(descriptor), + name='descriptor', + from_instance=descriptor) + return Proxy + class AttributeImpl(object): """internal implementation for instrumented attributes.""" @@ -1233,7 +1287,8 @@ def register_attribute(class_, key, uselist, useobject, callable_=None, proxy_pr return if proxy_property: - inst = ProxiedAttribute(key, proxy_property, comparator=comparator) + proxy_type = proxied_attribute_factory(proxy_property) + inst = proxy_type(key, proxy_property, comparator) else: inst = InstrumentedAttribute(_create_prop(class_, key, uselist, callable_, useobject=useobject, typecallable=typecallable, mutable_scalars=mutable_scalars, **kwargs), comparator=comparator) diff --git a/lib/sqlalchemy/util.py b/lib/sqlalchemy/util.py index 4e8a837c9..9adb3983d 100644 --- a/lib/sqlalchemy/util.py +++ b/lib/sqlalchemy/util.py @@ -344,6 +344,35 @@ def warn_exception(func, *args, **kwargs): except: warn("%s('%s') ignored" % sys.exc_info()[0:2]) +def monkeypatch_proxied_specials(into_cls, from_cls, skip=None, only=None, + name='self.proxy', from_instance=None): + """Automates delegation of __specials__ for a proxying type.""" + + if only: + dunders = only + else: + if skip is None: + skip = ('__slots__', '__del__', '__getattribute__', + '__metaclass__', '__getstate__', '__setstate__') + dunders = [m for m in dir(from_cls) + if (m.startswith('__') and m.endswith('__') and + not hasattr(into_cls, m) and m not in skip)] + for method in dunders: + try: + spec = inspect.getargspec(getattr(from_cls, method)) + fn_args = inspect.formatargspec(spec[0]) + d_args = inspect.formatargspec(spec[0][1:]) + except TypeError: + fn_args = '(self, *args, **kw)' + d_args = '(*args, **kw)' + + py = ("def %(method)s%(fn_args)s: " + "return %(name)s.%(method)s%(d_args)s" % locals()) + + env = from_instance is not None and {name: from_instance} or {} + exec py in env + setattr(into_cls, method, env[method]) + class SimpleProperty(object): """A *default* property accessor.""" |
