From: Mike Bayer Date: Wed, 29 Sep 2010 06:11:38 +0000 (-0400) Subject: - CircularDependencyError now has .cycles and .edges X-Git-Tag: rel_0_6_5~37 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e9e221977bde7724d4ef3b553de4f982028eead8;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - CircularDependencyError now has .cycles and .edges members, which are the set of elements involved in one or more cycles, and the set of edges as 2-tuples. [ticket:1890] --- diff --git a/CHANGES b/CHANGES index 38fd7443bf..150cfbfa4f 100644 --- a/CHANGES +++ b/CHANGES @@ -162,6 +162,12 @@ CHANGES - the logging message emitted by the engine when a connection is first used is now "BEGIN (implicit)" to emphasize that DBAPI has no explicit begin(). + +- misc + - CircularDependencyError now has .cycles and .edges + members, which are the set of elements involved in + one or more cycles, and the set of edges as 2-tuples. + [ticket:1890] 0.6.4 ===== diff --git a/lib/sqlalchemy/exc.py b/lib/sqlalchemy/exc.py index 003969f56c..a9bcb7a7ef 100644 --- a/lib/sqlalchemy/exc.py +++ b/lib/sqlalchemy/exc.py @@ -26,7 +26,11 @@ class ArgumentError(SQLAlchemyError): class CircularDependencyError(SQLAlchemyError): """Raised by topological sorts when a circular dependency is detected""" - + def __init__(self, message, cycles, edges): + message += ": cycles: %r all edges: %r" % (cycles, edges) + super(CircularDependencyError, self).__init__(message) + self.cycles = cycles + self.edges = edges class CompileError(SQLAlchemyError): """Raised when an error occurs during SQL compilation""" diff --git a/lib/sqlalchemy/topological.py b/lib/sqlalchemy/topological.py index 6c3e90d981..9386f73354 100644 --- a/lib/sqlalchemy/topological.py +++ b/lib/sqlalchemy/topological.py @@ -8,6 +8,7 @@ from sqlalchemy.exc import CircularDependencyError from sqlalchemy import util +import itertools __all__ = ['sort', 'sort_as_subsets', 'find_cycles'] @@ -27,8 +28,10 @@ def sort_as_subsets(tuples, allitems): if not output: raise CircularDependencyError( - "Circular dependency detected: cycles: %r all edges: %s" % - (find_cycles(tuples, allitems), _dump_edges(edges, True))) + "Circular dependency detected", + find_cycles(tuples, allitems), + _gen_edges(edges) + ) todo.difference_update(output) yield output @@ -72,14 +75,11 @@ def find_cycles(tuples, allitems): node = stack.pop() return output -def _dump_edges(edges, reverse): - l = [] - for left in edges: - for right in edges[left]: - if reverse: - l.append((right, left)) - else: - l.append((left, right)) - return repr(l) - - +def _gen_edges(edges): + return set(itertools.chain( + *[ + [ + (right, left) for right in edges[left] + ] for left in edges + ] + )) diff --git a/test/base/test_dependency.py b/test/base/test_dependency.py index aa4410576b..9fddfc47ff 100644 --- a/test/base/test_dependency.py +++ b/test/base/test_dependency.py @@ -84,10 +84,16 @@ class DependencySortTest(TestBase): (node4, node1), ] allitems = self._nodes_from_tuples(tuples) - assert_raises(exc.CircularDependencyError, list, - topological.sort(tuples, allitems)) - # TODO: test find_cycles + try: + list(topological.sort(tuples, allitems)) + assert False + except exc.CircularDependencyError, err: + eq_(err.cycles, set(['node1', 'node3', 'node2', 'node5', + 'node4'])) + eq_(err.edges, set([('node3', 'node1'), ('node4', 'node1'), + ('node2', 'node3'), ('node1', 'node2'), + ('node4','node5'), ('node5', 'node4')])) def test_raise_on_cycle_two(self): @@ -101,10 +107,15 @@ class DependencySortTest(TestBase): tuples = [(node1, node2), (node3, node1), (node2, node4), (node3, node2), (node2, node3)] allitems = self._nodes_from_tuples(tuples) - assert_raises(exc.CircularDependencyError, list, - topological.sort(tuples, allitems)) - # TODO: test find_cycles + try: + list(topological.sort(tuples, allitems)) + assert False + except exc.CircularDependencyError, err: + eq_(err.cycles, set(['node1', 'node3', 'node2'])) + eq_(err.edges, set([('node3', 'node1'), ('node2', 'node3'), + ('node3', 'node2'), ('node1', 'node2'), + ('node2','node4')])) def test_raise_on_cycle_three(self): question, issue, providerservice, answer, provider = \