From: Mike Bayer Date: Sun, 5 Jan 2014 19:11:12 +0000 (-0500) Subject: - conjunctions like and_() and or_() can now accept generators as arguments. X-Git-Tag: rel_0_9_1~6^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=196f7ee6cc132aa0f31741af80fa5c0ba77efcf2;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - conjunctions like and_() and or_() can now accept generators as arguments. --- diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst index 2dbb9702da..97da9e20c2 100644 --- a/doc/build/changelog/changelog_09.rst +++ b/doc/build/changelog/changelog_09.rst @@ -14,6 +14,17 @@ .. changelog:: :version: 0.9.1 + .. change:: + :tags: feature, core + + Conjunctions like :func:`.and_` and :func:`.or_` can now accept + Python generators as a single argument, e.g.:: + + and_(x == y for x, y in tuples) + + The logic here looks for a single argument ``*args`` where the first + element is an instance of ``types.GeneratorType``. + .. change:: :tags: feature, core diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py index 56fca5dd8d..c230802ccd 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -1492,6 +1492,7 @@ class BooleanClauseList(ClauseList, ColumnElement): def _construct(cls, operator, continue_on, skip_on, *clauses, **kw): convert_clauses = [] + clauses = util.coerce_generator_arg(clauses) for clause in clauses: clause = _literal_as_text(clause) diff --git a/lib/sqlalchemy/util/__init__.py b/lib/sqlalchemy/util/__init__.py index 77339e56a8..fdf0c9dac9 100644 --- a/lib/sqlalchemy/util/__init__.py +++ b/lib/sqlalchemy/util/__init__.py @@ -18,7 +18,8 @@ from ._collections import KeyedTuple, ImmutableContainer, immutabledict, \ column_dict, ordered_column_set, populate_column_dict, unique_list, \ UniqueAppender, PopulateDict, EMPTY_SET, to_list, to_set, \ to_column_set, update_copy, flatten_iterator, \ - LRUCache, ScopedRegistry, ThreadLocalRegistry, WeakSequence + LRUCache, ScopedRegistry, ThreadLocalRegistry, WeakSequence, \ + coerce_generator_arg from .langhelpers import iterate_attributes, class_hierarchy, \ portable_instancemethod, unbound_method_to_callable, \ diff --git a/lib/sqlalchemy/util/_collections.py b/lib/sqlalchemy/util/_collections.py index a43115203a..24a3c1767c 100644 --- a/lib/sqlalchemy/util/_collections.py +++ b/lib/sqlalchemy/util/_collections.py @@ -6,10 +6,12 @@ """Collection classes and helpers.""" +from __future__ import absolute_import import weakref import operator from .compat import threading, itertools_filterfalse from . import py2k +import types EMPTY_SET = frozenset() @@ -754,6 +756,11 @@ class UniqueAppender(object): def __iter__(self): return iter(self.data) +def coerce_generator_arg(arg): + if len(arg) == 1 and isinstance(arg[0], types.GeneratorType): + return list(arg[0]) + else: + return arg def to_list(x, default=None): if x is None: diff --git a/test/sql/test_compiler.py b/test/sql/test_compiler.py index a5916c8256..53b9f68fc6 100644 --- a/test/sql/test_compiler.py +++ b/test/sql/test_compiler.py @@ -853,6 +853,17 @@ class SelectTest(fixtures.TestBase, AssertsCompiledSQL): 'otherid_1': 9, 'myid_1': 12} ) + # test a generator + self.assert_compile( + and_( + conj for conj in [ + table1.c.myid == 12, + table1.c.name == 'asdf' + ] + ), + "mytable.myid = :myid_1 AND mytable.name = :name_1" + ) + def test_nested_conjunctions_short_circuit(self): """test that empty or_(), and_() conjunctions are collapsed by an enclosing conjunction."""