summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2015-07-20 20:35:04 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2015-07-20 20:35:04 -0400
commit575f080850a0a061ccb7ac40e3ea1fbf6b0fedf4 (patch)
tree4c3f6020e3d1a6bd4d761d31f996665fde826dce
parenta4b8aa320d63f7c0dc7c70dfce56f0af593d95f0 (diff)
downloadsqlalchemy-575f080850a0a061ccb7ac40e3ea1fbf6b0fedf4.tar.gz
- 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. Fixes #3494
-rw-r--r--doc/build/changelog/changelog_10.rst15
-rw-r--r--lib/sqlalchemy/__init__.py2
-rw-r--r--lib/sqlalchemy/event/attr.py14
-rw-r--r--lib/sqlalchemy/orm/properties.py2
-rw-r--r--lib/sqlalchemy/orm/strategies.py3
-rw-r--r--lib/sqlalchemy/util/langhelpers.py2
-rw-r--r--test/base/test_utils.py30
7 files changed, 57 insertions, 11 deletions
diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst
index 4fdb50945..64bce97ae 100644
--- a/doc/build/changelog/changelog_10.rst
+++ b/doc/build/changelog/changelog_10.rst
@@ -16,6 +16,21 @@
: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
diff --git a/lib/sqlalchemy/__init__.py b/lib/sqlalchemy/__init__.py
index 093e90bbf..9b8d06167 100644
--- a/lib/sqlalchemy/__init__.py
+++ b/lib/sqlalchemy/__init__.py
@@ -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):
diff --git a/lib/sqlalchemy/event/attr.py b/lib/sqlalchemy/event/attr.py
index a64c7d08d..8a88e40ef 100644
--- a/lib/sqlalchemy/event/attr.py
+++ b/lib/sqlalchemy/event/attr.py
@@ -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
diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py
index 55e02984b..b1f1c61c4 100644
--- a/lib/sqlalchemy/orm/properties.py
+++ b/lib/sqlalchemy/orm/properties.py
@@ -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.
diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py
index 78e929345..b9ef5808b 100644
--- a/lib/sqlalchemy/orm/strategies.py
+++ b/lib/sqlalchemy/orm/strategies.py
@@ -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)
diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py
index 499515142..dd2589243 100644
--- a/lib/sqlalchemy/util/langhelpers.py
+++ b/lib/sqlalchemy/util/langhelpers.py
@@ -805,6 +805,8 @@ class MemoizedSlots(object):
"""
+ __slots__ = ()
+
def _fallback_getattr(self, key):
raise AttributeError(key)
diff --git a/test/base/test_utils.py b/test/base/test_utils.py
index 256f52850..8074de53e 100644
--- a/test/base/test_utils.py
+++ b/test/base/test_utils.py
@@ -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):