]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
merged -r6204:6233 of trunk
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 2 Aug 2009 18:40:53 +0000 (18:40 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 2 Aug 2009 18:40:53 +0000 (18:40 +0000)
32 files changed:
CHANGES
examples/__init__.py [new file with mode: 0644]
examples/adjacencytree/__init__.py [new file with mode: 0644]
examples/association/__init__.py [new file with mode: 0644]
examples/collections/__init__.py [new file with mode: 0644]
examples/custom_attributes/__init__.py [new file with mode: 0644]
examples/derived_attributes/__init__.py [new file with mode: 0644]
examples/dynamic_dict/__init__.py [new file with mode: 0644]
examples/elementtree/__init__.py [new file with mode: 0644]
examples/elementtree/pickle.py
examples/graphs/__init__.py [new file with mode: 0644]
examples/nested_sets/__init__.py [new file with mode: 0644]
examples/pickle/__init__.py [new file with mode: 0644]
examples/pickle/custom_pickler.py
examples/poly_assoc/__init__.py [new file with mode: 0644]
examples/polymorph/__init__.py [new file with mode: 0644]
examples/polymorph/polymorph.py
examples/postgis/__init__.py [new file with mode: 0644]
examples/query_caching/__init__.py [new file with mode: 0644]
examples/sharding/__init__.py [new file with mode: 0644]
examples/vertical/__init__.py [new file with mode: 0644]
lib/sqlalchemy/orm/__init__.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/orm/strategies.py
setup.cfg
test/ex/__init__.py [new file with mode: 0644]
test/ex/test_examples.py [new file with mode: 0644]
test/ext/test_declarative.py
test/orm/test_mapper.py
test/orm/test_query.py
test/sql/test_selectable.py

diff --git a/CHANGES b/CHANGES
index 0a4c34b3d596b92f06299a4928c8b813a55c96b2..c073159da8087d9b9a6bdc7e11291227953cd414 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -31,6 +31,19 @@ CHANGES
       inheritance attributes which were based on 
       column_property() or similar would fail to evaluate.
       [ticket:1480]
+    
+    - Improved support for MapperProperty objects overriding
+      that of an inherited mapper for non-concrete 
+      inheritance setups - attribute extensions won't randomly
+      collide with each other.  [ticket:1488]
+    
+    - UPDATE and DELETE do not support ORDER BY, LIMIT, OFFSET,
+      etc. in standard SQL.  Query.update() and Query.delete()
+      now raise an exception if any of limit(), offset(),
+      order_by(), group_by(), or distinct() have been
+      called. [ticket:1487]
+      
+    - Added AttributeExtension to sqlalchemy.orm.__all__
       
     - Improved error message when query() is called with
       a non-SQL /entity expression. [ticket:1476]
@@ -45,6 +58,11 @@ CHANGES
       [ticket:1424].  See
       http://www.sqlalchemy.org/trac/wiki/UsageRecipes/PreFilteredQuery
       for an example.
+
+    - Fixed a somewhat hypothetical issue which would result
+      in the wrong primary key being calculated for a mapper
+      using the old polymorphic_union function - but this
+      is old stuff.  [ticket:1486]
       
 - sql
     - Fixed a bug in extract() introduced in 0.5.4 whereby
@@ -59,6 +77,10 @@ CHANGES
     - Fixed bug in Table and Column whereby passing empty
       dict for "info" argument would raise an exception.
       [ticket:1482]
+
+- oracle
+    - Backported 0.6 fix for Oracle alias names not getting
+      truncated.  [ticket:1309]
       
 - ext
    - The collection proxies produced by associationproxy are now
@@ -69,6 +91,11 @@ CHANGES
    - Declarative will raise an informative exception if 
      __table_args__ is passed as a tuple with no dict argument.
      Improved documentation.  [ticket:1468]
+
+- test
+   - Added examples into the test suite so they get exercised
+     regularly and cleaned up a couple deprecation warnings.
+
      
 0.5.5
 =======
diff --git a/examples/__init__.py b/examples/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/adjacencytree/__init__.py b/examples/adjacencytree/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/association/__init__.py b/examples/association/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/collections/__init__.py b/examples/collections/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/custom_attributes/__init__.py b/examples/custom_attributes/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/derived_attributes/__init__.py b/examples/derived_attributes/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/dynamic_dict/__init__.py b/examples/dynamic_dict/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/elementtree/__init__.py b/examples/elementtree/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
index 220bb2295386a75a1311c597f76514c078ad8ffb..2176512cf1d8c5532987fdb4b72b37f6997a1b7a 100644 (file)
@@ -26,12 +26,17 @@ from xml.etree import ElementTree
 engine = create_engine('sqlite://')
 meta = MetaData(engine)
 
+# setup a comparator for the PickleType since it's a mutable
+# element.
+def are_elements_equal(x, y):
+    return x == y
+
 # stores a top level record of an XML document.  
 # the "element" column will store the ElementTree document as a BLOB.
 documents = Table('documents', meta,
     Column('document_id', Integer, primary_key=True),
     Column('filename', String(30), unique=True),
-    Column('element', PickleType)
+    Column('element', PickleType(comparator=are_elements_equal))
 )
 
 meta.create_all()
diff --git a/examples/graphs/__init__.py b/examples/graphs/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/nested_sets/__init__.py b/examples/nested_sets/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/pickle/__init__.py b/examples/pickle/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
index 79a8b3fa393dcc42632187c79371591a8632b69b..a02b708e570ec57186d116f2f2dc0b153950d3c6 100644 (file)
@@ -68,7 +68,13 @@ class Foo(object):
 class Bar(object):
     def __init__(self, value):
         self.data = value
-    
+
+    def __eq__(self, other):
+        if not other is None:
+            return self.data == other.data
+        return NotImplemented
+
+
 mapper(Foo, foo_table, extension=MyExt())
 mapper(Bar, bar_table)
 
diff --git a/examples/poly_assoc/__init__.py b/examples/poly_assoc/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/polymorph/__init__.py b/examples/polymorph/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
index ea56ffed1f36988e726ff2f19c0937e096351447..60ef98f57dc19e1949de30a388a37e858dcd15cf 100644 (file)
@@ -66,7 +66,7 @@ mapper(Company, companies, properties={
     'employees': relation(Person, lazy=False, backref='company', cascade="all, delete-orphan")
 })
 
-session = create_session(echo_uow=False)
+session = create_session()
 c = Company(name='company1')
 c.employees.append(Manager(name='pointy haired boss', status='AAB', manager_name='manager1'))
 c.employees.append(Engineer(name='dilbert', status='BBA', engineer_name='engineer1', primary_language='java'))
diff --git a/examples/postgis/__init__.py b/examples/postgis/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/query_caching/__init__.py b/examples/query_caching/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/sharding/__init__.py b/examples/sharding/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/vertical/__init__.py b/examples/vertical/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
index 41a8550fff1d909c7a79bdfc40c9f25a38d23aec..3c39316da39a3abcd38924460187cd75a76dad3c 100644 (file)
@@ -26,6 +26,7 @@ from sqlalchemy.orm.interfaces import (
      MapperExtension,
      PropComparator,
      SessionExtension,
+     AttributeExtension,
      )
 from sqlalchemy.orm.util import (
      AliasedClass as aliased,
@@ -61,6 +62,7 @@ __all__ = (
     'EXT_STOP',
     'InstrumentationManager',
     'MapperExtension',
+    'AttributeExtension',
     'Validator',
     'PropComparator',
     'Query',
index 5335a6a6d3802a0e508a5ad970144018c5303869..b502c55aa44a5168fa302ea9785f4b3fcf8da55b 100644 (file)
@@ -649,6 +649,9 @@ class Mapper(object):
                         return self
 
                     # initialize properties on all mappers
+                    # note that _mapper_registry is unordered, which 
+                    # may randomly conceal/reveal issues related to 
+                    # the order of mapper compilation
                     for mapper in list(_mapper_registry):
                         if getattr(mapper, '_compile_failed', False):
                             raise sa_exc.InvalidRequestError("One or more mappers failed to compile.  Exception was probably "
index 0ea67f6e53b7bfe6bb977e06001433786263f984..21137bc28b19c90eec056c428d5447e28d1e671d 100644 (file)
@@ -316,6 +316,21 @@ class Query(object):
                 "Otherwise, call %s() before limit() or offset() are applied." % (meth, meth)
             )
 
+    def _no_select_modifiers(self, meth):
+        if not self._enable_assertions:
+            return
+        for attr, methname, notset in (
+            ('_limit', 'limit()', None),
+            ('_offset', 'offset()', None),
+            ('_order_by', 'order_by()', False),
+            ('_group_by', 'group_by()', False),
+            ('_distinct', 'distinct()', False),
+        ):
+            if getattr(self, attr) is not notset:
+                raise sa_exc.InvalidRequestError(
+                    "Can't call Query.%s() when %s has been called" % (meth, methname)
+                )
+
     def _get_options(self, populate_existing=None, 
                             version_check=None, 
                             only_load_props=None, 
@@ -1617,6 +1632,7 @@ class Query(object):
 
         if synchronize_session not in [False, 'evaluate', 'fetch']:
             raise sa_exc.ArgumentError("Valid strategies for session synchronization are False, 'evaluate' and 'fetch'")
+        self._no_select_modifiers("delete")
 
         self = self.enable_eagerloads(False)
 
@@ -1716,6 +1732,7 @@ class Query(object):
 
         if synchronize_session not in [False, 'evaluate', 'fetch']:
             raise sa_exc.ArgumentError("Valid strategies for session synchronization are False, 'evaluate' and 'fetch'")
+        self._no_select_modifiers("update")
 
         self = self.enable_eagerloads(False)
 
index 8204edd6740faef05a011ac7cb592eaa0986cd5a..e19e8fb31c052a7b3d8eb139b0809d32dd721e5e 100644 (file)
@@ -46,8 +46,10 @@ def _register_attribute(strategy, mapper, useobject,
     if useobject:
         attribute_ext.append(sessionlib.UOWEventHandler(prop.key))
 
+    
     for m in mapper.polymorphic_iterator():
-        if (m is prop.parent or not m.concrete) and m.has_property(prop.key):
+        if prop is m._props.get(prop.key):
+            
             attributes.register_attribute_impl(
                 m.class_, 
                 prop.key, 
index 25ee974dba0446e4e15604bb1b2d3857c6d0ff69..517492791e0893c21e52772db3272f2fb0905e56 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -3,4 +3,4 @@ tag_build = dev
 tag_svn_revision = true
 
 [nosetests]
-with-sqlalchemy = true
\ No newline at end of file
+with-sqlalchemy = true
diff --git a/test/ex/__init__.py b/test/ex/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/ex/test_examples.py b/test/ex/test_examples.py
new file mode 100644 (file)
index 0000000..411c44a
--- /dev/null
@@ -0,0 +1,46 @@
+from sqlalchemy.test import *
+import os, re
+
+
+def find_py_files(dirs):
+    for dn in dirs:
+        dn = os.path.abspath(dn)
+        for root, dirs, files in os.walk(dn):
+            for r in '.svn', 'CVS', '.git', '.hg':
+                try:
+                    dirs.remove(r)
+                except ValueError:
+                    pass
+
+            pyfiles = [fn for fn in files if fn.endswith('.py')]
+            if not pyfiles:
+                continue
+
+            # Find the root of the packages.
+            packroot = root
+            while 1:
+                if not os.path.exists(os.path.join(packroot, '__init__.py')):
+                    break
+                packroot = os.path.dirname(packroot)
+
+            for fn in pyfiles:
+                yield os.path.join(root[len(packroot)+1:], fn)
+
+def filename_to_module_name(fn):
+    if os.path.basename(fn) == '__init__.py':
+        fn = os.path.dirname(fn)
+    return re.sub('\.py$', '', fn.replace(os.sep, '.'))
+
+def find_modules(*args):
+    for fn in find_py_files(args or ('./examples',)):
+        yield filename_to_module_name(fn)
+
+def check_import(module):
+    __import__(module)
+
+
+class ExamplesTest(TestBase):
+    def test_examples(self):
+        for module in find_modules():
+            check_import.description = module
+            yield check_import, module
index 9ca8356918922e68eeeefafb4ff758dc13598f8b..745e3b7cf8cab3169a8bf7ae6472e11518a1e7b5 100644 (file)
@@ -1473,7 +1473,7 @@ class DeclarativeReflectionTest(testing.TestBase):
 
             if testing.against('oracle', 'firebird'):
                 id = Column('id', Integer, primary_key=True, test_needs_autoincrement=True)
-
+        
         u1 = User(name='u1', addresses=[
             Address(email='one'),
             Address(email='two'),
index 43700882781599070e6db8eeffd09384647315a6..c34ccdbab852b6def978bceb2a6a86f71236c227 100644 (file)
@@ -2220,6 +2220,64 @@ class NoLoadTest(_fixtures.FixtureTest):
             )
 
 
+class AttributeExtensionTest(_base.MappedTest):
+    @classmethod
+    def define_tables(cls, metadata):
+        Table('t1', 
+            metadata,
+            Column('id', Integer, primary_key=True),
+            Column('type', String(40)),
+            Column('data', String(50))
+            
+        )
+
+    @testing.resolve_artifact_names
+    def test_cascading_extensions(self):
+        ext_msg = []
+        
+        class Ex1(sa.orm.AttributeExtension):
+            def set(self, state, value, oldvalue, initiator):
+                ext_msg.append("Ex1 %r" % value)
+                return "ex1" + value
+                
+        class Ex2(sa.orm.AttributeExtension):
+            def set(self, state, value, oldvalue, initiator):
+                ext_msg.append("Ex2 %r" % value)
+                return "ex2" + value
+        
+        class A(_base.BasicEntity):
+            pass
+        class B(A):
+            pass
+        class C(B):
+            pass
+            
+        mapper(A, t1, polymorphic_on=t1.c.type, polymorphic_identity='a', properties={
+            'data':column_property(t1.c.data, extension=Ex1())
+        })
+        mapper(B, polymorphic_identity='b', inherits=A)
+        mc = mapper(C, polymorphic_identity='c', inherits=B, properties={
+            'data':column_property(t1.c.data, extension=Ex2())
+        })
+        
+        a1 = A(data='a1')
+        b1 = B(data='b1')
+        c1 = C(data='c1')
+        
+        eq_(a1.data, 'ex1a1')
+        eq_(b1.data, 'ex1b1')
+        eq_(c1.data, 'ex2c1')
+        
+        a1.data = 'a2'
+        b1.data='b2'
+        c1.data = 'c2'
+        eq_(a1.data, 'ex1a2')
+        eq_(b1.data, 'ex1b2')
+        eq_(c1.data, 'ex2c2')
+        
+        eq_(ext_msg, ["Ex1 'a1'", "Ex1 'b1'", "Ex2 'c1'", "Ex1 'a2'", "Ex1 'b2'", "Ex2 'c2'"])
+        
+    
 class MapperExtensionTest(_fixtures.FixtureTest):
     run_inserts = None
     
index cb60bef69a54f0a03a0a7a8b1b29647284b36df3..8cb7ef969bd676bfcea8ef574e7071490f66eb95 100644 (file)
@@ -2879,6 +2879,22 @@ class UpdateDeleteTest(_base.MappedTest):
             'user': relation(User, lazy=False, backref=backref('documents', lazy=True))
         })
 
+    @testing.resolve_artifact_names
+    def test_illegal_operations(self):
+        s = create_session()
+        
+        for q, mname in (
+            (s.query(User).limit(2), "limit"),
+            (s.query(User).offset(2), "offset"),
+            (s.query(User).limit(2).offset(2), "limit"),
+            (s.query(User).order_by(User.id), "order_by"),
+            (s.query(User).group_by(User.id), "group_by"),
+            (s.query(User).distinct(), "distinct")
+        ):
+            assert_raises_message(sa_exc.InvalidRequestError, r"Can't call Query.update\(\) when %s\(\) has been called" % mname, q.update, {'name':'ed'})
+            assert_raises_message(sa_exc.InvalidRequestError, r"Can't call Query.delete\(\) when %s\(\) has been called" % mname, q.delete)
+            
+        
     @testing.resolve_artifact_names
     def test_delete(self):
         sess = create_session(bind=testing.db, autocommit=False)
index 0e0cef38f23e223023fcbfaf97793a9c5a677eb2..95ca0d17bf21a2a824d0c3026c418d6bdf7832d8 100644 (file)
@@ -432,7 +432,6 @@ class ReduceTest(TestBase, AssertsExecutionResults):
                 magazine_page_table.c.page_id, 
                 cast(null(), Integer).label('magazine_page_id')
             ]).select_from(page_table.join(magazine_page_table)),
-            
         ).alias('pjoin')
 
         eq_(