From: Mike Bayer Date: Thu, 28 Aug 2008 18:21:42 +0000 (+0000) Subject: - starargs_as_list was not actually issuing SAPendingDeprecationWarning, fixed X-Git-Tag: rel_0_5rc1~33 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a18035cfb16d8b55e460a9f3752c3b9f697e7729;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - starargs_as_list was not actually issuing SAPendingDeprecationWarning, fixed - implemented code cleanup from [ticket:1152] but not including using the decorators module --- diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index 41e191312e..2c01cdc172 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -1902,18 +1902,12 @@ def connection_memoize(key): connection. The memo will be stored in ``connection.info[key]``. """ - def decorate(fn): - spec = inspect.getargspec(fn) - assert len(spec[0]) == 2 - assert spec[0][1] == 'connection' - assert spec[1:3] == (None, None) - - def decorated(self, connection): - try: - return connection.info[key] - except KeyError: - connection.info[key] = val = fn(self, connection) - return val + @util.decorator + def decorated(fn, self, connection): + try: + return connection.info[key] + except KeyError: + connection.info[key] = val = fn(self, connection) + return val - return util.function_named(decorated, fn.__name__) - return decorate + return decorated diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index 88e124e4b6..0b36d5f0cd 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -197,7 +197,9 @@ def proxied_attribute_factory(descriptor): class AttributeImpl(object): """internal implementation for instrumented attributes.""" - def __init__(self, class_, key, callable_, class_manager, trackparent=False, extension=None, compare_function=None, active_history=False, **kwargs): + def __init__(self, class_, key, + callable_, class_manager, trackparent=False, extension=None, + compare_function=None, active_history=False, **kwargs): """Construct an AttributeImpl. \class_ @@ -426,8 +428,11 @@ class MutableScalarAttributeImpl(ScalarAttributeImpl): uses_objects = False - def __init__(self, class_, key, callable_, class_manager, copy_function=None, compare_function=None, **kwargs): - super(ScalarAttributeImpl, self).__init__(class_, key, callable_, class_manager, compare_function=compare_function, **kwargs) + def __init__(self, class_, key, callable_, + class_manager, copy_function=None, + compare_function=None, **kwargs): + super(ScalarAttributeImpl, self).__init__(class_, key, callable_, + class_manager, compare_function=compare_function, **kwargs) class_manager.mutable_attributes.add(key) if copy_function is None: raise sa_exc.ArgumentError("MutableScalarAttributeImpl requires a copy function") @@ -467,7 +472,9 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl): accepts_scalar_loader = False uses_objects = True - def __init__(self, class_, key, callable_, class_manager, trackparent=False, extension=None, copy_function=None, compare_function=None, **kwargs): + def __init__(self, class_, key, callable_, class_manager, + trackparent=False, extension=None, copy_function=None, + compare_function=None, **kwargs): super(ScalarObjectAttributeImpl, self).__init__(class_, key, callable_, class_manager, trackparent=trackparent, extension=extension, compare_function=compare_function, **kwargs) @@ -542,7 +549,9 @@ class CollectionAttributeImpl(AttributeImpl): accepts_scalar_loader = False uses_objects = True - def __init__(self, class_, key, callable_, class_manager, typecallable=None, trackparent=False, extension=None, copy_function=None, compare_function=None, **kwargs): + def __init__(self, class_, key, callable_, class_manager, + typecallable=None, trackparent=False, extension=None, + copy_function=None, compare_function=None, **kwargs): super(CollectionAttributeImpl, self).__init__(class_, key, callable_, class_manager, trackparent=trackparent, extension=extension, compare_function=compare_function, **kwargs) @@ -1427,7 +1436,9 @@ def unregister_class(class_): manager.instantiable = False manager.unregister() -def register_attribute(class_, key, uselist, useobject, callable_=None, proxy_property=None, mutable_scalars=False, impl_class=None, **kwargs): +def register_attribute(class_, key, uselist, useobject, + callable_=None, proxy_property=None, + mutable_scalars=False, impl_class=None, **kwargs): manager = manager_of_class(class_) if manager.is_instrumented(key): return diff --git a/lib/sqlalchemy/orm/dependency.py b/lib/sqlalchemy/orm/dependency.py index 5fcaa4ab0f..f54c8f85f3 100644 --- a/lib/sqlalchemy/orm/dependency.py +++ b/lib/sqlalchemy/orm/dependency.py @@ -488,14 +488,14 @@ class ManyToManyDP(DependencyProcessor): return sync.source_changes(uowcommit, state, self.parent, self.prop.synchronize_pairs) class MapperStub(object): - """Pose as a Mapper representing the association table in a - many-to-many join, when performing a ``flush()``. + """Represent a many-to-many dependency within a flush + context. + + The UOWTransaction corresponds dependencies to mappers. + MapperStub takes the place of the "association table" + so that a depedendency can be corresponded to it. - The ``Task`` objects in the objectstore module treat it just like - any other ``Mapper``, but in fact it only serves as a dependency - placeholder for the many-to-many update task. """ - __metaclass__ = util.ArgSingleton def __init__(self, parent, mapper, key): diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index ac14b5daf7..99593e8894 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -280,14 +280,14 @@ class Mapper(object): return from_obj @property - @util.cache_decorator + @util.memoize def _with_polymorphic_mappers(self): if not self.with_polymorphic: return [self] return self.__mappers_from_spec(*self.with_polymorphic) @property - @util.cache_decorator + @util.memoize def _with_polymorphic_selectable(self): if not self.with_polymorphic: return self.mapped_table @@ -463,7 +463,7 @@ class Mapper(object): self.version_id_col = self.inherits.version_id_col for mapper in self.iterate_to_root(): - util.reset_cached(mapper, '_equivalent_columns') + util.reset_memoized(mapper, '_equivalent_columns') if self.order_by is False and not self.concrete and self.inherits.order_by is not False: self.order_by = self.inherits.order_by @@ -557,7 +557,7 @@ class Mapper(object): self.__log("Identified primary key columns: " + str(primary_key)) @property - @util.cache_decorator + @util.memoize def _get_clause(self): """create a "get clause" based on the primary key. this is used by query.get() and many-to-one lazyloads to load this item @@ -568,7 +568,7 @@ class Mapper(object): return sql.and_(*[k==v for (k, v) in params]), dict(params) @property - @util.cache_decorator + @util.memoize def _equivalent_columns(self): """Create a map of all *equivalent* columns, based on the determination of column pairs that are equated to diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index 075dde0814..bbec299673 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -82,9 +82,9 @@ class ColumnProperty(StrategizedProperty): return value class ColumnComparator(PropComparator): + @util.memoize def __clause_element__(self): return self.prop.columns[0]._annotate({"parententity": self.mapper}) - __clause_element__ = util.cache_decorator(__clause_element__) def operate(self, op, *other, **kwargs): return op(self.__clause_element__(), *other, **kwargs) diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index c1030aaf74..6c20a1fe3c 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -43,6 +43,8 @@ aliased = AliasedClass def _generative(*assertions): """Mark a method as generative.""" + + @util.decorator def generate(fn, *args, **kw): self = args[0]._clone() fn_name = fn.func_name @@ -50,7 +52,7 @@ def _generative(*assertions): assertion(self, fn_name) fn(self, *args[1:], **kw) return self - return util.decorator(generate) + return generate class Query(object): """Encapsulates the object-fetching operations provided by Mappers.""" diff --git a/lib/sqlalchemy/util.py b/lib/sqlalchemy/util.py index 24ac0a97e3..32d6e3efad 100644 --- a/lib/sqlalchemy/util.py +++ b/lib/sqlalchemy/util.py @@ -94,7 +94,6 @@ except ImportError: return 'defaultdict(%s, %s)' % (self.default_factory, dict.__repr__(self)) - def to_list(x, default=None): if x is None: return default @@ -103,6 +102,15 @@ def to_list(x, default=None): else: return x +def to_set(x): + if x is None: + return set() + if not isinstance(x, set): + return set(to_list(x)) + else: + return x + + try: from functools import update_wrapper except ImportError: @@ -121,44 +129,31 @@ def accepts_a_list_as_starargs(list_deprecation=None): spec = inspect.getargspec(fn) assert spec[1], 'Decorated function does not accept *args' - meta = format_argspec_plus(spec) - meta['name'] = fn.func_name - meta['varg'] = spec[1] - scratch = list(spec) - scratch[1] = '(%s[0])' % scratch[1] - meta['unpacked_pos'] = format_argspec_plus(scratch)['apply_pos'] - def _deprecate(): if list_deprecation: if list_deprecation == 'pending': warning_type = exc.SAPendingDeprecationWarning else: warning_type = exc.SADeprecationWarning - msg = ( - "%s%s now accepts multiple %s arguments as a " - "variable argument list. Supplying %s as a single " - "list is deprecated and support will be removed " - "in a future release." % ( - fn.func_name, - inspect.formatargspec(*spec), - spec[1], spec[1])) - warnings.warn(msg, warning_type, stacklevel=3) - - code = "\n".join(( - "def %(name)s%(args)s:", - " if len(%(varg)s) == 1 and isinstance(%(varg)s[0], list):", - " _deprecate()", - " return fn%(unpacked_pos)s", - " else:", - " return fn%(apply_pos)s")) % meta - - env = locals().copy() - exec code in env - decorated = env[fn.func_name] - decorated.func_defaults = fn.func_defaults - update_wrapper(decorated, fn) - decorated.generated_src = code - return decorated + msg = ( + "%s%s now accepts multiple %s arguments as a " + "variable argument list. Supplying %s as a single " + "list is deprecated and support will be removed " + "in a future release." % ( + fn.func_name, + inspect.formatargspec(*spec), + spec[1], spec[1])) + warnings.warn(msg, warning_type, stacklevel=3) + + def go(fn, *args, **kw): + if isinstance(args[-1], list): + _deprecate() + return fn(*(list(args[0:-1]) + args[-1]), **kw) + else: + return fn(*args, **kw) + + return decorator(go)(fn) + return decorate def unique_symbols(used, *bases): @@ -193,23 +188,6 @@ def decorator(target): return update_wrapper(decorated, fn) return update_wrapper(decorate, target) -def to_set(x): - if x is None: - return set() - if not isinstance(x, set): - return set(to_list(x)) - else: - return x - -def to_ascii(x): - """Convert Unicode or a string with unknown encoding into ASCII.""" - - if isinstance(x, str): - return x.encode('string_escape') - elif isinstance(x, unicode): - return x.encode('unicode_escape') - else: - raise TypeError if sys.version_info >= (2, 5): def decode_slice(slc): @@ -583,42 +561,6 @@ def monkeypatch_proxied_specials(into_cls, from_cls, skip=None, only=None, pass setattr(into_cls, method, env[method]) -class SimpleProperty(object): - """A *default* property accessor.""" - - def __init__(self, key): - self.key = key - - def __set__(self, obj, value): - setattr(obj, self.key, value) - - def __delete__(self, obj): - delattr(obj, self.key) - - def __get__(self, obj, owner): - if obj is None: - return self - else: - return getattr(obj, self.key) - - -class NotImplProperty(object): - """a property that raises ``NotImplementedError``.""" - - def __init__(self, doc): - self.__doc__ = doc - - def __set__(self, obj, value): - raise NotImplementedError() - - def __delete__(self, obj): - raise NotImplementedError() - - def __get__(self, obj, owner): - if obj is None: - return self - else: - raise NotImplementedError() class OrderedProperties(object): """An object that maintains the order in which attributes are set upon it. @@ -1087,7 +1029,7 @@ class OrderedIdentitySet(IdentitySet): class UniqueAppender(object): - """Only adds items to a collection once. + """Appends items to a collection ensuring uniqueness. Additional appends() of the same object are ignored. Membership is determined by identity (``is a``) not equality (``==``). @@ -1344,21 +1286,20 @@ def function_named(fn, name): fn.func_defaults, fn.func_closure) return fn -def cache_decorator(func): +@decorator +def memoize(fn, self): """apply caching to the return value of a function.""" - name = '_cached_' + func.__name__ + name = '_cached_' + fn.__name__ - def do_with_cache(self, *args, **kwargs): - try: - return getattr(self, name) - except AttributeError: - value = func(self, *args, **kwargs) - setattr(self, name, value) - return value - return do_with_cache + try: + return getattr(self, name) + except AttributeError: + value = fn(self) + setattr(self, name, value) + return value -def reset_cached(instance, name): +def reset_memoized(instance, name): try: delattr(instance, '_cached_' + name) except AttributeError: @@ -1438,18 +1379,14 @@ class WeakIdentityMapping(weakref.WeakKeyDictionary): del self.by_id[key] except (KeyError, AttributeError): # pragma: no cover pass # pragma: no cover - if sys.version_info < (2, 4): # pragma: no cover - def _ref(self, object): - oid = id(object) - return weakref.ref(object, lambda wr: self._cleanup(wr, oid)) - else: - class _keyed_weakref(weakref.ref): - def __init__(self, object, callback): - weakref.ref.__init__(self, object, callback) - self.key = id(object) - - def _ref(self, object): - return self._keyed_weakref(object, self._cleanup) + + class _keyed_weakref(weakref.ref): + def __init__(self, object, callback): + weakref.ref.__init__(self, object, callback) + self.key = id(object) + + def _ref(self, object): + return self._keyed_weakref(object, self._cleanup) def warn(msg): diff --git a/test/orm/mapper.py b/test/orm/mapper.py index ce47c164a4..37cda6eec0 100644 --- a/test/orm/mapper.py +++ b/test/orm/mapper.py @@ -138,11 +138,7 @@ class MapperTest(_fixtures.FixtureTest): raise Exception("this exception should be stated as a warning") sess.expunge = bad_expunge - try: - Foo(_sa_session=sess) - assert False - except Exception, e: - assert isinstance(e, sa.exc.SAWarning), e + self.assertRaises(sa.exc.SAWarning, Foo, _sa_session=sess) @testing.resolve_artifact_names def test_constructor_exc_2(self): @@ -156,17 +152,8 @@ class MapperTest(_fixtures.FixtureTest): mapper(Foo, users) mapper(Bar, addresses) - try: - Foo(x=5) - assert False - except TypeError: - assert True - - try: - Bar(x=5) - assert False - except TypeError: - assert True + self.assertRaises(TypeError, Foo, x=5) + self.assertRaises(TypeError, Bar, x=5) @testing.resolve_artifact_names def test_props(self):