[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