[python-sqlamp] Fixed #715906 FTBFS due to SA upgrade to 0.7.x

Martin Bacovsky mbacovsk at fedoraproject.org
Mon Oct 3 22:01:38 UTC 2011


commit 3536699df93f4e62a6680546663978d3717eabdc
Author: Martin Bačovský <mbacovsk at redhat.com>
Date:   Thu Jun 30 16:43:59 2011 +0200

    Fixed #715906 FTBFS due to SA upgrade to 0.7.x

 python-sqlamp.spec |    8 +-
 sa72.patch         |  583 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 590 insertions(+), 1 deletions(-)
---
diff --git a/python-sqlamp.spec b/python-sqlamp.spec
index 65decd6..29f9373 100644
--- a/python-sqlamp.spec
+++ b/python-sqlamp.spec
@@ -4,7 +4,7 @@
 
 Name:           python-sqlamp
 Version:        0.5.2
-Release:        2%{?dist}
+Release:        3%{?dist}
 Summary:        Library for working with hierarchical data structures using SQLAlchemy
 
 Group:          Development/Languages
@@ -13,6 +13,7 @@ URL:            http://sqlamp.angri.ru/
 Source0:        http://sqlamp.angri.ru/sqlamp-%{version}.tar.gz
 Patch0:         sa0.6.6.patch
 Patch1:         sqlite-3.6.x-bug-workaround.patch
+Patch2:         sa72.patch
 BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 BuildArch:      noarch
 
@@ -32,6 +33,7 @@ with hierarchical data structures. sqlamp uses (and depends on) SQLAlchemy.
 %setup -q -n sqlamp-%{version}
 %patch0 -p1
 %patch1 -p1
+%patch2 -p1
 
 %build
 %{__python} setup.py build
@@ -67,6 +69,10 @@ rm -rf %{buildroot}
 
 
 %changelog
+* Thu Jun 30 2011 Martin Bacovsky <mbacovsk at redhat.com> - 0.5.2-3
+- Fixed #715906 FTBFS due to SA upgrade to 0.7.x
+- Backported upstreams patch adding support for SQLAlchemy 0.7.2
+
 * Mon Mar 28 2011 Martin Bacovsky <mbacovsk at redhat.com> - 0.5.2-2
 - Fixed support of SQLAlchemy 0.6.6 in MPOptions.order_by_clause(). Patch by Josip Delic.
 - Workaround for bug in sqlite 3.6.x (problems with binding two integer attributes). Initial patch by Josip Delic.
