<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>trunk watcher</title>
  <id>tag:all.ware.appspot.com,2009-12-08:/</id>
  <subtitle>I watch trunk of beautiful codes.</subtitle>
  <link href="http://ware.appspot.com/" />
  <link href="http://ware.appspot.com/feed" rel="self" />
  <updated>2009-12-08T23:18:31Z</updated>
  <author>
    <name>Kosei Kitahara</name>
  </author>
  <rights>Copyright Kosei Kitahara.</rights>

  <entry xmlns="http://www.w3.org/2005/Atom"
      xmlns:thr="http://purl.org/syndication/thread/1.0">
    <link href="http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYzM0FDA"/>
    <id>http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYzM0FDA</id>
    <title>SQLAlchemy Changeset [6548]: merge r6545 of trunk for 1374</title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
        <p class="meta">
          <a href="http://www.sqlalchemy.org/trac/changeset/6548">6548</a><br />
            at 2009-12-08 23:18:31<br />
            by mike_mp@…<br />
        </p>

<pre class="data">
Index: /sqlalchemy/branches/rel_0_5/doc/build/sqlexpression.rst
===================================================================
--- /sqlalchemy/branches/rel_0_5/doc/build/sqlexpression.rst (revision 6526)
+++ /sqlalchemy/branches/rel_0_5/doc/build/sqlexpression.rst (revision 6548)
@@ -500,9 +500,9 @@
     {stop}[(u&#39;Wendy Williams, wendy@aol.com&#39;,)]
 
-To gain a &quot;hybrid&quot; approach, any of SA&#39;s SQL constructs can have text freely intermingled wherever you like - the ``text()`` construct can be placed within any other ``ClauseElement`` construct, and when used in a non-operator context, a direct string may be placed which converts to ``text()`` automatically.  Below we combine the usage of ``text()`` and strings with our constructed ``select()`` object, by using the ``select()`` object to structure the statement, and the ``text()``/strings to provide all the content within the structure.  For this example, SQLAlchemy is not given any ``Column`` or ``Table`` objects in any of its expressions, so it cannot generate a FROM clause.  So we also give it the ``from_obj`` keyword argument, which is a list of ``ClauseElements`` (or strings) to be placed within the FROM clause:
-
-.. sourcecode:: pycon+sql
-
-    &gt;&gt;&gt; s = select([text(&quot;users.fullname || &#39;, &#39; || addresses.email_address AS title&quot;)], 
+To gain a &quot;hybrid&quot; approach, the `select()` construct accepts strings for most of its arguments.  Below we combine the usage of strings with our constructed ``select()`` object, by using the ``select()`` object to structure the statement, and strings to provide all the content within the structure.  For this example, SQLAlchemy is not given any ``Column`` or ``Table`` objects in any of its expressions, so it cannot generate a FROM clause.  So we also give it the ``from_obj`` keyword argument, which is a list of ``ClauseElements`` (or strings) to be placed within the FROM clause:
+
+.. sourcecode:: pycon+sql
+
+    &gt;&gt;&gt; s = select([&quot;users.fullname || &#39;, &#39; || addresses.email_address AS title&quot;], 
     ...        and_( 
     ...            &quot;users.id = addresses.user_id&quot;, 
@@ -523,5 +523,4 @@
 Using Aliases 
 ==============
-
 
 The alias corresponds to a &quot;renamed&quot; version of a table or arbitrary relation, which occurs anytime you say &quot;SELECT  .. FROM sometable AS someothername&quot;.  The ``AS`` creates a new name for the table.  Aliases are super important in SQL as they allow you to reference the same table more than once.  Scenarios where you need to do this include when you self-join a table to itself, or more commonly when you need to join from a parent table to a child table multiple times.  For example, we know that our user ``jack`` has two email addresses.  How can we locate jack based on the combination of those two addresses?  We need to join twice to it.  Let&#39;s construct two distinct aliases for the ``addresses`` table and join:
</pre>

      </div>
    </content>
    <author>
      <name>mike_mp@…</name>
    </author>
    <updated>2009-12-08T23:18:31Z</updated>
  </entry>

  <entry xmlns="http://www.w3.org/2005/Atom"
      xmlns:thr="http://purl.org/syndication/thread/1.0">
    <link href="http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYsdUFDA"/>
    <id>http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYsdUFDA</id>
    <title>SQLAlchemy Changeset [6547]: don&#39;t advocate for text() inside of select(), plain strings are  …</title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
        <p class="meta">
          <a href="http://www.sqlalchemy.org/trac/changeset/6547">6547</a><br />
            at 2009-12-08 23:17:14<br />
            by mike_mp@…<br />
        </p>

<pre class="data">
Index: /sqlalchemy/trunk/doc/build/sqlexpression.rst
===================================================================
--- /sqlalchemy/trunk/doc/build/sqlexpression.rst (revision 6527)
+++ /sqlalchemy/trunk/doc/build/sqlexpression.rst (revision 6547)
@@ -501,9 +501,9 @@
     {stop}[(u&#39;Wendy Williams, wendy@aol.com&#39;,)]
 
-To gain a &quot;hybrid&quot; approach, any of SA&#39;s SQL constructs can have text freely intermingled wherever you like - the ``text()`` construct can be placed within any other ``ClauseElement`` construct, and when used in a non-operator context, a direct string may be placed which converts to ``text()`` automatically.  Below we combine the usage of ``text()`` and strings with our constructed ``select()`` object, by using the ``select()`` object to structure the statement, and the ``text()``/strings to provide all the content within the structure.  For this example, SQLAlchemy is not given any ``Column`` or ``Table`` objects in any of its expressions, so it cannot generate a FROM clause.  So we also give it the ``from_obj`` keyword argument, which is a list of ``ClauseElements`` (or strings) to be placed within the FROM clause:
-
-.. sourcecode:: pycon+sql
-
-    &gt;&gt;&gt; s = select([text(&quot;users.fullname || &#39;, &#39; || addresses.email_address AS title&quot;)], 
+To gain a &quot;hybrid&quot; approach, the `select()` construct accepts strings for most of its arguments.  Below we combine the usage of strings with our constructed ``select()`` object, by using the ``select()`` object to structure the statement, and strings to provide all the content within the structure.  For this example, SQLAlchemy is not given any ``Column`` or ``Table`` objects in any of its expressions, so it cannot generate a FROM clause.  So we also give it the ``from_obj`` keyword argument, which is a list of ``ClauseElements`` (or strings) to be placed within the FROM clause:
+
+.. sourcecode:: pycon+sql
+
+    &gt;&gt;&gt; s = select([&quot;users.fullname || &#39;, &#39; || addresses.email_address AS title&quot;], 
     ...        and_( 
     ...            &quot;users.id = addresses.user_id&quot;, 
@@ -524,5 +524,4 @@
 Using Aliases 
 ==============
-
 
 The alias corresponds to a &quot;renamed&quot; version of a table or arbitrary relation, which occurs anytime you say &quot;SELECT  .. FROM sometable AS someothername&quot;.  The ``AS`` creates a new name for the table.  Aliases are super important in SQL as they allow you to reference the same table more than once.  Scenarios where you need to do this include when you self-join a table to itself, or more commonly when you need to join from a parent table to a child table multiple times.  For example, we know that our user ``jack`` has two email addresses.  How can we locate jack based on the combination of those two addresses?  We need to join twice to it.  Let&#39;s construct two distinct aliases for the ``addresses`` table and join:
</pre>

      </div>
    </content>
    <author>
      <name>mike_mp@…</name>
    </author>
    <updated>2009-12-08T23:17:14Z</updated>
  </entry>

  <entry xmlns="http://www.w3.org/2005/Atom"
      xmlns:thr="http://purl.org/syndication/thread/1.0">
    <link href="http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYsNUFDA"/>
    <id>http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYsNUFDA</id>
    <title>SQLAlchemy Changeset [6546]: - The signature of the proxy_factory callable passed to  association_proxy  …</title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
        <p class="meta">
          <a href="http://www.sqlalchemy.org/trac/changeset/6546">6546</a><br />
            at 2009-12-08 23:09:48<br />
            by mike_mp@…<br />
        </p>

<pre class="data">
Index: /sqlalchemy/trunk/test/ext/test_associationproxy.py
===================================================================
--- /sqlalchemy/trunk/test/ext/test_associationproxy.py (revision 6251)
+++ /sqlalchemy/trunk/test/ext/test_associationproxy.py (revision 6546)
@@ -7,4 +7,5 @@
 from sqlalchemy.orm.collections import collection
 from sqlalchemy.ext.associationproxy import *
+from sqlalchemy.ext.associationproxy import _AssociationList
 from sqlalchemy.test import *
 from sqlalchemy.test.util import gc_collect
@@ -616,4 +617,54 @@
             pass
 
+class ProxyFactoryTest(ListTest):
+    def setup(self):
+        metadata = MetaData(testing.db)
+
+        parents_table = Table(&#39;Parent&#39;, metadata,
+                              Column(&#39;id&#39;, Integer, primary_key=True,
+                                     test_needs_autoincrement=True),
+                              Column(&#39;name&#39;, String(128)))
+        children_table = Table(&#39;Children&#39;, metadata,
+                               Column(&#39;id&#39;, Integer, primary_key=True,
+                                      test_needs_autoincrement=True),
+                               Column(&#39;parent_id&#39;, Integer,
+                                      ForeignKey(&#39;Parent.id&#39;)),
+                               Column(&#39;foo&#39;, String(128)),
+                               Column(&#39;name&#39;, String(128)))
+        
+        class CustomProxy(_AssociationList):
+            def __init__(self, lazy_collection, creator, value_attr, parent):
+                getter, setter = parent._default_getset(lazy_collection)
+                _AssociationList.__init__(self, lazy_collection, creator, getter, setter, parent)
+                
+        
+        class Parent(object):
+            children = association_proxy(&#39;_children&#39;, &#39;name&#39;, 
+                        proxy_factory=CustomProxy, 
+                        proxy_bulk_set=CustomProxy.extend
+                    )
+
+            def __init__(self, name):
+                self.name = name
+
+        class Child(object):
+            def __init__(self, name):
+                self.name = name
+
+        mapper(Parent, parents_table, properties={
+            &#39;_children&#39;: relation(Child, lazy=False,
+                                  collection_class=list)})
+        mapper(Child, children_table)
+
+        metadata.create_all()
+
+        self.metadata = metadata
+        self.session = create_session()
+        self.Parent, self.Child = Parent, Child
+    
+    def test_sequence_ops(self):
+        self._test_sequence_ops()
+    
+    
 class ScalarTest(TestBase):
     def test_scalar_proxy(self):
Index: /sqlalchemy/trunk/lib/sqlalchemy/ext/associationproxy.py
===================================================================
--- /sqlalchemy/trunk/lib/sqlalchemy/ext/associationproxy.py (revision 6189)
+++ /sqlalchemy/trunk/lib/sqlalchemy/ext/associationproxy.py (revision 6546)
@@ -221,5 +221,5 @@
 
         if self.proxy_factory:
-            return self.proxy_factory(lazy_collection, creator, self.value_attr)
+            return self.proxy_factory(lazy_collection, creator, self.value_attr, self)
 
         if self.getset_factory:
Index: /sqlalchemy/trunk/CHANGES
===================================================================
--- /sqlalchemy/trunk/CHANGES (revision 6544)
+++ /sqlalchemy/trunk/CHANGES (revision 6546)
@@ -746,4 +746,11 @@
       in terms of the SqlSoup object&#39;s bind.
     
+    - The signature of the proxy_factory callable passed to 
+      association_proxy is now (lazy_collection, creator, 
+      value_attr, association_proxy), adding a fourth argument
+      that is the parent AssociationProxy argument.  Allows
+      serializability and subclassing of the built in collections.
+      [ticket:1259]
+      
 0.5.7
 =====
</pre>

      </div>
    </content>
    <author>
      <name>mike_mp@…</name>
    </author>
    <updated>2009-12-08T23:09:48Z</updated>
  </entry>

  <entry xmlns="http://www.w3.org/2005/Atom"
      xmlns:thr="http://purl.org/syndication/thread/1.0">
    <link href="http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYy80FDA"/>
    <id>http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYy80FDA</id>
    <title>TurboGears Changeset [6974]: The last fix for #1314 did not allow sets as values for  …</title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
        <p class="meta">
          <a href="http://trac.turbogears.org/changeset/6974">6974</a><br />
            at 2009-12-08 15:42:10<br />
            by cito@online.de<br />
        </p>

<pre class="data">
Index: /branches/1.1/turbogears/widgets/tests/test_widgets.py
===================================================================
--- /branches/1.1/turbogears/widgets/tests/test_widgets.py (revision 6973)
+++ /branches/1.1/turbogears/widgets/tests/test_widgets.py (revision 6974)
@@ -387,4 +387,7 @@
                 return False
 
+        def __hash__(self):
+            return hash(self.value)
+
     class TestValidator:
 
@@ -411,17 +414,11 @@
         &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
         &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
+    assert single.render(None, format=&#39;xhtml&#39;) == output
+    assert single.render(&#39;&#39;, format=&#39;xhtml&#39;) == output
     output = single.render(TestValue(3), format=&#39;xhtml&#39;)
     assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
         &#39;&lt;option selected=&quot;selected&quot; value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
         &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
-    # check with unvalidated values
-    output = single.render(&#39;&#39;, format=&#39;xhtml&#39;)
-    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
-    output = single.render(&#39;3&#39;, format=&#39;xhtml&#39;)
-    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
-        &#39;&lt;option selected=&quot;selected&quot; value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
+    assert single.render(u&#39;3&#39;, format=&#39;xhtml&#39;) == output
     # Test MultipleSelectField
     multiple = widgets.MultipleSelectField(
@@ -434,4 +431,8 @@
         &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
         &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
+    assert multiple.render(None, format=&#39;xhtml&#39;) == output
+    assert multiple.render(&#39;&#39;, format=&#39;xhtml&#39;) == output
+    assert multiple.render([], format=&#39;xhtml&#39;) == output
+    assert multiple.render(set(), format=&#39;xhtml&#39;) == output
     output = multiple.render([TestValue(1), TestValue(3)], format=&#39;xhtml&#39;)
     assert (&#39;&lt;option selected=&quot;selected&quot; value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
@@ -439,15 +440,7 @@
         &#39;&lt;option selected=&quot;selected&quot; value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
         &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
-    # check with unvalidated values
-    output = multiple.render(&#39;&#39;, format=&#39;xhtml&#39;)
-    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
-    output = multiple.render([&#39;1&#39;, &#39;3&#39;], format=&#39;xhtml&#39;)
-    assert (&#39;&lt;option selected=&quot;selected&quot; value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
-        &#39;&lt;option selected=&quot;selected&quot; value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
+    assert multiple.render(set([TestValue(1), TestValue(3)]), format=&#39;xhtml&#39;) == output
+    assert multiple.render([u&#39;1&#39;, u&#39;3&#39;], format=&#39;xhtml&#39;) == output
+    assert multiple.render(set([u&#39;1&#39;, u&#39;3&#39;]), format=&#39;xhtml&#39;) == output
 
 
Index: /branches/1.1/turbogears/widgets/forms.py
===================================================================
--- /branches/1.1/turbogears/widgets/forms.py (revision 6973)
+++ /branches/1.1/turbogears/widgets/forms.py (revision 6974)
@@ -992,9 +992,13 @@
         if self._multiple_selection:
             if value:
-                if isinstance(value[0], basestring):
-                    try:
-                        value = self.validator.to_python(value)
-                    except validators.Invalid:
-                        return False
+                # check only one item in the given list or set
+                for v in value:
+                    if isinstance(v, basestring):
+                        # if it&#39;s  a string, we need to convert it
+                        try:
+                            value = self.validator.to_python(value)
+                        except validators.Invalid:
+                            return False
+                    break
                 if option_value is None:
                     for v in value:
@@ -1005,4 +1009,5 @@
             return False
         else:
+            # if the value is a string, we need to convert it
             if isinstance(value, basestring):
                 try:
Index: /branches/1.5-genshi-widgets/turbogears/widgets/tests/test_widgets.py
===================================================================
--- /branches/1.5-genshi-widgets/turbogears/widgets/tests/test_widgets.py (revision 6973)
+++ /branches/1.5-genshi-widgets/turbogears/widgets/tests/test_widgets.py (revision 6974)
@@ -453,4 +453,7 @@
                 return False
 
+        def __hash__(self):
+            return hash(self.value)
+
     class TestValidator:
 
@@ -477,21 +480,13 @@
         &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
         &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
+    assert single.render(None, format=&#39;xhtml&#39;) == output
+    assert single.render(&#39;&#39;, format=&#39;xhtml&#39;) == output
     output = single.render(TestValue(3), format=&#39;xhtml&#39;)
-    output = output.replace(&#39;selected=&quot;selected&quot;&#39;, &#39;*&#39;)
-    output = output.replace(&#39;value=&quot;3&quot; *&#39;, &#39;* value=&quot;3&quot;&#39;)
+    selected = output.replace(&#39;selected=&quot;selected&quot;&#39;, &#39;*&#39;)
+    selected = selected.replace(&#39;value=&quot;3&quot; *&#39;, &#39;* value=&quot;3&quot;&#39;)
     assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
         &#39;&lt;option * value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
-    # check with unvalidated values
-    output = single.render(&#39;&#39;, format=&#39;xhtml&#39;)
-    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
-    output = single.render(&#39;3&#39;, format=&#39;xhtml&#39;)
-    output = output.replace(&#39;selected=&quot;selected&quot;&#39;, &#39;*&#39;)
-    output = output.replace(&#39;value=&quot;3&quot; *&#39;, &#39;* value=&quot;3&quot;&#39;)
-    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
-        &#39;&lt;option * value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in selected, output
+    assert single.render(u&#39;3&#39;, format=&#39;xhtml&#39;) == output
     # Test MultipleSelectField
     multiple = widgets.MultipleSelectField(
@@ -504,26 +499,20 @@
         &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
         &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
+    assert multiple.render(None, format=&#39;xhtml&#39;) == output
+    assert multiple.render(&#39;&#39;, format=&#39;xhtml&#39;) == output
+    assert multiple.render([], format=&#39;xhtml&#39;) == output
+    assert multiple.render(set(), format=&#39;xhtml&#39;) == output
     output = multiple.render([TestValue(1), TestValue(3)], format=&#39;xhtml&#39;)
-    output = output.replace(&#39;selected=&quot;selected&quot;&#39;, &#39;*&#39;)
-    output = output.replace(&#39;value=&quot;1&quot; *&#39;, &#39;* value=&quot;1&quot;&#39;)
-    output = output.replace(&#39;value=&quot;3&quot; *&#39;, &#39;* value=&quot;3&quot;&#39;)
+    selected = output.replace(&#39;selected=&quot;selected&quot;&#39;, &#39;*&#39;)
+    selected = selected.replace(&#39;value=&quot;1&quot; *&#39;, &#39;* value=&quot;1&quot;&#39;)
+    selected = selected.replace(&#39;value=&quot;3&quot; *&#39;, &#39;* value=&quot;3&quot;&#39;)
     assert (&#39;&lt;option * value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
         &#39;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
         &#39;&lt;option * value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
-    # check with unvalidated values
-    output = multiple.render(&#39;&#39;, format=&#39;xhtml&#39;)
-    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
-    output = multiple.render([&#39;1&#39;, &#39;3&#39;], format=&#39;xhtml&#39;)
-    output = output.replace(&#39;selected=&quot;selected&quot;&#39;, &#39;*&#39;)
-    output = output.replace(&#39;value=&quot;1&quot; *&#39;, &#39;* value=&quot;1&quot;&#39;)
-    output = output.replace(&#39;value=&quot;3&quot; *&#39;, &#39;* value=&quot;3&quot;&#39;)
-    assert (&#39;&lt;option * value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
-        &#39;&lt;option * value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in selected), output
+    assert multiple.render(
+        set([TestValue(1), TestValue(3)]), format=&#39;xhtml&#39;) == output
+    assert multiple.render([u&#39;1&#39;, u&#39;3&#39;], format=&#39;xhtml&#39;) == output
+    assert multiple.render(set([u&#39;1&#39;, u&#39;3&#39;]), format=&#39;xhtml&#39;) == output
 
 
Index: /branches/1.5-genshi-widgets/turbogears/widgets/forms.py
===================================================================
--- /branches/1.5-genshi-widgets/turbogears/widgets/forms.py (revision 6973)
+++ /branches/1.5-genshi-widgets/turbogears/widgets/forms.py (revision 6974)
@@ -1051,11 +1051,16 @@
 
     def _is_option_selected(self, option_value, value):
+        &quot;&quot;&quot;Check whether an option value has been selected.&quot;&quot;&quot;
         if self._multiple_selection:
             if value:
-                if isinstance(value[0], basestring):
-                    try:
-                        value = self.validator.to_python(value)
-                    except validators.Invalid:
-                        return False
+               # check only one item in the given list or set
+                for v in value:
+                    if isinstance(v, basestring):
+                        # if it&#39;s  a string, we need to convert it
+                        try:
+                            value = self.validator.to_python(value)
+                        except validators.Invalid:
+                            return False
+                    break
                 if option_value is None:
                     for v in value:
@@ -1066,4 +1071,5 @@
             return False
         else:
+            # if the value is a string, we need to convert it
             if isinstance(value, basestring):
                 try:
Index: /branches/1.5/turbogears/widgets/tests/test_widgets.py
===================================================================
--- /branches/1.5/turbogears/widgets/tests/test_widgets.py (revision 6973)
+++ /branches/1.5/turbogears/widgets/tests/test_widgets.py (revision 6974)
@@ -407,4 +407,7 @@
                 return False
 
+        def __hash__(self):
+            return hash(self.value)
+
     class TestValidator:
 
@@ -431,17 +434,11 @@
         &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
         &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
+    assert single.render(None, format=&#39;xhtml&#39;) == output
+    assert single.render(&#39;&#39;, format=&#39;xhtml&#39;) == output
     output = single.render(TestValue(3), format=&#39;xhtml&#39;)
     assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
         &#39;&lt;option selected=&quot;selected&quot; value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
         &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
-    # check with unvalidated values
-    output = single.render(&#39;&#39;, format=&#39;xhtml&#39;)
-    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
-    output = single.render(&#39;3&#39;, format=&#39;xhtml&#39;)
-    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
-        &#39;&lt;option selected=&quot;selected&quot; value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
+    assert single.render(u&#39;3&#39;, format=&#39;xhtml&#39;) == output
     # Test MultipleSelectField
     multiple = widgets.MultipleSelectField(
@@ -454,4 +451,8 @@
         &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
         &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
+    assert multiple.render(None, format=&#39;xhtml&#39;) == output
+    assert multiple.render(&#39;&#39;, format=&#39;xhtml&#39;) == output
+    assert multiple.render([], format=&#39;xhtml&#39;) == output
+    assert multiple.render(set(), format=&#39;xhtml&#39;) == output
     output = multiple.render([TestValue(1), TestValue(3)], format=&#39;xhtml&#39;)
     assert (&#39;&lt;option selected=&quot;selected&quot; value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
@@ -459,15 +460,7 @@
         &#39;&lt;option selected=&quot;selected&quot; value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
         &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
-    # check with unvalidated values
-    output = multiple.render(&#39;&#39;, format=&#39;xhtml&#39;)
-    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
-    output = multiple.render([&#39;1&#39;, &#39;3&#39;], format=&#39;xhtml&#39;)
-    assert (&#39;&lt;option selected=&quot;selected&quot; value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
-        &#39;&lt;option selected=&quot;selected&quot; value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
-        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
+    assert multiple.render(set([TestValue(1), TestValue(3)]), format=&#39;xhtml&#39;) == output
+    assert multiple.render([u&#39;1&#39;, u&#39;3&#39;], format=&#39;xhtml&#39;) == output
+    assert multiple.render(set([u&#39;1&#39;, u&#39;3&#39;]), format=&#39;xhtml&#39;) == output
 
 def test_empty_selection_field():
Index: /branches/1.5/turbogears/widgets/forms.py
===================================================================
--- /branches/1.5/turbogears/widgets/forms.py (revision 6973)
+++ /branches/1.5/turbogears/widgets/forms.py (revision 6974)
@@ -992,9 +992,13 @@
         if self._multiple_selection:
             if value:
-                if isinstance(value[0], basestring):
-                    try:
-                        value = self.validator.to_python(value)
-                    except validators.Invalid:
-                        return False
+                # check only one item in the given list or set
+                for v in value:
+                    if isinstance(v, basestring):
+                        # if it&#39;s  a string, we need to convert it
+                        try:
+                            value = self.validator.to_python(value)
+                        except validators.Invalid:
+                            return False
+                    break
                 if option_value is None:
                     for v in value:
@@ -1005,4 +1009,5 @@
             return False
         else:
+            # if the value is a string, we need to convert it
             if isinstance(value, basestring):
                 try:
</pre>

      </div>
    </content>
    <author>
      <name>cito@online.de</name>
    </author>
    <updated>2009-12-08T15:42:10Z</updated>
  </entry>

  <entry xmlns="http://www.w3.org/2005/Atom"
      xmlns:thr="http://purl.org/syndication/thread/1.0">
    <link href="http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYr9UFDA"/>
    <id>http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYr9UFDA</id>
    <title>TurboGears Changeset [6973]: The fix for #1314 did not account for the case where the values were not  …</title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
        <p class="meta">
          <a href="http://trac.turbogears.org/changeset/6973">6973</a><br />
            at 2009-12-08 12:52:26<br />
            by cito@online.de<br />
        </p>

<pre class="data">
Index: /branches/1.1/turbogears/widgets/tests/test_widgets.py
===================================================================
--- /branches/1.1/turbogears/widgets/tests/test_widgets.py (revision 6960)
+++ /branches/1.1/turbogears/widgets/tests/test_widgets.py (revision 6973)
@@ -407,5 +407,18 @@
             (TestValue(3), &#39;three&#39;), (TestValue(4), &#39;four&#39;)],
         validator=TestValidator())
+    output = single.render(format=&#39;xhtml&#39;)
+    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
     output = single.render(TestValue(3), format=&#39;xhtml&#39;)
+    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
+        &#39;&lt;option selected=&quot;selected&quot; value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
+    # check with unvalidated values
+    output = single.render(&#39;&#39;, format=&#39;xhtml&#39;)
+    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
+    output = single.render(&#39;3&#39;, format=&#39;xhtml&#39;)
     assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
         &#39;&lt;option selected=&quot;selected&quot; value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
@@ -416,4 +429,9 @@
             (TestValue(3), &#39;three&#39;), (TestValue(4), &#39;four&#39;)],
         validator=validators.ForEach(TestValidator()))
+    output = multiple.render(format=&#39;xhtml&#39;)
+    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
     output = multiple.render([TestValue(1), TestValue(3)], format=&#39;xhtml&#39;)
     assert (&#39;&lt;option selected=&quot;selected&quot; value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
@@ -421,4 +439,16 @@
         &#39;&lt;option selected=&quot;selected&quot; value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
         &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
+    # check with unvalidated values
+    output = multiple.render(&#39;&#39;, format=&#39;xhtml&#39;)
+    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
+    output = multiple.render([&#39;1&#39;, &#39;3&#39;], format=&#39;xhtml&#39;)
+    assert (&#39;&lt;option selected=&quot;selected&quot; value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
+        &#39;&lt;option selected=&quot;selected&quot; value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
+
 
 def test_empty_selection_field():
Index: /branches/1.1/turbogears/widgets/forms.py
===================================================================
--- /branches/1.1/turbogears/widgets/forms.py (revision 6968)
+++ /branches/1.1/turbogears/widgets/forms.py (revision 6973)
@@ -990,18 +990,28 @@
 
     def _is_option_selected(self, option_value, value):
-        if option_value is None:
-            if self._multiple_selection:
-                if value:
+        if self._multiple_selection:
+            if value:
+                if isinstance(value[0], basestring):
+                    try:
+                        value = self.validator.to_python(value)
+                    except validators.Invalid:
+                        return False
+                if option_value is None:
                     for v in value:
                         if v is None:
                             return True
-                return False
-            return value is None
-        else:
-            if self._multiple_selection:
-                if value:
+                else:
                     return option_value in value
-                return False
-            return option_value == value
+            return False
+        else:
+            if isinstance(value, basestring):
+                try:
+                    value = self.validator.to_python(value)
+                except validators.Invalid:
+                    return False
+            if option_value is None:
+                return value is None
+            else:
+                return option_value == value
 
 
Index: /branches/1.5-genshi-widgets/turbogears/widgets/tests/test_widgets.py
===================================================================
--- /branches/1.5-genshi-widgets/turbogears/widgets/tests/test_widgets.py (revision 6961)
+++ /branches/1.5-genshi-widgets/turbogears/widgets/tests/test_widgets.py (revision 6973)
@@ -473,5 +473,20 @@
             (TestValue(3), &#39;three&#39;), (TestValue(4), &#39;four&#39;)],
         validator=TestValidator())
+    output = single.render(format=&#39;xhtml&#39;)
+    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
     output = single.render(TestValue(3), format=&#39;xhtml&#39;)
+    output = output.replace(&#39;selected=&quot;selected&quot;&#39;, &#39;*&#39;)
+    output = output.replace(&#39;value=&quot;3&quot; *&#39;, &#39;* value=&quot;3&quot;&#39;)
+    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
+        &#39;&lt;option * value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
+    # check with unvalidated values
+    output = single.render(&#39;&#39;, format=&#39;xhtml&#39;)
+    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
+    output = single.render(&#39;3&#39;, format=&#39;xhtml&#39;)
     output = output.replace(&#39;selected=&quot;selected&quot;&#39;, &#39;*&#39;)
     output = output.replace(&#39;value=&quot;3&quot; *&#39;, &#39;* value=&quot;3&quot;&#39;)
@@ -484,5 +499,24 @@
             (TestValue(3), &#39;three&#39;), (TestValue(4), &#39;four&#39;)],
         validator=validators.ForEach(TestValidator()))
+    output = multiple.render(format=&#39;xhtml&#39;)
+    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
     output = multiple.render([TestValue(1), TestValue(3)], format=&#39;xhtml&#39;)
+    output = output.replace(&#39;selected=&quot;selected&quot;&#39;, &#39;*&#39;)
+    output = output.replace(&#39;value=&quot;1&quot; *&#39;, &#39;* value=&quot;1&quot;&#39;)
+    output = output.replace(&#39;value=&quot;3&quot; *&#39;, &#39;* value=&quot;3&quot;&#39;)
+    assert (&#39;&lt;option * value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
+        &#39;&lt;option * value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
+    # check with unvalidated values
+    output = multiple.render(&#39;&#39;, format=&#39;xhtml&#39;)
+    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
+    output = multiple.render([&#39;1&#39;, &#39;3&#39;], format=&#39;xhtml&#39;)
     output = output.replace(&#39;selected=&quot;selected&quot;&#39;, &#39;*&#39;)
     output = output.replace(&#39;value=&quot;1&quot; *&#39;, &#39;* value=&quot;1&quot;&#39;)
Index: /branches/1.5-genshi-widgets/turbogears/widgets/forms.py
===================================================================
--- /branches/1.5-genshi-widgets/turbogears/widgets/forms.py (revision 6972)
+++ /branches/1.5-genshi-widgets/turbogears/widgets/forms.py (revision 6973)
@@ -1051,18 +1051,28 @@
 
     def _is_option_selected(self, option_value, value):
-        if option_value is None:
-            if self._multiple_selection:
-                if value:
+        if self._multiple_selection:
+            if value:
+                if isinstance(value[0], basestring):
+                    try:
+                        value = self.validator.to_python(value)
+                    except validators.Invalid:
+                        return False
+                if option_value is None:
                     for v in value:
                         if v is None:
                             return True
-                return False
-            return value is None
-        else:
-            if self._multiple_selection:
-                if value:
+                else:
                     return option_value in value
-                return False
-            return option_value == value
+            return False
+        else:
+            if isinstance(value, basestring):
+                try:
+                    value = self.validator.to_python(value)
+                except validators.Invalid:
+                    return False
+            if option_value is None:
+                return value is None
+            else:
+                return option_value == value
 
 
Index: /branches/1.5/turbogears/widgets/tests/test_widgets.py
===================================================================
--- /branches/1.5/turbogears/widgets/tests/test_widgets.py (revision 6960)
+++ /branches/1.5/turbogears/widgets/tests/test_widgets.py (revision 6973)
@@ -427,5 +427,18 @@
             (TestValue(3), &#39;three&#39;), (TestValue(4), &#39;four&#39;)],
         validator=TestValidator())
+    output = single.render(format=&#39;xhtml&#39;)
+    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
     output = single.render(TestValue(3), format=&#39;xhtml&#39;)
+    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
+        &#39;&lt;option selected=&quot;selected&quot; value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
+    # check with unvalidated values
+    output = single.render(&#39;&#39;, format=&#39;xhtml&#39;)
+    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39;) in output, output
+    output = single.render(&#39;3&#39;, format=&#39;xhtml&#39;)
     assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
         &#39;&lt;option selected=&quot;selected&quot; value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
@@ -436,5 +449,21 @@
             (TestValue(3), &#39;three&#39;), (TestValue(4), &#39;four&#39;)],
         validator=validators.ForEach(TestValidator()))
+    output = multiple.render(format=&#39;xhtml&#39;)
+    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
     output = multiple.render([TestValue(1), TestValue(3)], format=&#39;xhtml&#39;)
+    assert (&#39;&lt;option selected=&quot;selected&quot; value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
+        &#39;&lt;option selected=&quot;selected&quot; value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
+    # check with unvalidated values
+    output = multiple.render(&#39;&#39;, format=&#39;xhtml&#39;)
+    assert (&#39;&lt;option value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;3&quot;&gt;three&lt;/option&gt;&#39;
+        &#39;&lt;option value=&quot;4&quot;&gt;four&lt;/option&gt;&#39; in output), output
+    output = multiple.render([&#39;1&#39;, &#39;3&#39;], format=&#39;xhtml&#39;)
     assert (&#39;&lt;option selected=&quot;selected&quot; value=&quot;1&quot;&gt;one&lt;/option&gt;&#39;
         &#39;&lt;option value=&quot;2&quot;&gt;two&lt;/option&gt;&#39;
Index: /branches/1.5/turbogears/widgets/forms.py
===================================================================
--- /branches/1.5/turbogears/widgets/forms.py (revision 6968)
+++ /branches/1.5/turbogears/widgets/forms.py (revision 6973)
@@ -990,18 +990,28 @@
 
     def _is_option_selected(self, option_value, value):
-        if option_value is None:
-            if self._multiple_selection:
-                if value:
+        if self._multiple_selection:
+            if value:
+                if isinstance(value[0], basestring):
+                    try:
+                        value = self.validator.to_python(value)
+                    except validators.Invalid:
+                        return False
+                if option_value is None:
                     for v in value:
                         if v is None:
                             return True
-                return False
-            return value is None
-        else:
-            if self._multiple_selection:
-                if value:
+                else:
                     return option_value in value
-                return False
-            return option_value == value
+            return False
+        else:
+            if isinstance(value, basestring):
+                try:
+                    value = self.validator.to_python(value)
+                except validators.Invalid:
+                    return False
+            if option_value is None:
+                return value is None
+            else:
+                return option_value == value
 
 
</pre>

      </div>
    </content>
    <author>
      <name>cito@online.de</name>
    </author>
    <updated>2009-12-08T12:52:26Z</updated>
  </entry>

  <entry xmlns="http://www.w3.org/2005/Atom"
      xmlns:thr="http://purl.org/syndication/thread/1.0">
    <link href="http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYys0FDA"/>
    <id>http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYys0FDA</id>
    <title>Twisted Changeset [27711]: Remove the tests that lack assertions.</title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
        <p class="meta">
          <a href="http://twistedmatrix.com/trac/changeset/27711">27711</a><br />
            at 2009-12-08 11:35:20<br />
            by jml@example.com (jml)<br />
        </p>

<pre class="data">
Index: /branches/subunit-4004/twisted/trial/test/test_reporter.py
===================================================================
--- /branches/subunit-4004/twisted/trial/test/test_reporter.py (revision 27710)
+++ /branches/subunit-4004/twisted/trial/test/test_reporter.py (revision 27711)
@@ -841,85 +841,4 @@
 
 
-    def test_startStop(self):
-        &quot;&quot;&quot;
-        Test start and stop.
-        &quot;&quot;&quot;
-        self.result.startTest(self.test)
-        self.result.stopTest(self.test)
-
-
-    def test_addUnexpectedSuccess(self):
-        &quot;&quot;&quot;
-        Test addUnexpectedSuccess.
-        &quot;&quot;&quot;
-        self.result.startTest(self.test)
-        self.result.addUnexpectedSuccess(self.test, &quot;todo&quot;)
-        self.result.stopTest(self.test)
-
-
-    def test_addSuccess(self):
-        &quot;&quot;&quot;
-        Test addSuccess.
-        &quot;&quot;&quot;
-        self.result.startTest(self.test)
-        self.result.addSuccess(self.test)
-        self.result.stopTest(self.test)
-
-
-    def test_addError(self):
-        &quot;&quot;&quot;
-        Test addError.
-        &quot;&quot;&quot;
-        self.result.startTest(self.test)
-        try:
-            raise Exception(&#39;woo&#39;)
-        except Exception:
-            self.result.addError(self.test, self._exc_info())
-        self.result.stopTest(self.test)
-
-
-    def test_addFailure(self):
-        &quot;&quot;&quot;
-        Test addFailure.
-        &quot;&quot;&quot;
-        self.result.startTest(self.test)
-        try:
-            raise Exception(&#39;woo&#39;)
-        except Exception:
-            self.result.addFailure(self.test, self._exc_info())
-        self.result.stopTest(self.test)
-
-
-    def test_addExpectedFailure(self):
-        &quot;&quot;&quot;
-        Test addExpectedFailure.
-        &quot;&quot;&quot;
-        self.result.startTest(self.test)
-        try:
-            raise Exception(&#39;woo&#39;)
-        except Exception:
-            self.result.addExpectedFailure(self.test, self._exc_info(),
-                Todo(&quot;&quot;))
-        self.result.stopTest(self.test)
-
-
-    def test_addUnexpectedSuccess_todo(self):
-        &quot;&quot;&quot;
-        Test addUnexpectedSuccess.
-        &quot;&quot;&quot;
-        self.result.startTest(self.test)
-        self.result.addUnexpectedSuccess(self.test, Todo(&quot;&quot;))
-        self.result.stopTest(self.test)
-
-
-    def test_addSkip(self):
-        &quot;&quot;&quot;
-        Test addSkip.
-        &quot;&quot;&quot;
-        self.result.startTest(self.test)
-        self.result.addSkip(self.test, &quot;todo&quot;)
-        self.result.stopTest(self.test)
-
-
     def test_stop(self):
         &quot;&quot;&quot;
@@ -930,12 +849,4 @@
         self.result.stop()
         self.assertEqual(True, self.result.shouldStop)
-
-
-    def test_done(self):
-        &quot;&quot;&quot;
-        done() is called when the test suite is done.
-        &quot;&quot;&quot;
-        # XXX: Drop this and replace with verifyObject
-        self.result.done()
 
 
@@ -973,5 +884,6 @@
 
     def test_startStop(self):
-        TestReporterInterface.test_startStop(self)
+        self.result.startTest(self.test)
+        self.result.stopTest(self.test)
         self.assertTrue(self.result._lastTime &gt; 0)
         self.assertEqual(self.result.testsRun, 1)
</pre>

      </div>
    </content>
    <author>
      <name>jml@example.com (jml)</name>
    </author>
    <updated>2009-12-08T11:35:20Z</updated>
  </entry>

  <entry xmlns="http://www.w3.org/2005/Atom"
      xmlns:thr="http://purl.org/syndication/thread/1.0">
    <link href="http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYyc0FDA"/>
    <id>http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYyc0FDA</id>
    <title>Twisted Changeset [27710]: Comprehensive, direct test coverage of SubunitReporter.   refs #4004</title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
        <p class="meta">
          <a href="http://twistedmatrix.com/trac/changeset/27710">27710</a><br />
            at 2009-12-08 11:25:08<br />
            by jml@example.com (jml)<br />
        </p>

<pre class="data">
Index: /branches/subunit-4004/twisted/trial/test/test_reporter.py
===================================================================
--- /branches/subunit-4004/twisted/trial/test/test_reporter.py (revision 27680)
+++ /branches/subunit-4004/twisted/trial/test/test_reporter.py (revision 27710)
@@ -936,4 +936,5 @@
         done() is called when the test suite is done.
         &quot;&quot;&quot;
+        # XXX: Drop this and replace with verifyObject
         self.result.done()
 
@@ -1177,4 +1178,25 @@
 
 
+    def assertForwardsToSubunit(self, methodName, *args, **kwargs):
+        &quot;&quot;&quot;
+        Assert that &#39;methodName&#39; on L{SubunitReporter} forwards to the
+        equivalent method on subunit.
+
+        Checks that the return value from subunit is returned from the
+        L{SubunitReporter} and that the reporter writes the same data to its
+        stream as subunit does to its own.
+
+        Assumes that the method on subunit has the same name as the method on
+        L{SubunitReporter}.
+        &quot;&quot;&quot;
+        stream = StringIO.StringIO()
+        subunitClient = reporter.TestProtocolClient(stream)
+        subunitReturn = getattr(subunitClient, methodName)(*args, **kwargs)
+        subunitOutput = stream.getvalue()
+        reporterReturn = getattr(self.result, methodName)(*args, **kwargs)
+        self.assertEqual(subunitReturn, reporterReturn)
+        self.assertEqual(subunitOutput, self.stream.getvalue())
+
+
     def test_oldSubunitInstalled(self):
         &quot;&quot;&quot;
@@ -1220,4 +1242,77 @@
 
 
+    def test_doneDoesNothing(self):
+        &quot;&quot;&quot;
+        The subunit reporter doesn&#39;t need to print out a summary -- the stream
+        of results is everything. Thus, done() does nothing.
+        &quot;&quot;&quot;
+        self.result.done()
+        self.assertEqual(&#39;&#39;, self.stream.getvalue())
+
+
+    def test_startTest_sends_subunit_startTest(self):
+        &quot;&quot;&quot;
+        SubunitReporter.startTest() sends the subunit &#39;startTest&#39; message.
+        &quot;&quot;&quot;
+        self.assertForwardsToSubunit(&#39;startTest&#39;, self.test)
+
+
+    def test_stopTest_sends_subunit_stopTest(self):
+        &quot;&quot;&quot;
+        SubunitReporter.stopTest() sends the subunit &#39;stopTest&#39; message.
+        &quot;&quot;&quot;
+        self.assertForwardsToSubunit(&#39;stopTest&#39;, self.test)
+
+
+    def test_addSuccess_sends_subunit_addSuccess(self):
+        &quot;&quot;&quot;
+        SubunitReporter.addSuccess() sends the subunit &#39;addSuccess&#39; message.
+        &quot;&quot;&quot;
+        self.assertForwardsToSubunit(&#39;addSuccess&#39;, self.test)
+
+
+    def test_addSkip_sends_subunit_addSkip(self):
+        &quot;&quot;&quot;
+        SubunitReporter.addSkip() sends the subunit &#39;addSkip&#39; message.
+        &quot;&quot;&quot;
+        self.assertForwardsToSubunit(&#39;addSkip&#39;, self.test, &#39;reason&#39;)
+
+
+    def test_addError_sends_subunit_addError(self):
+        &quot;&quot;&quot;
+        SubunitReporter.addError() sends the subunit &#39;addError&#39; message.
+        &quot;&quot;&quot;
+        try:
+            1 / 0
+        except ZeroDivisionError:
+            error = sys.exc_info()
+        self.assertForwardsToSubunit(&#39;addError&#39;, self.test, error)
+
+
+    def test_addFailure_sends_subunit_addFailure(self):
+        &quot;&quot;&quot;
+        SubunitReporter.addFailure() sends the subunit &#39;addFailure&#39; message.
+        &quot;&quot;&quot;
+        try:
+            self.fail(&#39;hello&#39;)
+        except self.failureException:
+            failure = sys.exc_info()
+        self.assertForwardsToSubunit(&#39;addFailure&#39;, self.test, failure)
+
+
+    def test_addUnexpectedSuccess_sends_subunit_addSuccess(self):
+        &quot;&quot;&quot;
+        SubunitReporter.addFailure() sends the subunit &#39;addSuccess&#39; message,
+        since subunit doesn&#39;t model unexpected success.
+        &quot;&quot;&quot;
+        stream = StringIO.StringIO()
+        subunitClient = reporter.TestProtocolClient(stream)
+        subunitClient.addSuccess(self.test)
+        subunitOutput = stream.getvalue()
+        self.result.addUnexpectedSuccess(self.test, &#39;todo&#39;)
+        self.assertEqual(subunitOutput, self.stream.getvalue())
+
+
+
 class TestSubunitReporterNotInstalled(unittest.TestCase):
     &quot;&quot;&quot;
</pre>

      </div>
    </content>
    <author>
      <name>jml@example.com (jml)</name>
    </author>
    <updated>2009-12-08T11:25:08Z</updated>
  </entry>

  <entry xmlns="http://www.w3.org/2005/Atom"
      xmlns:thr="http://purl.org/syndication/thread/1.0">
    <link href="http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYrtUFDA"/>
    <id>http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYrtUFDA</id>
    <title>Twisted Changeset [27709]: Address review commentary. refs #3296</title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
        <p class="meta">
          <a href="http://twistedmatrix.com/trac/changeset/27709">27709</a><br />
            at 2009-12-08 08:25:29<br />
            by jonathanj@example.com (jonathanj)<br />
        </p>

<pre class="data">
Index: /branches/isupport-aware-mode-parsing-3296/twisted/words/test/test_irc.py
===================================================================
--- /branches/isupport-aware-mode-parsing-3296/twisted/words/test/test_irc.py (revision 27552)
+++ /branches/isupport-aware-mode-parsing-3296/twisted/words/test/test_irc.py (revision 27709)
@@ -884,5 +884,9 @@
 
 
-    def testISupport(self):
+    def test_ISUPPORT(self):
+        &quot;&quot;&quot;
+        The client parses ISUPPORT messages sent by the server and calls
+        L{IRCClient.isupport}.
+        &quot;&quot;&quot;
         self._sendISUPPORT()
 
@@ -1075,6 +1079,6 @@
     def test_modeMissingDirection(self):
         &quot;&quot;&quot;
-        Mode strings that do not begin with a directional character, C{+} or
-        C{-}, have C{+} automatically prepended.
+        Mode strings that do not begin with a directional character, C{&#39;+&#39;} or
+        C{&#39;-&#39;}, have C{&#39;+&#39;} automatically prepended.
         &quot;&quot;&quot;
         self._sendModeChange(&#39;s&#39;)
Index: /branches/isupport-aware-mode-parsing-3296/twisted/words/protocols/irc.py
===================================================================
--- /branches/isupport-aware-mode-parsing-3296/twisted/words/protocols/irc.py (revision 27551)
+++ /branches/isupport-aware-mode-parsing-3296/twisted/words/protocols/irc.py (revision 27709)
@@ -1747,5 +1747,5 @@
         &quot;&quot;&quot;
         channel, modes, args = params[0], params[1], params[2:]
-        
+
         if modes[0] not in &#39;-+&#39;:
             modes = &#39;+&#39; + modes
@@ -1761,7 +1761,6 @@
             added, removed = parseModes(modes, args, paramModes)
         except IRCBadModes:
-            log.msg(&#39;An error occured while parsing the following MODE message:&#39;
-                    &#39; MODE %s&#39; % (&#39; &#39;.join(params),))
-            log.err()
+            log.err(None, &#39;An error occured while parsing the following &#39;
+                          &#39;MODE message: MODE %s&#39; % (&#39; &#39;.join(params),))
         else:
             if added:
</pre>

      </div>
    </content>
    <author>
      <name>jonathanj@example.com (jonathanj)</name>
    </author>
    <updated>2009-12-08T08:25:29Z</updated>
  </entry>

  <entry xmlns="http://www.w3.org/2005/Atom"
      xmlns:thr="http://purl.org/syndication/thread/1.0">
    <link href="http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYyM0FDA"/>
    <id>http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYyM0FDA</id>
    <title>SQLAlchemy Changeset [6545]: - merge of trunk r6544 - Session.execute() now locates table- and  …</title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
        <p class="meta">
          <a href="http://www.sqlalchemy.org/trac/changeset/6545">6545</a><br />
            at 2009-12-08 03:10:59<br />
            by mike_mp@…<br />
        </p>

<pre class="data">
Index: /sqlalchemy/branches/rel_0_5/test/orm/test_session.py
===================================================================
--- /sqlalchemy/branches/rel_0_5/test/orm/test_session.py (revision 6309)
+++ /sqlalchemy/branches/rel_0_5/test/orm/test_session.py (revision 6545)
@@ -106,47 +106,72 @@
     @engines.close_open_connections
     @testing.resolve_artifact_names
-    def test_table_binds_from_expression(self):
-        &quot;&quot;&quot;Session can extract Table objects from ClauseElements and match them to tables.&quot;&quot;&quot;
-
-        mapper(Address, addresses)
-        mapper(User, users, properties={
+    def test_mapped_binds(self):
+
+        # ensure tables are unbound
+        m2 = sa.MetaData()
+        users_unbound =users.tometadata(m2)
+        addresses_unbound = addresses.tometadata(m2)
+
+        mapper(Address, addresses_unbound)
+        mapper(User, users_unbound, properties={
             &#39;addresses&#39;:relation(Address,
                                  backref=backref(&quot;user&quot;, cascade=&quot;all&quot;),
                                  cascade=&quot;all&quot;)})
 
-        Session = sessionmaker(binds={users: self.metadata.bind,
-                                      addresses: self.metadata.bind})
+        Session = sessionmaker(binds={User: self.metadata.bind,
+                                      Address: self.metadata.bind})
         sess = Session()
 
-        sess.execute(users.insert(), params=dict(id=1, name=&#39;ed&#39;))
-        eq_(sess.execute(users.select(users.c.id == 1)).fetchall(),
-            [(1, &#39;ed&#39;)])
-
-        eq_(sess.execute(users.select(User.id == 1)).fetchall(),
-            [(1, &#39;ed&#39;)])
-
+        u1 = User(id=1, name=&#39;ed&#39;)
+        sess.add(u1)
+        eq_(sess.query(User).filter(User.id==1).all(),
+            [User(id=1, name=&#39;ed&#39;)])
+
+        # test expression binding
+        sess.execute(users_unbound.insert(), params=dict(id=2, name=&#39;jack&#39;))
+        eq_(sess.execute(users_unbound.select(users_unbound.c.id == 2)).fetchall(),
+            [(2, &#39;jack&#39;)])
+
+        eq_(sess.execute(users_unbound.select(User.id == 2)).fetchall(),
+            [(2, &#39;jack&#39;)])
+
+        sess.execute(users_unbound.delete())
+        eq_(sess.execute(users_unbound.select()).fetchall(), [])
+        
         sess.close()
 
     @engines.close_open_connections
     @testing.resolve_artifact_names
-    def test_mapped_binds_from_expression(self):
-        &quot;&quot;&quot;Session can extract Table objects from ClauseElements and match them to tables.&quot;&quot;&quot;
-
-        mapper(Address, addresses)
-        mapper(User, users, properties={
+    def test_table_binds(self):
+
+        # ensure tables are unbound
+        m2 = sa.MetaData()
+        users_unbound =users.tometadata(m2)
+        addresses_unbound = addresses.tometadata(m2)
+
+        mapper(Address, addresses_unbound)
+        mapper(User, users_unbound, properties={
             &#39;addresses&#39;:relation(Address,
                                  backref=backref(&quot;user&quot;, cascade=&quot;all&quot;),
                                  cascade=&quot;all&quot;)})
 
-        Session = sessionmaker(binds={User: self.metadata.bind,
-                                      Address: self.metadata.bind})
+        Session = sessionmaker(binds={users_unbound: self.metadata.bind,
+                                      addresses_unbound: self.metadata.bind})
         sess = Session()
 
-        sess.execute(users.insert(), params=dict(id=1, name=&#39;ed&#39;))
-        eq_(sess.execute(users.select(users.c.id == 1)).fetchall(),
-            [(1, &#39;ed&#39;)])
-
-        eq_(sess.execute(users.select(User.id == 1)).fetchall(),
-            [(1, &#39;ed&#39;)])
+        u1 = User(id=1, name=&#39;ed&#39;)
+        sess.add(u1)
+        eq_(sess.query(User).filter(User.id==1).all(),
+            [User(id=1, name=&#39;ed&#39;)])
+
+        sess.execute(users_unbound.insert(), params=dict(id=2, name=&#39;jack&#39;))
+        eq_(sess.execute(users_unbound.select(users_unbound.c.id == 2)).fetchall(),
+            [(2, &#39;jack&#39;)])
+
+        eq_(sess.execute(users_unbound.select(User.id == 2)).fetchall(),
+            [(2, &#39;jack&#39;)])
+
+        sess.execute(users_unbound.delete())
+        eq_(sess.execute(users_unbound.select()).fetchall(), [])
 
         sess.close()
Index: /sqlalchemy/branches/rel_0_5/lib/sqlalchemy/orm/session.py
===================================================================
--- /sqlalchemy/branches/rel_0_5/lib/sqlalchemy/orm/session.py (revision 6535)
+++ /sqlalchemy/branches/rel_0_5/lib/sqlalchemy/orm/session.py (revision 6545)
@@ -571,11 +571,9 @@
 
         if binds is not None:
-            for mapperortable, value in binds.iteritems():
-                if isinstance(mapperortable, type):
-                    mapperortable = _class_mapper(mapperortable).base_mapper
-                self.__binds[mapperortable] = value
-                if isinstance(mapperortable, Mapper):
-                    for t in mapperortable._all_tables:
-                        self.__binds[t] = value
+            for mapperortable, bind in binds.iteritems():
+                if isinstance(mapperortable, (type, Mapper)):
+                    self.bind_mapper(mapperortable, bind)
+                else:
+                    self.bind_table(mapperortable, bind)
 
         if not self.autocommit:
@@ -858,5 +856,5 @@
 
         c_mapper = mapper is not None and _class_to_mapper(mapper) or None
-
+        
         # manually bound?
         if self.__binds:
@@ -867,5 +865,5 @@
                     return self.__binds[c_mapper.mapped_table]
             if clause:
-                for t in sql_util.find_tables(clause):
+                for t in sql_util.find_tables(clause, include_crud=True):
                     if t in self.__binds:
                         return self.__binds[t]
Index: /sqlalchemy/branches/rel_0_5/lib/sqlalchemy/sql/util.py
===================================================================
--- /sqlalchemy/branches/rel_0_5/lib/sqlalchemy/sql/util.py (revision 6298)
+++ /sqlalchemy/branches/rel_0_5/lib/sqlalchemy/sql/util.py (revision 6545)
@@ -48,5 +48,7 @@
 
     
-def find_tables(clause, check_columns=False, include_aliases=False, include_joins=False, include_selects=False):
+def find_tables(clause, check_columns=False, 
+                include_aliases=False, include_joins=False, 
+                include_selects=False, include_crud=False):
     &quot;&quot;&quot;locate Table objects within the given expression.&quot;&quot;&quot;
     
@@ -62,5 +64,9 @@
     if include_aliases:
         _visitors[&#39;alias&#39;]  = tables.append
-
+    
+    if include_crud:
+        _visitors[&#39;insert&#39;] = _visitors[&#39;update&#39;] = \
+                    _visitors[&#39;delete&#39;] = lambda ent: tables.append(ent.table)
+        
     if check_columns:
         def visit_column(column):
Index: /sqlalchemy/branches/rel_0_5/CHANGES
===================================================================
--- /sqlalchemy/branches/rel_0_5/CHANGES (revision 6541)
+++ /sqlalchemy/branches/rel_0_5/CHANGES (revision 6545)
@@ -23,5 +23,10 @@
       by contains_eager() out of individual instance states.
       [ticket:1553]
-
+    
+    - Session.execute() now locates table- and 
+      mapper-specific binds based on a passed
+      in expression which is an insert()/update()/delete() 
+      construct. [ticket:1054]
+      
     - Fixed a needless select which would occur when merging
       transient objects that contained a null primary key
</pre>

      </div>
    </content>
    <author>
      <name>mike_mp@…</name>
    </author>
    <updated>2009-12-08T03:10:59Z</updated>
  </entry>

  <entry xmlns="http://www.w3.org/2005/Atom"
      xmlns:thr="http://purl.org/syndication/thread/1.0">
    <link href="http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYx80FDA"/>
    <id>http://ware.appspot.com/agR3YXJlchELEglDaGFuZ2Vsb2cYx80FDA</id>
    <title>SQLAlchemy Changeset [6544]: - Session.execute() now locates table- and  mapper-specific binds based on  …</title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
        <p class="meta">
          <a href="http://www.sqlalchemy.org/trac/changeset/6544">6544</a><br />
            at 2009-12-08 03:09:18<br />
            by mike_mp@…<br />
        </p>

<pre class="data">
Index: /sqlalchemy/trunk/test/orm/test_session.py
===================================================================
--- /sqlalchemy/trunk/test/orm/test_session.py (revision 6407)
+++ /sqlalchemy/trunk/test/orm/test_session.py (revision 6544)
@@ -105,47 +105,72 @@
     @engines.close_open_connections
     @testing.resolve_artifact_names
-    def test_table_binds_from_expression(self):
-        &quot;&quot;&quot;Session can extract Table objects from ClauseElements and match them to tables.&quot;&quot;&quot;
-
-        mapper(Address, addresses)
-        mapper(User, users, properties={
+    def test_mapped_binds(self):
+
+        # ensure tables are unbound
+        m2 = sa.MetaData()
+        users_unbound =users.tometadata(m2)
+        addresses_unbound = addresses.tometadata(m2)
+
+        mapper(Address, addresses_unbound)
+        mapper(User, users_unbound, properties={
             &#39;addresses&#39;:relation(Address,
                                  backref=backref(&quot;user&quot;, cascade=&quot;all&quot;),
                                  cascade=&quot;all&quot;)})
 
-        Session = sessionmaker(binds={users: self.metadata.bind,
-                                      addresses: self.metadata.bind})
+        Session = sessionmaker(binds={User: self.metadata.bind,
+                                      Address: self.metadata.bind})
         sess = Session()
 
-        sess.execute(users.insert(), params=dict(id=1, name=&#39;ed&#39;))
-        eq_(sess.execute(users.select(users.c.id == 1)).fetchall(),
-            [(1, &#39;ed&#39;)])
-
-        eq_(sess.execute(users.select(User.id == 1)).fetchall(),
-            [(1, &#39;ed&#39;)])
-
+        u1 = User(id=1, name=&#39;ed&#39;)
+        sess.add(u1)
+        eq_(sess.query(User).filter(User.id==1).all(),
+            [User(id=1, name=&#39;ed&#39;)])
+
+        # test expression binding
+        sess.execute(users_unbound.insert(), params=dict(id=2, name=&#39;jack&#39;))
+        eq_(sess.execute(users_unbound.select(users_unbound.c.id == 2)).fetchall(),
+            [(2, &#39;jack&#39;)])
+
+        eq_(sess.execute(users_unbound.select(User.id == 2)).fetchall(),
+            [(2, &#39;jack&#39;)])
+
+        sess.execute(users_unbound.delete())
+        eq_(sess.execute(users_unbound.select()).fetchall(), [])
+        
         sess.close()
 
     @engines.close_open_connections
     @testing.resolve_artifact_names
-    def test_mapped_binds_from_expression(self):
-        &quot;&quot;&quot;Session can extract Table objects from ClauseElements and match them to tables.&quot;&quot;&quot;
-
-        mapper(Address, addresses)
-        mapper(User, users, properties={
+    def test_table_binds(self):
+
+        # ensure tables are unbound
+        m2 = sa.MetaData()
+        users_unbound =users.tometadata(m2)
+        addresses_unbound = addresses.tometadata(m2)
+
+        mapper(Address, addresses_unbound)
+        mapper(User, users_unbound, properties={
             &#39;addresses&#39;:relation(Address,
                                  backref=backref(&quot;user&quot;, cascade=&quot;all&quot;),
                                  cascade=&quot;all&quot;)})
 
-        Session = sessionmaker(binds={User: self.metadata.bind,
-                                      Address: self.metadata.bind})
+        Session = sessionmaker(binds={users_unbound: self.metadata.bind,
+                                      addresses_unbound: self.metadata.bind})
         sess = Session()
 
-        sess.execute(users.insert(), params=dict(id=1, name=&#39;ed&#39;))
-        eq_(sess.execute(users.select(users.c.id == 1)).fetchall(),
-            [(1, &#39;ed&#39;)])
-
-        eq_(sess.execute(users.select(User.id == 1)).fetchall(),
-            [(1, &#39;ed&#39;)])
+        u1 = User(id=1, name=&#39;ed&#39;)
+        sess.add(u1)
+        eq_(sess.query(User).filter(User.id==1).all(),
+            [User(id=1, name=&#39;ed&#39;)])
+
+        sess.execute(users_unbound.insert(), params=dict(id=2, name=&#39;jack&#39;))
+        eq_(sess.execute(users_unbound.select(users_unbound.c.id == 2)).fetchall(),
+            [(2, &#39;jack&#39;)])
+
+        eq_(sess.execute(users_unbound.select(User.id == 2)).fetchall(),
+            [(2, &#39;jack&#39;)])
+
+        sess.execute(users_unbound.delete())
+        eq_(sess.execute(users_unbound.select()).fetchall(), [])
 
         sess.close()
Index: /sqlalchemy/trunk/lib/sqlalchemy/orm/session.py
===================================================================
--- /sqlalchemy/trunk/lib/sqlalchemy/orm/session.py (revision 6534)
+++ /sqlalchemy/trunk/lib/sqlalchemy/orm/session.py (revision 6544)
@@ -555,11 +555,9 @@
 
         if binds is not None:
-            for mapperortable, value in binds.iteritems():
-                if isinstance(mapperortable, type):
-                    mapperortable = _class_mapper(mapperortable).base_mapper
-                self.__binds[mapperortable] = value
-                if isinstance(mapperortable, Mapper):
-                    for t in mapperortable._all_tables:
-                        self.__binds[t] = value
+            for mapperortable, bind in binds.iteritems():
+                if isinstance(mapperortable, (type, Mapper)):
+                    self.bind_mapper(mapperortable, bind)
+                else:
+                    self.bind_table(mapperortable, bind)
 
         if not self.autocommit:
@@ -840,5 +838,5 @@
 
         c_mapper = mapper is not None and _class_to_mapper(mapper) or None
-
+        
         # manually bound?
         if self.__binds:
@@ -849,5 +847,5 @@
                     return self.__binds[c_mapper.mapped_table]
             if clause is not None:
-                for t in sql_util.find_tables(clause):
+                for t in sql_util.find_tables(clause, include_crud=True):
                     if t in self.__binds:
                         return self.__binds[t]
Index: /sqlalchemy/trunk/lib/sqlalchemy/sql/util.py
===================================================================
--- /sqlalchemy/trunk/lib/sqlalchemy/sql/util.py (revision 6529)
+++ /sqlalchemy/trunk/lib/sqlalchemy/sql/util.py (revision 6544)
@@ -48,5 +48,7 @@
 
     
-def find_tables(clause, check_columns=False, include_aliases=False, include_joins=False, include_selects=False):
+def find_tables(clause, check_columns=False, 
+                include_aliases=False, include_joins=False, 
+                include_selects=False, include_crud=False):
     &quot;&quot;&quot;locate Table objects within the given expression.&quot;&quot;&quot;
     
@@ -62,5 +64,9 @@
     if include_aliases:
         _visitors[&#39;alias&#39;]  = tables.append
-
+    
+    if include_crud:
+        _visitors[&#39;insert&#39;] = _visitors[&#39;update&#39;] = \
+                    _visitors[&#39;delete&#39;] = lambda ent: tables.append(ent.table)
+        
     if check_columns:
         def visit_column(column):
Index: /sqlalchemy/trunk/CHANGES
===================================================================
--- /sqlalchemy/trunk/CHANGES (revision 6543)
+++ /sqlalchemy/trunk/CHANGES (revision 6544)
@@ -764,5 +764,10 @@
       by contains_eager() out of individual instance states.
       [ticket:1553]
-
+    
+    - Session.execute() now locates table- and 
+      mapper-specific binds based on a passed
+      in expression which is an insert()/update()/delete() 
+      construct. [ticket:1054]
+      
     - Fixed a needless select which would occur when merging
       transient objects that contained a null primary key
</pre>

      </div>
    </content>
    <author>
      <name>mike_mp@…</name>
    </author>
    <updated>2009-12-08T03:09:18Z</updated>
  </entry>

</feed>
