]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Using a mixin won't break if the mixin implements an
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 25 Mar 2010 21:25:32 +0000 (17:25 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 25 Mar 2010 21:25:32 +0000 (17:25 -0400)
unpredictable __getattribute__(), i.e. Zope interfaces.
[ticket:1746]

CHANGES
lib/sqlalchemy/ext/declarative.py
test/ext/test_declarative.py

diff --git a/CHANGES b/CHANGES
index db32156f947873bdfdf79e3672805d20adf510d3..a14e39dbd5d0152f1fa05d8136dad59a9a795d46 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -71,6 +71,11 @@ CHANGES
    - The psycopg2 dialect will log NOTICE messages via the
      "sqlalchemy.dialects.postgresql" logger name. 
      [ticket:877]
+
+- declarative
+   - Using a mixin won't break if the mixin implements an 
+     unpredictable __getattribute__(), i.e. Zope interfaces.
+     [ticket:1746]
      
 0.6beta2
 ========
index 775efbff1b434b3959cca5fb6e820d5e359575ad..435d38161927fc5a361f6976046801d179c6af75 100644 (file)
@@ -545,7 +545,7 @@ def _as_declarative(cls, classname, dict_):
         names = dir(base)
         if not _is_mapped_class(base):
             for name in names:
-                obj = getattr(base,name)
+                obj = getattr(base,name, None)
                 if isinstance(obj, Column):
                     dict_[name]=column_copies[obj]=obj.copy()
             get_mapper_args = get_mapper_args or getattr(base,'__mapper_args__',None)
index a6f3f7a796ab2960dcd3f8198efadc38c33b9dbf..fbe47ee7fce03d1d2702ba50b80d9b8ee0fd6cc0 100644 (file)
@@ -6,7 +6,9 @@ import sqlalchemy as sa
 from sqlalchemy.test import testing
 from sqlalchemy import MetaData, Integer, String, ForeignKey, ForeignKeyConstraint, asc, Index
 from sqlalchemy.test.schema import Table, Column
-from sqlalchemy.orm import relationship, create_session, class_mapper, joinedload, compile_mappers, backref, clear_mappers, polymorphic_union, deferred
+from sqlalchemy.orm import relationship, create_session, class_mapper, \
+                            joinedload, compile_mappers, backref, clear_mappers, \
+                            polymorphic_union, deferred
 from sqlalchemy.test.testing import eq_
 from sqlalchemy.util import classproperty
 
@@ -75,7 +77,9 @@ class DeclarativeTest(DeclarativeTestBase):
                 __table__ = t
                 foo = Column(Integer, primary_key=True)
         # can't specify new columns not already in the table
-        assert_raises_message(sa.exc.ArgumentError, "Can't add additional column 'foo' when specifying __table__", go)
+        assert_raises_message(sa.exc.ArgumentError, 
+                                "Can't add additional column 'foo' when specifying __table__", 
+                                go)
 
         # regular re-mapping works tho
         class Bar(Base):
@@ -84,6 +88,33 @@ class DeclarativeTest(DeclarativeTestBase):
             
         assert class_mapper(Bar).get_property('some_data').columns[0] is t.c.data
     
+    def test_difficult_class(self):
+        """test no getattr() errors with a customized class"""
+
+        # metaclass to mock the way zope.interface breaks getattr()
+        class BrokenMeta(type):
+            def __getattribute__(self, attr):
+                if attr == 'xyzzy':
+                    raise AttributeError, 'xyzzy'
+                else:
+                    return object.__getattribute__(self,attr)
+
+        # even though this class has an xyzzy attribute, getattr(cls,"xyzzy")
+        # fails
+        class BrokenParent(object):
+            __metaclass__ = BrokenMeta
+            xyzzy = "magic"
+
+        # _as_declarative() inspects obj.__class__.__bases__
+        class User(BrokenParent,ComparableEntity):
+            __tablename__ = 'users'
+            id = Column('id', Integer, primary_key=True,
+                test_needs_autoincrement=True)
+            name = Column('name', String(50))
+
+        decl.instrument_declarative(User,{},Base.metadata)
+
+        
     def test_undefer_column_name(self):
         # TODO: not sure if there was an explicit
         # test for this elsewhere