diff --git a/sa72.patch b/sa72.patch
new file mode 100644
index 0000000..9094996
--- /dev/null
+++ b/sa72.patch
@@ -0,0 +1,583 @@
+diff -r 964320ab0ff5 -r 26f59cbfac52 CHANGES
+--- a/CHANGES	Tue Feb 15 21:57:02 2011 +0100
++++ b/CHANGES	Wed Jun 22 23:15:27 2011 +0200
+@@ -1,7 +1,26 @@
+-0.5.3: *not released yet*
+--------------------------
+-- Fixed support of SQLAlchemy 0.6.6 in :meth:`MPOptions.order_by_clause`.
+-  Patch by Josip Delic.
++0.6: *not released yet*
++-----------------------
++The most notable change in 0.6 is the full support of SQLAlchemy 0.7.2,
++0.6.8 and 0.5.8. Adding support of 0.7 required some backward-incompatible
++changes in public API, from which the biggest one is inability to use
++:class:`MPClassManager` instances as arguments for `order_by()`. If you use
++`query*` methods from :class:`MPClassManager` and :class:`MPInstanceManager`
++you don't need to worry --- query objects which come from there are properly
++ordered. If you use old semantic like ``session.query(Node).order_by(Node.mp)``
++you will need to convert such code to use :meth:`MPClassManager.query`, like
++``Node.mp.query(session)``.
++
++Another similar change is that :func:`tree_recursive_iterator` doesn't reorder
++the argument provided, so if you construct your queries by hand (which is not
++recommended) --- make sure that they're properly ordered.
++
++- :class:`MPClassManager` can not be used as an argument for `order_by()`.
++  Instead use method :meth:`MPClassManager.query` for constructing queries.
++- Method :meth:`MPClassManager.query_all_trees` was renamed
++  to :meth:`~MPClassManager.query`. The old name still works though.
++- :func:`tree_recursive_iterator` doesn't reorder query argument anymore.
++- Added support of SQLAlchemy 0.7.2.
++- Documentation was cleaned up and updated.
+ - Workaround for bug in sqlite 3.6.x (problems with binding two integer
+   attributes). Initial patch by Josip Delic.
+ 
+@@ -22,7 +41,7 @@
+ 0.5: released 2009-09-05
+ ------------------------
+ This release contains some backward-incompatible changes in setup facilities.
+-The main highligts are support of ``declarative`` SQLAlchemy extension and
++The main highlights are support of ``declarative`` SQLAlchemy extension and
+ some cleaning up in :class:`MPManager`'s constructor options.
+ 
+ - Index name now includes table name as prefix so there is an
+@@ -49,14 +68,14 @@
+ ------------------------
+ - Small fixes in documentation: actually Tropashko was not the
+   first who introduced MP, he only promoted it.
+-- Implemented :meth:`MPClassManager.query_all_trees`
++- Implemented :meth:`MPClassManager.query_all_trees`.
+ - Fixed a bug of :meth:`MPClassManager.rebuild_all_trees` did not
+   reset path for root nodes.
+-- Implemented :func:`tree_recursive_iterator`
++- Implemented :func:`tree_recursive_iterator`.
+ - Changed the value of path field for a root nodes. Previously
+   they used to had ``'0' * steplen`` path and so first-level
+   children gain ``'0' * steplen * 2``, but now new roots will
+-  have an ampty string in their path field. This change should
++  have an empty string in their path field. This change should
+   be backward-compatible as it touches only new trees. But
+   if you want to have no difference between two identical old
+   and new trees in your table you can rebuild all your trees
+diff -r 964320ab0ff5 -r 26f59cbfac52 LICENSE
+--- a/LICENSE	Tue Feb 15 21:57:02 2011 +0100
++++ b/LICENSE	Wed Jun 22 23:15:27 2011 +0200
+@@ -1,4 +1,4 @@
+-Copyright 2009 Anton Gritsay <anton at angri.ru>
++Copyright 2009-2011 Anton Gritsay <anton at angri.ru>
+ 
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+diff -r 964320ab0ff5 -r 26f59cbfac52 doc/index.rst
+--- a/doc/index.rst	Tue Feb 15 21:57:02 2011 +0100
++++ b/doc/index.rst	Wed Jun 22 23:15:27 2011 +0200
+@@ -62,8 +62,8 @@
+         }
+     )
+ 
+-You may see value provided as `properties` argument: this is a way `recomended
+-<http://www.sqlalchemy.org/docs/05/mappers.html#adjacency-list-relationships>`_
++You may see value provided as `properties` argument: this is a way `recommended
++<http://www.sqlalchemy.org/docs/orm/relationships.html#adjacency-list-relationships>`_
+ by the official SQLAlchemy documentation to set up an adjacency relation.
+ 
+ 
+@@ -103,8 +103,8 @@
+ 
+ As you can see it is pretty much the same as usual for `sqlalchemy's
+ "declarative" extension
+-<http://www.sqlalchemy.org/docs/05/reference/ext/declarative.html>`_.
+-Only two things here are sqlamp-special: ``metaclass`` argument provided
++<http://www.sqlalchemy.org/docs/orm/extensions/declarative.html>`_.
++Only two things here are sqlamp-specific: ``metaclass`` argument provided
+ to ``declarative_base()`` factory function should be :class:`DeclarativeMeta`
+ and the node class should have an ``__mp_manager__`` property with string
+ value. See :class:`DeclarativeMeta` for more information about options.
+@@ -135,7 +135,7 @@
+     grandchild.mp.query_ancestors().all()
+     # [<Node 'root'>, <Node 'child1'>]
+ 
+-    session.query(Node).order_by(Node.mp).all()
++    Node.mp.query(session).all()
+     # [<Node 'root'>, <Node 'child1'>, <Node 'grandchild'>, <Node 'child2'>]
+ 
+     for node in root.mp.query_descendants(and_self=True):
+@@ -151,11 +151,11 @@
+ *Note*: ``Node.mp`` (a so-called "class manager") is not the same
+ as ``node.mp`` ("instance manager"). Do not confuse them as they are for
+ different purposes and their APIs has no similar. Class manager (see
+-:class:`MPClassManager`) used to features that are not intended
++:class:`MPClassManager`) exists for features that are not intended
+ to particular node but for the whole tree: basic setup (mapper
+-extension) and tree-maintainance functions. And an instance managers
++extension) and tree-maintenance functions. And an instance managers
+ (:class:`MPInstanceManager`) are each unique to and bounded to a node.
+-They are implements a queries for a related nodes and other things
++They allow to make queries for related nodes and other things
+ specific to concrete node. There is also third kind of values
+ that ``MPManager`` descriptor may return, see :class:`its reference
+ <MPManager>` for more info.
+@@ -164,21 +164,21 @@
+ ----------------------
+ Implementation details
+ ----------------------
+-:mod:`sqlamp` had borrowed some implementation ideas from `django-treebeard`_.
++:mod:`sqlamp` borrowed some implementation ideas from `django-treebeard`_.
+ In particular, `sqlamp` uses the same alphabet (which consists of numeric
+ digits and latin-letters in upper case), `sqlamp` as like as `django-treebeard`
+ doesn't use path parts delimiter --- path parts has fixed adjustable length.
+-But unlike `django-treebeard` `sqlamp` stores each tree absolutelly
++But unlike `django-treebeard` `sqlamp` stores each tree absolutely
+ stand-alone --- two or more trees may (and will) have identical values in
+ `path` and `depth` fields and be different only by values in `tree_id` field.
+ This is the way that can be found in `django-mptt`_.
+ 
+ :mod:`sqlamp` works *only* on basis of Adjacency Relations. This solution
+-makes data more denormalized but more fault-tolerant. It is able to rebuild
+-all pathes for all trees using only `AL` data. Also it makes applying `sqlamp`
+-on existing project easer.
++makes data more denormalized but more fault-tolerant. It makes possible
++rebuilding all paths for all trees using only `AL` data. Also it makes
++applying `sqlamp` on existing project easier.
+ 
+-.. _`django-treebeard`: http://django-treebeard.googlecode.com/
++.. _`django-treebeard`: https://tabo.pe/projects/django-treebeard/
+ .. _`django-mptt`: http://django-mptt.googlecode.com/
+ 
+ 
+@@ -204,10 +204,7 @@
+ 
+ 
+ .. autoclass:: MPClassManager
+-    :members:
+-
+-    .. automethod:: __clause_element__
+-
++    :members: max_children, max_depth, query, rebuild_all_trees, rebuild_subtree
+ 
+ .. autoclass:: MPInstanceManager
+     :members: filter_descendants, query_descendants, filter_children, query_children, filter_ancestors, query_ancestors
+diff -r 964320ab0ff5 -r 26f59cbfac52 setup.py
+--- a/setup.py	Tue Feb 15 21:57:02 2011 +0100
++++ b/setup.py	Wed Jun 22 23:15:27 2011 +0200
+@@ -36,11 +36,14 @@
+ and `PostgreSQL`_ (tested with 8.3.7), but sqlamp should work with any
+ other DBMS supported by SQLAlchemy.
+ 
++Supported versions of SQLAlchemy include current minor versions
++of branches 0.5 and 0.6 as well as 0.7 since 0.7.2.
++
+ .. _`Vadim Tropashko`: http://vadimtropashko.wordpress.com
+ .. _`Sql Design Patterns`:
+    http://www.rampant-books.com/book_2006_1_sql_coding_styles.htm
+ .. _`Trees in SQL: Nested Sets and Materialized Path (by Vadim Tropashko)`:
+-   http://www.dbazine.com/oracle/or-articles/tropashko4
++   https://communities.bmc.com/communities/docs/DOC-9902
+ .. _`sqlite`: http://sqlite.org
+ .. _`MySQL`: http://mysql.com
+ .. _`PostgreSQL`: http://postgresql.org
+@@ -51,7 +54,7 @@
+ except ImportError:
+     from distutils.core import setup
+ 
+-version = "0.5.2"
++version = "0.5.2"
+ url = "http://sqlamp.angri.ru"
+ 
+ setup(
+@@ -69,7 +72,7 @@
+     test_suite="tests.run-all.get_suite",
+ 
+     classifiers=(
+-        'Development Status :: 3 - Alpha',
++        'Development Status :: 3 - Alpha',
+         'Environment :: Plugins',
+         'Intended Audience :: Developers',
+         'License :: OSI Approved :: BSD License',
+diff -r 964320ab0ff5 -r 26f59cbfac52 sqlamp/__init__.py
+--- a/sqlamp/__init__.py	Tue Feb 15 21:57:02 2011 +0100
++++ b/sqlamp/__init__.py	Wed Jun 22 23:15:27 2011 +0200
+@@ -28,7 +28,7 @@
+           of setting values in `tree_id`, `path` and `depth` fields is done
+           by `sqlamp`.
+         * Fetching node's descendants, ancestors and children using the most
+-          efficient way available (see :class:`MPInstanceManager`)
++          efficient way available (see :class:`MPInstanceManager`).
+         * Autochecking exhaustion of tree size limits --- maximum number of
+           children and maximum nesting level (see :class:`MPManager` to learn
+           more about limits fine-tuning) is done during session flush.
+@@ -48,11 +48,14 @@
+     and `PostgreSQL`_ (tested with 8.3.7), but sqlamp should work with any
+     other DBMS supported by SQLAlchemy.
+ 
++    Supported versions of SQLAlchemy include current minor versions
++    of branches 0.5 and 0.6 as well as 0.7 since 0.7.2.
++
+     .. _`Vadim Tropashko`: http://vadimtropashko.wordpress.com
+     .. _`Sql Design Patterns`:
+        http://www.rampant-books.com/book_2006_1_sql_coding_styles.htm
+     .. _`Trees in SQL: Nested Sets and Materialized Path (by Vadim Tropashko)`:
+-       http://www.dbazine.com/oracle/or-articles/tropashko4
++       https://communities.bmc.com/communities/docs/DOC-9902
+     .. _`sqlite`: http://sqlite.org
+     .. _`MySQL`: http://mysql.com
+     .. _`PostgreSQL`: http://postgresql.org
+@@ -69,7 +72,7 @@
+     'PathOverflowError', 'TooManyChildrenError', 'PathTooDeepError'
+ ]
+ 
+-__version__ = (0, 5, 2)
++__version__ = (0, 5, 2)
+ __doc__ %= {'version': '.'.join(map(str, __version__))}
+ 
+ 
+@@ -77,8 +80,17 @@
+ PATH_FIELD_LENGTH = 255
+ 
+ 
+-class PathOverflowError(Exception):
+-    "Base class for exceptions in calculations of node's path."
++if hasattr(sqlalchemy.exc, 'DontWrapMixin'):
++    # SQLAlchemy 0.7.2+ allows deriving from this special mixin in order to
++    # let exceptions raised from types methods during flush pass intact.
++    class PathOverflowError(Exception, sqlalchemy.exc.DontWrapMixin):
++        "Base class for exceptions in calculations of node's path."
++else:
++    # SQLAlchemy < 0.7 doesn't need any special base class.
++    class PathOverflowError(Exception):
++        "Base class for exceptions in calculations of node's path."
++    # 0.7 and 0.7.1 wrap exceptions and reraise
++    # sqlalchemy.exc.StatementError, so are not fully supported.
+ 
+ class TooManyChildrenError(PathOverflowError):
+     "Maximum children limit is exceeded. Raised during flush."
+@@ -197,16 +209,17 @@
+         ]
+         map(table.append_constraint, self.indices)
+ 
+-    def order_by_clause(self):
++    def query(self, entities, session):
+         """
+-        Get an object applicable for usage as an argument for
+-        `Query.order_by()`. Used to sort subtree query
+-        by `tree_id` and `path`.
++        Create and return `sqlalchemy.org.Query` object, passing arguments
++        to its constructor.
++
++        The query object is returned being ordered by `tree_id_field`
++        and `path_field`.
+         """
+-        return sqlalchemy.sql.expression.ClauseList(
+-            self.tree_id_field,
+-            self.path_field
+-        ).compile()
++        return sqlalchemy.orm.Query(entities, session) \
++                             .order_by(None) \
++                             .order_by(self.tree_id_field, self.path_field)
+ 
+ 
+ class _InsertionsParamsSelector(object):
+@@ -363,11 +376,17 @@
+ 
+ class MPClassManager(object):
+     """
+-    Node class manager. No need to create it by hand: it created
++    Node class manager. No need to create it by hand: it's created
+     by :class:`MPManager`.
+ 
+-    :param node_class: class which was mapped to tree table.
+-    :param opts: instance of :class:`MPOptions`
++    :param node_class: class which was mapped to the tree table.
++    :param opts: instance of :class:`MPOptions`.
++
++    .. versionchanged::
++        0.6
++        Previously existing method `__clause_element__` which used to allow
++        using the instances of :class:`MPClassManager` as arguments to methods
++        `query.order_by()` was removed in 0.6. Use :meth:`query` instead.
+     """
+     def __init__(self, node_class, opts):
+         self._mp_opts = opts
+@@ -382,23 +401,6 @@
+         "The maximum level of nesting in this tree, readonly."
+         return self._mp_opts.max_depth
+ 
+-    def __clause_element__(self):
+-        """
+-        Allows to use instances of `MPClassManager` directly
+-        as argument for `sqlalchemy.orm.Query.order_by()`.
+-        Sort query by `tree_id` and `path` fields. Can be
+-        used like this (assume that :class:`MPManager` is
+-        attached to class `Node` and named `'mp'`)::
+-
+-            query = session.query(Node).filter(root.filter_children())
+-            query.order_by(Node.mp)
+-
+-        .. note:: There is no need to sort queries returned by
+-            :class:`MPInstanceManager`'s `query_*()` methods this way
+-            as they returned already sorted.
+-        """
+-        return self._mp_opts.order_by_clause()
+-
+     def rebuild_subtree(self, root_node_id, order_by=None):
+         """
+         Reset paths for all nodes in subtree defined by `root_node_id`
+@@ -488,7 +490,7 @@
+         for index in opts.indices:
+             index.create()
+ 
+-    def query_all_trees(self, session):
++    def query(self, session):
+         """
+         Query all stored trees.
+ 
+@@ -496,10 +498,14 @@
+         :returns:
+             `Query` object with all nodes of all trees sorted as usual
+             by `(tree_id, path)`.
++
++        .. versionchanged::
++            0.6
++            Before 0.6 this method was called ``query_all_trees``. The old
++            name still works for backward compatibility.
+         """
+-        query = sqlalchemy.orm.Query(self.node_class, session=session) \
+-                              .order_by(self)
+-        return query
++        return self._mp_opts.query(self.node_class, session)
++    query_all_trees = query
+ 
+ 
+ class MPInstanceManager(object):
+@@ -510,7 +516,7 @@
+     node: descendants, ancestors, etc.
+ 
+     :param opts:
+-        instance of `MPOptions`.
++        instance of :class:`MPOptions`.
+     :param root_node_class:
+         the root class in the node class' polymorphic inheritance hierarchy.
+         This class will be used to perform queries.
+@@ -546,7 +552,7 @@
+             # use node's session only if particular session
+             # was not specified
+             session = obj_session
+-        return sqlalchemy.orm.Query(self._root_node_class, session=session)
++        return self._mp_opts.query(self._root_node_class, session=session)
+ 
+     def _get_session_and_assert_flushed(self, obj):
+         """
+@@ -594,12 +600,11 @@
+ 
+         Usage example::
+ 
+-            session.query(Node).filter(root.mp.filter_descendants()) \\
+-                               .order_by(Node.mp)
++            Node.mp.query(session).filter(root.mp.filter_descendants())
+ 
+         This example is silly and only shows an approach of using
+-        `filter_descendants`, dont use it for such purpose as there is a
+-        better way for such simple queries: :meth:`query_descendants`.
++        `filter_descendants`. Don't use it for such purpose as there is
++        a better way for such simple queries: :meth:`query_descendants`.
+ 
+         :param and_self:
+             `bool`, if set to `True` self node will be selected by filter.
+@@ -649,10 +654,8 @@
+             a `sqlalchemy.orm.Query` object which contains only node's
+             descendants and is ordered by `path`.
+         """
+-        query = self._get_query(self._get_obj(), session) \
+-                    .filter(self.filter_descendants(and_self=and_self)) \
+-                    .order_by(self._mp_opts.order_by_clause())
+-        return query
++        return self._get_query(self._get_obj(), session) \
++                   .filter(self.filter_descendants(and_self=and_self))
+ 
+     def filter_children(self):
+         """
+@@ -675,10 +678,8 @@
+         The same as :meth:`query_descendants` but queries children nodes and
+         does not accepts :attr:`and_self` parameter.
+         """
+-        query = self._get_query(self._get_obj(), session) \
+-                    .filter(self.filter_children()) \
+-                    .order_by(self._mp_opts.order_by_clause())
+-        return query
++        return self._get_query(self._get_obj(), session) \
++                   .filter(self.filter_children())
+ 
+     def filter_ancestors(self, and_self=False):
+         "The same as :meth:`filter_descendants` but filters ancestor nodes."
+@@ -701,10 +702,9 @@
+ 
+     def query_ancestors(self, session=None, and_self=False):
+         "The same as :meth:`query_descendants` but queries node's ancestors."
+-        query = self._get_query(self._get_obj(), session) \
+-                    .filter(self.filter_ancestors(and_self=and_self)) \
+-                    .order_by(self._mp_opts.depth_field)
+-        return query
++        return self._get_query(self._get_obj(), session) \
++                   .filter(self.filter_ancestors(and_self=and_self)) \
++                   .order_by(None).order_by(self._mp_opts.depth_field)
+ 
+     def filter_parent(self):
+         "Get a filter condition for a node's parent."
+@@ -750,7 +750,7 @@
+         onclause as parent id field.
+ 
+     :param path_field='mp_path':
+-        the hame for the path field or the field object itself. The field
++        the name for the path field or the field object itself. The field
+         will be created if the actual parameter value is a string and
+         there is no such column in the table `table`. If value provided
+         is an object column some sanity checks will be performed with
+@@ -799,7 +799,7 @@
+         which is intended to be used as mapper extension.
+ 
+         The second scenario is access to :class:`MPManager` via mapped
+-        class. The corresponding :class:`MPInstanceManager`'s instance
++        class. The corresponding :class:`MPClassManager` instance
+         is returned.
+ 
+         .. note:: If the nodes of your tree use polymorphic inheritance
+@@ -924,7 +924,7 @@
+     (it is zero-length tuple for leaf nodes), else it is a generator object.
+ 
+     :param flat_tree: plain sequence of tree nodes.
+-    :param class_manager: instance of :class:`MPClassManager`
++    :param class_manager: instance of :class:`MPClassManager`.
+ 
+     Can be used when it is simpler to process tree structure recursively.
+     Simple usage example::
+@@ -943,9 +943,13 @@
+             sqlamp.tree_recursive_iterator(query, Node.mp)
+         )
+ 
+-    If `flat_tree` is a `sqlalchemy.orm.Query` instance, it will be ordered
+-    by `class_manager`. If it is plain list, do not forget that such ordering
+-    is strictly required for `tree_recursive_iterator()` to work right.
++    .. versionchanged::
++        0.6
++        Before this function was sorting `flat_tree` if it was a query-object.
++        Since 0.6 it doesn't do it, so make sure that `flat_tree` is properly
++        sorted. The best way to achieve this is using queries returned from
++        public API methods of :class:`MPClassManager` and
++        :class:`MPInstanceManager`.
+ 
+     .. warning:: Process `flat_tree` items once and sequentially so works
+       right only if used in depth-first recursive consumer.
+@@ -956,8 +960,6 @@
+     def is_child(parent, child):
+         return tree_id(parent) == tree_id(child) \
+                 and depth(child) == depth(parent) + 1
+-    if isinstance(flat_tree, sqlalchemy.orm.Query):
+-        flat_tree = flat_tree.order_by(class_manager)
+     return _recursive_iterator(flat_tree, is_child)
+ 
+ 
+@@ -986,7 +988,7 @@
+             return
+         mp_manager_name = cls.__mp_manager__
+ 
+-        # preventing the property to be inherited
++        # preventing the property from being inherited
+         del cls.__mp_manager__
+ 
+         opts = {}
+@@ -1014,11 +1016,20 @@
+                 column = sqlalchemy.Column(ftype(), nullable=False)
+                 # SQLAlchemy 0.5.x needs this:
+                 dct[opts[field]] = column
+-                # and SQLAlchemy 0.6/.x needs this:
++                # and SQLAlchemy 0.6.x needs this:
+                 setattr(cls, opts[field], column)
+         super(DeclarativeMeta, cls).__init__(name, bases, dct)
+         mp_manager = MPManager(cls.__table__, **opts)
+         setattr(cls, mp_manager_name, mp_manager)
+-        mp_class_manager = getattr(cls, mp_manager_name)
+-        cls.__mapper__.extension.append(mp_manager.mapper_extension)
++        mapper_ext = mp_manager.mapper_extension
++        if hasattr(cls.__mapper__, 'extension'):
++            # SQLAlchemy < 0.7
++            cls.__mapper__.extension.append(mapper_ext)
++        else:
++            # SQLAlchemy 0.7+
++            from sqlalchemy import event
++            event.listen(cls.__mapper__, 'before_insert',
++                         mapper_ext.before_insert, propagate=True)
++            event.listen(cls.__mapper__, 'after_insert',
++                         mapper_ext.after_insert, propagate=True)
+ 
+diff -r 964320ab0ff5 -r 26f59cbfac52 tests/functional-tests.py
+--- a/tests/functional-tests.py	Tue Feb 15 21:57:02 2011 +0100
++++ b/tests/functional-tests.py	Wed Jun 22 23:15:27 2011 +0200
+@@ -165,19 +165,19 @@
+     def test_descendants(self):
+         self._fill_tree()
+         child212 = self.sess.query(Cls).filter_by(name='child212').one()
+-        descendants = self.sess.query(Cls).filter(
++        descendants = Cls.mp.query(self.sess).filter(
+             child212.mp.filter_descendants(and_self=False)
+-        ).order_by(Cls.mp).all()
++        ).all()
+         self.assertEqual(descendants, child212.mp.query_descendants().all())
+-        should_be = self.sess.query(Cls).filter(
++        should_be = Cls.mp.query(self.sess).filter(
+             tbl.c.name.in_(
+                 ("child2121", "child2122", "child21221", "child21222")
+             )
+-        ).order_by(Cls.mp).all()
++        ).all()
+         self.assertEqual(descendants, should_be)
+-        descendants_and_self = self.sess.query(Cls).filter(
++        descendants_and_self = Cls.mp.query(self.sess).filter(
+             child212.mp.filter_descendants(and_self=True)
+-        ).order_by(Cls.mp).all()
++        ).all()
+         self.assertEqual(
+             descendants_and_self,
+             child212.mp.query_descendants(and_self=True).all()
+@@ -187,12 +187,12 @@
+     def test_children(self):
+         self._fill_tree()
+         root2 = self.sess.query(Cls).filter_by(name='root2').one()
+-        children = self.sess.query(Cls).filter(
++        children = Cls.mp.query(self.sess).filter(
+             root2.mp.filter_children()
+-        ).order_by(Cls.mp).all()
+-        should_be = self.sess.query(Cls).filter(
++        ).all()
++        should_be = Cls.mp.query(self.sess).filter(
+             tbl.c.name.in_(("child21", "child22", "child23"))
+-        ).order_by(Cls.mp).all()
++        ).all()
+         self.assertEqual(children, should_be)
+         self.assertEqual(children, root2.mp.query_children().all())
+ 
+@@ -210,18 +210,18 @@
+ 
+     def test_ancestors(self):
+         self._fill_tree()
+-        child2122 = self.sess.query(Cls).filter_by(name='child2122').one()
+-        ancestors = self.sess.query(Cls).filter(
++        child2122 = Cls.mp.query(self.sess).filter_by(name='child2122').one()
++        ancestors = Cls.mp.query(self.sess).filter(
+             child2122.mp.filter_ancestors()
+-        ).order_by(Cls.mp).all()
+-        should_be = self.sess.query(Cls).filter(
++        ).all()
++        should_be = Cls.mp.query(self.sess).filter(
+             tbl.c.name.in_(("child212", "child21", "child2", "root2"))
+-        ).order_by(Cls.mp).all()
++        ).all()
+         self.assertEqual(ancestors, should_be)
+         self.assertEqual(ancestors, child2122.mp.query_ancestors().all())
+-        ancestors_and_self = self.sess.query(Cls).filter(
++        ancestors_and_self = Cls.mp.query(self.sess).filter(
+             child2122.mp.filter_ancestors(and_self=True)
+-        ).order_by(Cls.mp).all()
++        ).all()
+         self.assertEqual(ancestors_and_self, should_be + [child2122])
+         self.assertEqual(
+             ancestors_and_self,


More information about the scm-commits mailing list