From d706a661709a2c963f8adde53959c5fc42f26d58 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Fri, 27 Jan 2012 20:59:33 -0500 Subject: [PATCH] - [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] --- CHANGES | 5 +++++ lib/sqlalchemy/ext/hybrid.py | 4 ++++ test/ext/test_hybrid.py | 32 ++++++++++++++++++++++++++------ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 1859b91459..bab93885c3 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 fc09c172df..086ec90336 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 7ed8e207c3..b85d4b0fc2 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) -- 2.47.2