summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES5
-rw-r--r--lib/sqlalchemy/ext/hybrid.py4
-rw-r--r--test/ext/test_hybrid.py32
3 files changed, 35 insertions, 6 deletions
diff --git a/CHANGES b/CHANGES
index 1859b9145..bab93885c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -52,6 +52,11 @@ CHANGES
- [bug] ensure pickleability of all ORM exceptions
for multiprocessing compatibility. [ticket:2371]
+ - [bug] implemented standard "can't set attribute" /
+ "can't delete attribute" AttributeError when
+ setattr/delattr used on a hybrid that doesn't
+ define fset or fdel. [ticket:2353]
+
- sql
- [feature] Added "false()" and "true()" expression
constructs to sqlalchemy.sql namespace, though
diff --git a/lib/sqlalchemy/ext/hybrid.py b/lib/sqlalchemy/ext/hybrid.py
index fc09c172d..086ec9033 100644
--- a/lib/sqlalchemy/ext/hybrid.py
+++ b/lib/sqlalchemy/ext/hybrid.py
@@ -613,9 +613,13 @@ class hybrid_property(object):
return self.fget(instance)
def __set__(self, instance, value):
+ if self.fset is None:
+ raise AttributeError("can't set attribute")
self.fset(instance, value)
def __delete__(self, instance):
+ if self.fdel is None:
+ raise AttributeError("can't delete attribute")
self.fdel(instance)
def setter(self, fset):
diff --git a/test/ext/test_hybrid.py b/test/ext/test_hybrid.py
index 7ed8e207c..b85d4b0fc 100644
--- a/test/ext/test_hybrid.py
+++ b/test/ext/test_hybrid.py
@@ -3,7 +3,7 @@ from sqlalchemy.orm import relationship, Session, aliased
from test.lib.schema import Column
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext import hybrid
-from test.lib.testing import eq_, AssertsCompiledSQL
+from test.lib.testing import eq_, AssertsCompiledSQL, assert_raises_message
from test.lib import fixtures
class PropertyComparatorTest(fixtures.TestBase, AssertsCompiledSQL):
@@ -191,7 +191,7 @@ class PropertyExpressionTest(fixtures.TestBase, AssertsCompiledSQL):
class PropertyValueTest(fixtures.TestBase, AssertsCompiledSQL):
__dialect__ = 'default'
- def _fixture(self):
+ def _fixture(self, assignable):
Base = declarative_base()
class A(Base):
@@ -203,14 +203,34 @@ class PropertyValueTest(fixtures.TestBase, AssertsCompiledSQL):
def value(self):
return self._value - 5
- @value.setter
- def value(self, v):
- self._value = v + 5
+ if assignable:
+ @value.setter
+ def value(self, v):
+ self._value = v + 5
return A
+ def test_nonassignable(self):
+ A = self._fixture(False)
+ a1 = A(_value=5)
+ assert_raises_message(
+ AttributeError,
+ "can't set attribute",
+ setattr, a1, 'value', 10
+ )
+
+ def test_nondeletable(self):
+ A = self._fixture(False)
+ a1 = A(_value=5)
+ assert_raises_message(
+ AttributeError,
+ "can't delete attribute",
+ delattr, a1, 'value'
+ )
+
+
def test_set_get(self):
- A = self._fixture()
+ A = self._fixture(True)
a1 = A(value=5)
eq_(a1.value, 5)
eq_(a1._value, 10)