]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed an issue where a particular base class within utils
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 21 Jul 2015 00:35:04 +0000 (20:35 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 21 Jul 2015 00:35:04 +0000 (20:35 -0400)
didn't implement ``__slots__``, and therefore meant all subclasses
of that class didn't either, negating the rationale for ``__slots__``
to be in use.  Didn't cause any issue except on IronPython
which apparently does not implement ``__slots__`` behavior compatibly
with cPython.
Fixes #3494

doc/build/changelog/changelog_10.rst
lib/sqlalchemy/__init__.py
lib/sqlalchemy/event/attr.py
lib/sqlalchemy/orm/properties.py
lib/sqlalchemy/orm/strategies.py
lib/sqlalchemy/util/langhelpers.py
test/base/test_utils.py

index 4fdb50945661ff63e9c7f26d5ee83a83477b0bb9..64bce97ae4f949d9404b663a7cd632fb0d9315a1 100644 (file)
     .. include:: changelog_07.rst
         :start-line: 5
 
+.. changelog::
+    :version: 1.0.8
+
+    .. change::
+        :tags: bug, misc
+        :tickets: 3494
+
+        Fixed an issue where a particular base class within utils
+        didn't implement ``__slots__``, and therefore meant all subclasses
+        of that class didn't either, negating the rationale for ``__slots__``
+        to be in use.  Didn't cause any issue except on IronPython
+        which apparently does not implement ``__slots__`` behavior compatibly
+        with cPython.
+
+
 .. changelog::
     :version: 1.0.7
     :released: July 20, 2015
index 093e90bbf20d42a6a168c6676de3ea02f2a4048f..9b8d06167c4e5246d319994bdb42950e96013b0e 100644 (file)
@@ -120,7 +120,7 @@ from .schema import (
 from .inspection import inspect
 from .engine import create_engine, engine_from_config
 
-__version__ = '1.0.7'
+__version__ = '1.0.8'
 
 
 def __go(lcls):
index a64c7d08d261e2d86550ba7639b68d92216b7058..8a88e40efc9eb0cc1bd1b2ad5e3d8af3c24b7924 100644 (file)
@@ -51,7 +51,7 @@ class _ClsLevelDispatch(RefCollection):
     """Class-level events on :class:`._Dispatch` classes."""
 
     __slots__ = ('name', 'arg_names', 'has_kw',
-                 'legacy_signatures', '_clslevel')
+                 'legacy_signatures', '_clslevel', '__weakref__')
 
     def __init__(self, parent_dispatch_cls, fn):
         self.name = fn.__name__
@@ -230,9 +230,7 @@ class _EmptyListener(_InstanceLevelDispatch):
 
 
 class _CompoundListener(_InstanceLevelDispatch):
-    _exec_once = False
-
-    __slots__ = '_exec_once_mutex',
+    __slots__ = '_exec_once_mutex', '_exec_once'
 
     def _memoized_attr__exec_once_mutex(self):
         return threading.Lock()
@@ -279,11 +277,14 @@ class _ListenerCollection(_CompoundListener):
 
     """
 
-    __slots__ = 'parent_listeners', 'parent', 'name', 'listeners', 'propagate'
+    __slots__ = (
+        'parent_listeners', 'parent', 'name', 'listeners',
+        'propagate', '__weakref__')
 
     def __init__(self, parent, target_cls):
         if target_cls not in parent._clslevel:
             parent.update_subclass(target_cls)
+        self._exec_once = False
         self.parent_listeners = parent._clslevel[target_cls]
         self.parent = parent
         self.name = parent.name
@@ -339,11 +340,10 @@ class _ListenerCollection(_CompoundListener):
 
 
 class _JoinedListener(_CompoundListener):
-    _exec_once = False
-
     __slots__ = 'parent', 'name', 'local', 'parent_listeners'
 
     def __init__(self, parent, name, local):
+        self._exec_once = False
         self.parent = parent
         self.name = name
         self.local = local
index 55e02984b5b0d5ede30961567a1e020cb72fa5c8..b1f1c61c4f7cbab88fdf87305abbb24f1d5a63e1 100644 (file)
@@ -39,7 +39,7 @@ class ColumnProperty(StrategizedProperty):
         'instrument', 'comparator_factory', 'descriptor', 'extension',
         'active_history', 'expire_on_flush', 'info', 'doc',
         'strategy_class', '_creation_order', '_is_polymorphic_discriminator',
-        '_mapped_by_synonym', '_deferred_loader')
+        '_mapped_by_synonym', '_deferred_column_loader')
 
     def __init__(self, *columns, **kwargs):
         """Provide a column-level property for use with a Mapper.
index 78e9293458be95696bace6175ba460cef5fbaa90..b9ef5808bb9de5a01e346a8739fe31d9f822ad97 100644 (file)
@@ -361,7 +361,8 @@ class LazyLoader(AbstractRelationshipLoader, util.MemoizedSlots):
 
     __slots__ = (
         '_lazywhere', '_rev_lazywhere', 'use_get', '_bind_to_col',
-        '_equated_columns', '_rev_bind_to_col', '_rev_equated_columns')
+        '_equated_columns', '_rev_bind_to_col', '_rev_equated_columns',
+        '_simple_lazy_clause')
 
     def __init__(self, parent):
         super(LazyLoader, self).__init__(parent)
index 499515142a1fd7a10afef823487bfbcdf437b95d..dd258924308f1498429f86dfa4743190cda0a4cd 100644 (file)
@@ -805,6 +805,8 @@ class MemoizedSlots(object):
 
     """
 
+    __slots__ = ()
+
     def _fallback_getattr(self, key):
         raise AttributeError(key)
 
index 256f52850c7a52fa6461f159d2b26c87a6d7f8a6..8074de53ed38e241d9f7910ff7c579f834b1e194 100644 (file)
@@ -2,13 +2,14 @@ import copy
 
 from sqlalchemy import util, sql, exc, testing
 from sqlalchemy.testing import assert_raises, assert_raises_message, fixtures
-from sqlalchemy.testing import eq_, is_, ne_, fails_if
+from sqlalchemy.testing import eq_, is_, ne_, fails_if, mock
 from sqlalchemy.testing.util import picklers, gc_collect
 from sqlalchemy.util import classproperty, WeakSequence, get_callable_argspec
 from sqlalchemy.sql import column
 from sqlalchemy.util import langhelpers
 import inspect
 
+
 class _KeyedTupleTest(object):
 
     def _fixture(self, values, labels):
@@ -284,6 +285,33 @@ class MemoizedAttrTest(fixtures.TestBase):
         eq_(f1.bar(), 20)
         eq_(val[0], 21)
 
+    def test_memoized_slots(self):
+        canary = mock.Mock()
+
+        class Foob(util.MemoizedSlots):
+            __slots__ = ('foo_bar', 'gogo')
+
+            def _memoized_method_gogo(self):
+                canary.method()
+                return "gogo"
+
+            def _memoized_attr_foo_bar(self):
+                canary.attr()
+                return "foobar"
+
+        f1 = Foob()
+        assert_raises(AttributeError, setattr, f1, "bar", "bat")
+
+        eq_(f1.foo_bar, "foobar")
+
+        eq_(f1.foo_bar, "foobar")
+
+        eq_(f1.gogo(), "gogo")
+
+        eq_(f1.gogo(), "gogo")
+
+        eq_(canary.mock_calls, [mock.call.attr(), mock.call.method()])
+
 
 class ToListTest(fixtures.TestBase):
     def test_from_string(self):