Enrico Zinihttp://www.enricozini.org/tags/turbogearsstaticsite2009-11-04T16:52:38ZEnrico Zini: posts with tag turbogearsCustom function decorators with TurboGears 2http://www.enricozini.org/blog/2009/tg/custom-decorators2009-11-04T16:52:38Z2009-11-04T16:52:38Z
<p>I am exposing some library functions using a TurboGears2 controller (see
<a href="http://www.enricozini.org/blog/2009/tg/web-api-with-turbogears2">Building a web-based API with Turbogears2</a>). It turns out that some functions return a dict,
some a list, some a string, and TurboGears 2 only allows JSON serialisation for
dicts.</p>
<p>A simple work-around for this is to wrap the function result into a dict,
something like this:</p>
<div class="codehilite"><pre><span></span><code><span class="nd">@expose</span><span class="p">(</span><span class="s2">"json"</span><span class="p">)</span>
<span class="nd">@validate</span><span class="p">(</span><span class="n">validator_dispatcher</span><span class="p">,</span> <span class="n">error_handler</span><span class="o">=</span><span class="n">api_validation_error</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">list_colours</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">filter</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">productID</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">maxResults</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="c1"># Call API</span>
<span class="n">res</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">engine</span><span class="o">.</span><span class="n">list_colours</span><span class="p">(</span><span class="nb">filter</span><span class="p">,</span> <span class="n">productID</span><span class="p">,</span> <span class="n">maxResults</span><span class="p">)</span>
<span class="c1"># Return result</span>
<span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">r</span><span class="o">=</span><span class="n">res</span><span class="p">)</span>
</code></pre></div>
<p>It would be nice, however, to have an <code>@webapi()</code> decorator that
automatically wraps the function result with the dict:</p>
<div class="codehilite"><pre><span></span><code><span class="k">def</span> <span class="nf">webapi</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">dict_wrap</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">r</span><span class="o">=</span><span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">))</span>
<span class="k">return</span> <span class="n">dict_wrap</span>
<span class="c1"># ...in the controller...</span>
<span class="nd">@expose</span><span class="p">(</span><span class="s2">"json"</span><span class="p">)</span>
<span class="nd">@validate</span><span class="p">(</span><span class="n">validator_dispatcher</span><span class="p">,</span> <span class="n">error_handler</span><span class="o">=</span><span class="n">api_validation_error</span><span class="p">)</span>
<span class="nd">@webapi</span>
<span class="k">def</span> <span class="nf">list_colours</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">filter</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">productID</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">maxResults</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="c1"># Call API</span>
<span class="n">res</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">engine</span><span class="o">.</span><span class="n">list_colours</span><span class="p">(</span><span class="nb">filter</span><span class="p">,</span> <span class="n">productID</span><span class="p">,</span> <span class="n">maxResults</span><span class="p">)</span>
<span class="c1"># Return result</span>
<span class="k">return</span> <span class="n">res</span>
</code></pre></div>
<p>This works, as long as @webapi appears <em>last</em> in the list of decorators.
This is because if it appears last it will be the first to wrap the function,
and so it will not interfere with the <code>tg.decorators</code> machinery.</p>
<p>Would it be possible to create a decorator that can be put anywhere among the
decorator list? Yes, it is possible but tricky, and it gives me the feeling
that it may break in any future version of TurboGears:</p>
<div class="codehilite"><pre><span></span><code><span class="k">class</span> <span class="nc">webapi</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">func</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">dict_wrap</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">r</span><span class="o">=</span><span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">))</span>
<span class="c1"># Migrate the decoration attribute to our new function</span>
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="s1">'decoration'</span><span class="p">):</span>
<span class="n">dict_wrap</span><span class="o">.</span><span class="n">decoration</span> <span class="o">=</span> <span class="n">func</span><span class="o">.</span><span class="n">decoration</span>
<span class="n">dict_wrap</span><span class="o">.</span><span class="n">decoration</span><span class="o">.</span><span class="n">controller</span> <span class="o">=</span> <span class="n">dict_wrap</span>
<span class="nb">delattr</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="s1">'decoration'</span><span class="p">)</span>
<span class="k">return</span> <span class="n">dict_wrap</span>
<span class="c1"># ...in the controller...</span>
<span class="nd">@expose</span><span class="p">(</span><span class="s2">"json"</span><span class="p">)</span>
<span class="nd">@validate</span><span class="p">(</span><span class="n">validator_dispatcher</span><span class="p">,</span> <span class="n">error_handler</span><span class="o">=</span><span class="n">api_validation_error</span><span class="p">)</span>
<span class="nd">@webapi</span>
<span class="k">def</span> <span class="nf">list_colours</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">filter</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">productID</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">maxResults</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="c1"># Call API</span>
<span class="n">res</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">engine</span><span class="o">.</span><span class="n">list_colours</span><span class="p">(</span><span class="nb">filter</span><span class="p">,</span> <span class="n">productID</span><span class="p">,</span> <span class="n">maxResults</span><span class="p">)</span>
<span class="c1"># Return result</span>
<span class="k">return</span> <span class="n">res</span>
</code></pre></div>
<p>As a convenience, TurboGears 2 offers, in the <code>decorators</code> module, a way to
build decorator "hooks":</p>
<div class="codehilite"><pre><span></span><code><span class="k">class</span> <span class="nc">before_validate</span><span class="p">(</span><span class="n">_hook_decorator</span><span class="p">):</span>
<span class="sd">'''A list of callables to be run before validation is performed'''</span>
<span class="n">hook_name</span> <span class="o">=</span> <span class="s1">'before_validate'</span>
<span class="k">class</span> <span class="nc">before_call</span><span class="p">(</span><span class="n">_hook_decorator</span><span class="p">):</span>
<span class="sd">'''A list of callables to be run before the controller method is called'''</span>
<span class="n">hook_name</span> <span class="o">=</span> <span class="s1">'before_call'</span>
<span class="k">class</span> <span class="nc">before_render</span><span class="p">(</span><span class="n">_hook_decorator</span><span class="p">):</span>
<span class="sd">'''A list of callables to be run before the template is rendered'''</span>
<span class="n">hook_name</span> <span class="o">=</span> <span class="s1">'before_render'</span>
<span class="k">class</span> <span class="nc">after_render</span><span class="p">(</span><span class="n">_hook_decorator</span><span class="p">):</span>
<span class="sd">'''A list of callables to be run after the template is rendered.</span>
<span class="sd"> Will be run before it is returned returned up the WSGI stack'''</span>
<span class="n">hook_name</span> <span class="o">=</span> <span class="s1">'after_render'</span>
</code></pre></div>
<p>The way these are invoked can be found in the <code>_perform_call</code> function in
<code>tg/controllers.py</code>.</p>
<p>To show an example use of those hooks, let's add a some polygen wisdom to every
data structure we return:</p>
<div class="codehilite"><pre><span></span><code><span class="k">class</span> <span class="nc">wisdom</span><span class="p">(</span><span class="n">decorators</span><span class="o">.</span><span class="n">before_render</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">grammar</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">wisdom</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">add_wisdom</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">grammar</span> <span class="o">=</span> <span class="n">grammar</span>
<span class="k">def</span> <span class="nf">add_wisdom</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">remainder</span><span class="p">,</span> <span class="n">params</span><span class="p">,</span> <span class="n">output</span><span class="p">):</span>
<span class="kn">from</span> <span class="nn">subprocess</span> <span class="kn">import</span> <span class="n">Popen</span><span class="p">,</span> <span class="n">PIPE</span>
<span class="n">output</span><span class="p">[</span><span class="s2">"wisdom"</span><span class="p">]</span> <span class="o">=</span> <span class="n">Popen</span><span class="p">([</span><span class="s2">"polyrun"</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">grammar</span><span class="p">],</span> <span class="n">stdout</span><span class="o">=</span><span class="n">PIPE</span><span class="p">)</span><span class="o">.</span><span class="n">communicate</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span>
<span class="c1"># ...in the controller...</span>
<span class="nd">@wisdom</span><span class="p">(</span><span class="s2">"genius"</span><span class="p">)</span>
<span class="nd">@expose</span><span class="p">(</span><span class="s2">"json"</span><span class="p">)</span>
<span class="nd">@validate</span><span class="p">(</span><span class="n">validator_dispatcher</span><span class="p">,</span> <span class="n">error_handler</span><span class="o">=</span><span class="n">api_validation_error</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">list_colours</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">filter</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">productID</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">maxResults</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="c1"># Call API</span>
<span class="n">res</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">engine</span><span class="o">.</span><span class="n">list_colours</span><span class="p">(</span><span class="nb">filter</span><span class="p">,</span> <span class="n">productID</span><span class="p">,</span> <span class="n">maxResults</span><span class="p">)</span>
<span class="c1"># Return result</span>
<span class="k">return</span> <span class="n">res</span>
</code></pre></div>
<p>These hooks cannot however be used for what I need, that is, to wrap the result
inside a dict. The reason is because they are called in this way:</p>
<div class="codehilite"><pre><span></span><code> <span class="n">controller</span><span class="o">.</span><span class="n">decoration</span><span class="o">.</span><span class="n">run_hooks</span><span class="p">(</span>
<span class="s1">'before_render'</span><span class="p">,</span> <span class="n">remainder</span><span class="p">,</span> <span class="n">params</span><span class="p">,</span> <span class="n">output</span><span class="p">)</span>
</code></pre></div>
<p>and not in this way:</p>
<div class="codehilite"><pre><span></span><code> <span class="n">output</span> <span class="o">=</span> <span class="n">controller</span><span class="o">.</span><span class="n">decoration</span><span class="o">.</span><span class="n">run_hooks</span><span class="p">(</span>
<span class="s1">'before_render'</span><span class="p">,</span> <span class="n">remainder</span><span class="p">,</span> <span class="n">params</span><span class="p">,</span> <span class="n">output</span><span class="p">)</span>
</code></pre></div>
<p>So it is possible to modify the output (if it is a mutable structure) but not
to exchange it with something else.</p>
<p>Can we do even better? Sure we can. We can assimilate <code>@expose</code> and <code>@validate</code>
inside <code>@webapi</code> to avoid repeating those same many decorator lines over and
over again:</p>
<div class="codehilite"><pre><span></span><code><span class="k">class</span> <span class="nc">webapi</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">error_handler</span> <span class="o">=</span> <span class="kc">None</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">error_handler</span> <span class="o">=</span> <span class="n">error_handler</span>
<span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">func</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">dict_wrap</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">r</span><span class="o">=</span><span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">))</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">expose</span><span class="p">(</span><span class="s2">"json"</span><span class="p">)(</span><span class="n">dict_wrap</span><span class="p">)</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">validate</span><span class="p">(</span><span class="n">validator_dispatcher</span><span class="p">,</span> <span class="n">error_handler</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">error_handler</span><span class="p">)(</span><span class="n">res</span><span class="p">)</span>
<span class="k">return</span> <span class="n">res</span>
<span class="c1"># ...in the controller...</span>
<span class="nd">@expose</span><span class="p">(</span><span class="s2">"json"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">api_validation_error</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="n">pylons</span><span class="o">.</span><span class="n">response</span><span class="o">.</span><span class="n">status</span> <span class="o">=</span> <span class="s2">"400 Error"</span>
<span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">e</span><span class="o">=</span><span class="s2">"validation error on input fields"</span><span class="p">,</span> <span class="n">form_errors</span><span class="o">=</span><span class="n">pylons</span><span class="o">.</span><span class="n">c</span><span class="o">.</span><span class="n">form_errors</span><span class="p">)</span>
<span class="nd">@webapi</span><span class="p">(</span><span class="n">error_handler</span><span class="o">=</span><span class="n">api_validation_error</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">list_colours</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">filter</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">productID</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">maxResults</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="c1"># Call API</span>
<span class="n">res</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">engine</span><span class="o">.</span><span class="n">list_colours</span><span class="p">(</span><span class="nb">filter</span><span class="p">,</span> <span class="n">productID</span><span class="p">,</span> <span class="n">maxResults</span><span class="p">)</span>
<span class="c1"># Return result</span>
<span class="k">return</span> <span class="n">res</span>
</code></pre></div>
<p>This got rid of <code>@expose</code> and <code>@validate</code>, and provides <em>almost</em> all the
default values that I need. Unfortunately I could not find out how to access
<code>api_validation_error</code> from the decorator so that I can pass it to the
validator, therefore I remain with the inconvenience of having to explicitly
pass it every time.</p>
Building a web-based API with Turbogears2http://www.enricozini.org/blog/2009/tg/web-api-with-turbogears22009-10-15T13:45:39Z2009-10-15T13:45:39Z
<p>I am using TurboGears2 to export a python API over the web. Every API method is
wrapper by a controller method that validates the parameters and returns the
results encoded in JSON.</p>
<p>The basic idea is this:</p>
<div class="codehilite"><pre><span></span><span class="nd">@expose</span><span class="p">(</span><span class="s2">"json"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">list_colours</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">filter</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">productID</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">maxResults</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="c1"># Call API</span>
<span class="n">res</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">engine</span><span class="o">.</span><span class="n">list_colours</span><span class="p">(</span><span class="nb">filter</span><span class="p">,</span> <span class="n">productID</span><span class="p">,</span> <span class="n">maxResults</span><span class="p">)</span>
<span class="c1"># Return result</span>
<span class="k">return</span> <span class="n">res</span>
</pre></div>
<p>To validate the parameters we can use forms, it's their job after all:</p>
<div class="codehilite"><pre><span></span><span class="k">class</span> <span class="nc">ListColoursForm</span><span class="p">(</span><span class="n">TableForm</span><span class="p">):</span>
<span class="n">fields</span> <span class="o">=</span> <span class="p">[</span>
<span class="c1"># One field per parameter</span>
<span class="n">twf</span><span class="o">.</span><span class="n">TextField</span><span class="p">(</span><span class="s2">"filter"</span><span class="p">,</span> <span class="n">help_text</span><span class="o">=</span><span class="s2">"Please enter the string to use as a filter"</span><span class="p">),</span>
<span class="n">twf</span><span class="o">.</span><span class="n">TextField</span><span class="p">(</span><span class="s2">"productID"</span><span class="p">,</span> <span class="n">help_text</span><span class="o">=</span><span class="s2">"Please enter the product ID"</span><span class="p">),</span>
<span class="n">twf</span><span class="o">.</span><span class="n">TextField</span><span class="p">(</span><span class="s2">"maxResults"</span><span class="p">,</span> <span class="n">validator</span><span class="o">=</span><span class="n">twfv</span><span class="o">.</span><span class="n">Int</span><span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mi">0</span><span class="p">),</span> <span class="n">default</span><span class="o">=</span><span class="mi">200</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="mi">5</span><span class="p">,</span> <span class="n">help_text</span><span class="o">=</span><span class="s2">"Please enter the maximum number of results"</span><span class="p">),</span>
<span class="p">]</span>
<span class="n">list_colours_form</span><span class="o">=</span><span class="n">ListColoursForm</span><span class="p">()</span>
<span class="c1">#...</span>
<span class="nd">@expose</span><span class="p">(</span><span class="s2">"json"</span><span class="p">)</span>
<span class="nd">@validate</span><span class="p">(</span><span class="n">list_colours_form</span><span class="p">,</span> <span class="n">error_handler</span><span class="o">=</span><span class="n">list_colours_validation_error</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">list_colours</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">filter</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">productID</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">maxResults</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="c1"># Parameter validation is done by the form</span>
<span class="c1"># Call API</span>
<span class="n">res</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">engine</span><span class="o">.</span><span class="n">list_colours</span><span class="p">(</span><span class="nb">filter</span><span class="p">,</span> <span class="n">productID</span><span class="p">,</span> <span class="n">maxResults</span><span class="p">)</span>
<span class="c1"># Return result</span>
<span class="k">return</span> <span class="n">res</span>
</pre></div>
<p>All straightforward so far. However, this means that we need two exposed
methods for every API call: one for the API call and one error handler.
For every API call, we have to type the name several times, which is error
prone and risks to get things mixed up.</p>
<p>We can however have a single error handler for all methonds:</p>
<div class="codehilite"><pre><span></span><span class="k">def</span> <span class="nf">get_method</span><span class="p">():</span>
<span class="sd">'''</span>
<span class="sd"> The method name is the first url component after the controller name that</span>
<span class="sd"> does not start with 'test'</span>
<span class="sd"> '''</span>
<span class="n">found_controller</span> <span class="o">=</span> <span class="bp">False</span>
<span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="n">pylons</span><span class="o">.</span><span class="n">c</span><span class="o">.</span><span class="n">url</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"/"</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">found_controller</span> <span class="ow">and</span> <span class="n">name</span> <span class="o">==</span> <span class="s2">"controllername"</span><span class="p">:</span>
<span class="n">found_controller</span> <span class="o">=</span> <span class="bp">True</span>
<span class="k">continue</span>
<span class="k">if</span> <span class="n">name</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">"test"</span><span class="p">):</span>
<span class="k">continue</span>
<span class="k">if</span> <span class="n">found_controller</span><span class="p">:</span>
<span class="k">return</span> <span class="n">name</span>
<span class="k">return</span> <span class="bp">None</span>
<span class="k">class</span> <span class="nc">ValidatorDispatcher</span><span class="p">:</span>
<span class="sd">'''</span>
<span class="sd"> Validate using the right form according to the value of the "method" field</span>
<span class="sd"> '''</span>
<span class="k">def</span> <span class="nf">validate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">state</span><span class="p">):</span>
<span class="n">method</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"method"</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>
<span class="c1"># Extract the method from the URL if it is missing</span>
<span class="k">if</span> <span class="n">method</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
<span class="n">method</span> <span class="o">=</span> <span class="n">get_method</span><span class="p">()</span>
<span class="n">args</span><span class="p">[</span><span class="s2">"method"</span><span class="p">]</span> <span class="o">=</span> <span class="n">method</span>
<span class="k">return</span> <span class="n">forms</span><span class="p">[</span><span class="n">method</span><span class="p">]</span><span class="o">.</span><span class="n">validate</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">state</span><span class="p">)</span>
<span class="n">validator_dispatcher</span> <span class="o">=</span> <span class="n">ValidatorDispatcher</span><span class="p">()</span>
</pre></div>
<p>This validator will try to find the method name, either as a form field
or by parsing the URL. It will then use the method name to find the form to use
for validation, and pass control to the <code>validate</code> method of that form.</p>
<p>We then need to add an extra "method" field to our forms, and arrange the forms
inside a dictionary:</p>
<div class="codehilite"><pre><span></span><span class="k">class</span> <span class="nc">ListColoursForm</span><span class="p">(</span><span class="n">TableForm</span><span class="p">):</span>
<span class="n">fields</span> <span class="o">=</span> <span class="p">[</span>
<span class="c1"># One hidden field to have a place for the method name</span>
<span class="n">twf</span><span class="o">.</span><span class="n">HiddenField</span><span class="p">(</span><span class="s2">"method"</span><span class="p">)</span>
<span class="c1"># One field per parameter</span>
<span class="n">twf</span><span class="o">.</span><span class="n">TextField</span><span class="p">(</span><span class="s2">"filter"</span><span class="p">,</span> <span class="n">help_text</span><span class="o">=</span><span class="s2">"Please enter the string to use as a filter"</span><span class="p">),</span>
<span class="c1">#...</span>
<span class="n">forms</span><span class="p">[</span><span class="s2">"list_colours"</span><span class="p">]</span> <span class="o">=</span> <span class="n">ListColoursForm</span><span class="p">()</span>
</pre></div>
<p>And now our methods become much nicer to write:</p>
<div class="codehilite"><pre><span></span> <span class="nd">@expose</span><span class="p">(</span><span class="s2">"json"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">api_validation_error</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="n">pylons</span><span class="o">.</span><span class="n">response</span><span class="o">.</span><span class="n">status</span> <span class="o">=</span> <span class="s2">"400 Error"</span>
<span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">form_errors</span><span class="o">=</span><span class="n">pylons</span><span class="o">.</span><span class="n">c</span><span class="o">.</span><span class="n">form_errors</span><span class="p">)</span>
<span class="nd">@expose</span><span class="p">(</span><span class="s2">"json"</span><span class="p">)</span>
<span class="nd">@validate</span><span class="p">(</span><span class="n">validator_dispatcher</span><span class="p">,</span> <span class="n">error_handler</span><span class="o">=</span><span class="n">api_validation_error</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">list_colours</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">filter</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">productID</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">maxResults</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="c1"># Parameter validation is done by the form</span>
<span class="c1"># Call API</span>
<span class="n">res</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">engine</span><span class="o">.</span><span class="n">list_colours</span><span class="p">(</span><span class="nb">filter</span><span class="p">,</span> <span class="n">productID</span><span class="p">,</span> <span class="n">maxResults</span><span class="p">)</span>
<span class="c1"># Return result</span>
<span class="k">return</span> <span class="n">res</span>
</pre></div>
<p><code>api_validation_error</code> is interesting: it returns a proper HTTP error status,
and a JSON body with the details of the error, taken straight from the form
validators. It took me a while to find out that the form errors are in
<code>pylons.c.form_errors</code> (and for reference, the form values are in
<code>pylons.c.form_values</code>). <code>pylons.response</code> is a <a href="http://pythonpaste.org/webob/reference.html#id2">WebOb Response</a> that we can play with.</p>
<p>So now our client side is able to call the API methods, and get a proper error
if it calls them wrong.</p>
<p>But now that we have the forms ready, it doesn't take much to display them in
web pages as well:</p>
<div class="codehilite"><pre><span></span><span class="k">def</span> <span class="nf">_describe</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">method</span><span class="p">):</span>
<span class="s2">"Return a dict describing an API method"</span>
<span class="n">ldesc</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">engine</span><span class="p">,</span> <span class="n">method</span><span class="p">)</span><span class="o">.</span><span class="vm">__doc__</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
<span class="n">sdesc</span> <span class="o">=</span> <span class="n">ldesc</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="n">method</span><span class="p">,</span> <span class="n">sdesc</span> <span class="o">=</span> <span class="n">sdesc</span><span class="p">,</span> <span class="n">ldesc</span> <span class="o">=</span> <span class="n">ldesc</span><span class="p">)</span>
<span class="nd">@expose</span><span class="p">(</span><span class="s2">"myappserver.templates.myappapi"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">index</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">'''</span>
<span class="sd"> Show an index of exported API methods</span>
<span class="sd"> '''</span>
<span class="n">methods</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">()</span>
<span class="k">for</span> <span class="n">m</span> <span class="ow">in</span> <span class="n">forms</span><span class="o">.</span><span class="n">keys</span><span class="p">():</span>
<span class="n">methods</span><span class="p">[</span><span class="n">m</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_describe</span><span class="p">(</span><span class="n">m</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">methods</span><span class="o">=</span><span class="n">methods</span><span class="p">)</span>
<span class="nd">@expose</span><span class="p">(</span><span class="s1">'myappserver.templates.testform'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">testform</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">method</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="sd">'''</span>
<span class="sd"> Show a form with the parameters of an API method</span>
<span class="sd"> '''</span>
<span class="n">kw</span><span class="p">[</span><span class="s2">"method"</span><span class="p">]</span> <span class="o">=</span> <span class="n">method</span>
<span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">method</span><span class="o">=</span><span class="n">method</span><span class="p">,</span> <span class="n">action</span><span class="o">=</span><span class="s2">"/myapp/test/"</span><span class="o">+</span><span class="n">method</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="n">kw</span><span class="p">,</span> <span class="n">info</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_describe</span><span class="p">(</span><span class="n">method</span><span class="p">),</span> <span class="n">form</span><span class="o">=</span><span class="n">forms</span><span class="p">[</span><span class="n">method</span><span class="p">])</span>
<span class="nd">@expose</span><span class="p">(</span><span class="n">content_type</span><span class="o">=</span><span class="s2">"text/plain"</span><span class="p">)</span>
<span class="nd">@validate</span><span class="p">(</span><span class="n">validator_dispatcher</span><span class="p">,</span> <span class="n">error_handler</span><span class="o">=</span><span class="n">testform</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">method</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="sd">'''</span>
<span class="sd"> Run an API method and show its prettyprinted result</span>
<span class="sd"> '''</span>
<span class="n">res</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">method</span><span class="p">))(</span><span class="o">**</span><span class="n">kw</span><span class="p">)</span>
<span class="k">return</span> <span class="n">pprint</span><span class="o">.</span><span class="n">pformat</span><span class="p">(</span><span class="n">res</span><span class="p">)</span>
</pre></div>
<p>In a few lines, we have all we need: an index of the API methods (including
their documentation taken from the docstrings!), and for each method a form to
invoke it and a page to see the results.</p>
<p>Make the forms children of AjaxForm, and you can even see the results together
with the form.</p>
Passing values to turbogears widgets at display time (the general case)http://www.enricozini.org/blog/2008/tips/tg-params-to-widgets2009-06-05T22:57:39Z2009-06-05T22:57:39Z
<p><a href="http://www.enricozini.org/blog/2007/tips/passing-values-to-turbogears-widgets">Last time I dug this up</a> I
was not clear enough in documenting my findings, so I had to find them again.
Here is the second attempt.</p>
<p>In Turbogears, in order to pass parameters to arbitrary widgets in a compound
widget, the syntax is:</p>
<div class="codehilite"><pre><span></span><code><span class="n">form</span><span class="o">.</span><span class="n">display</span><span class="p">(</span><span class="n">PARAMNAME</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span><span class="n">WIDGETNAME</span><span class="o">=</span><span class="n">VALUE</span><span class="p">))</span>
</code></pre></div>
<p>And if you have more complex nested widgets and would like to know what goes
on, this monkey patch is good for inspecting the params lookup functions:</p>
<div class="codehilite"><pre><span></span><code><span class="kn">import</span> <span class="nn">turbogears.widgets.forms</span>
<span class="n">old_rpbp</span> <span class="o">=</span> <span class="n">turbogears</span><span class="o">.</span><span class="n">widgets</span><span class="o">.</span><span class="n">forms</span><span class="o">.</span><span class="n">retrieve_params_by_path</span>
<span class="k">def</span> <span class="nf">inspect_rpbp</span><span class="p">(</span><span class="n">params</span><span class="p">,</span> <span class="n">path</span><span class="p">):</span>
<span class="nb">print</span> <span class="s2">"RPBP"</span><span class="p">,</span> <span class="nb">repr</span><span class="p">(</span><span class="n">params</span><span class="p">),</span> <span class="nb">repr</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">old_rpbp</span><span class="p">(</span><span class="n">params</span><span class="p">,</span> <span class="n">path</span><span class="p">)</span>
<span class="nb">print</span> <span class="s2">"RPBP RES"</span><span class="p">,</span> <span class="n">res</span>
<span class="k">return</span> <span class="n">res</span>
<span class="n">turbogears</span><span class="o">.</span><span class="n">widgets</span><span class="o">.</span><span class="n">forms</span><span class="o">.</span><span class="n">retrieve_params_by_path</span> <span class="o">=</span> <span class="n">inspect_rpbp</span>
</code></pre></div>
<p>The code for the lookup itself is, as the name suggests, in the
<code>retrieve_params_by_path</code> function in the file <code>widgets/forms.py</code> in the
Turbogears source code.</p>
Linking to self in turbogearshttp://www.enricozini.org/blog/2007/tips/tg-linktoself2009-06-05T22:57:39Z2009-06-05T22:57:39Z
<p>I want to put in my master.kid some icons that allow to change the current
language for the session.</p>
<p>First, all user-accessible methods need to handle a 'language' parameter:</p>
<div class="codehilite"><pre><span></span>@expose(template="myapp.templates.foobar")
def index(self, someparam, **kw):
if 'language' in kw: turbogears.i18n.set_session_locale(kw['language'])
</pre></div>
<p>Then, we need a way to edit the current URL so that we can generate modified
links to self that preserve the existing path_info and query parameters. In
your main controller, add:</p>
<div class="codehilite"><pre><span></span>def linkself(**kw):
params = {}
params.update(cherrypy.request.params)
params.update(kw)
url = cherrypy.request.browser_url.split('?', 1)[0]
return url + '?' + '&'.join(['='.join(x) for x in params.iteritems()])
def add_custom_stdvars(vars):
return vars.update({"linkself": linkself})
turbogears.view.variable_providers.append(add_custom_stdvars)
</pre></div>
<p>(see <a href="http://docs.turbogears.org/1.0/stdvars">the turbogears stdvars
documentation</a> and <a href="http://www.cherrypy.org/wiki/RequestObject">the cherrypy
request documentation (cherrypy 2 documentation at the bottom of the
page)</a>)</p>
<p>And finally, in master.kid:</p>
<div class="codehilite"><pre><span></span><span class="nt"><div</span> <span class="na">id=</span><span class="s">"footer"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">id=</span><span class="s">"langselector"</span><span class="nt">></span>
<span class="nt"><span</span> <span class="na">class=</span><span class="s">"language"</span><span class="nt">></span>
<span class="nt"><a</span> <span class="na">href=</span><span class="s">"</span><span class="cp">${</span><span class="n">tg</span><span class="o">.</span><span class="n">linkself</span><span class="p">(</span><span class="n">language</span><span class="o">=</span><span class="s1">'it_IT'</span><span class="p">)</span><span class="cp">}</span><span class="s">"</span><span class="nt">></span>
<span class="nt"><img</span> <span class="na">src=</span><span class="s">"</span><span class="cp">${</span><span class="n">tg</span><span class="o">.</span><span class="n">url</span><span class="p">(</span><span class="s1">'/static/images/it.png'</span><span class="p">)</span><span class="cp">}</span><span class="s">"/</span><span class="nt">></span>
<span class="nt"></a></span>
<span class="nt"></span></span>
<span class="nt"><span</span> <span class="na">class=</span><span class="s">"language"</span><span class="nt">></span>
<span class="nt"><a</span> <span class="na">href=</span><span class="s">"</span><span class="cp">${</span><span class="n">tg</span><span class="o">.</span><span class="n">linkself</span><span class="p">(</span><span class="n">language</span><span class="o">=</span><span class="s1">'C'</span><span class="p">)</span><span class="cp">}</span><span class="s">"</span><span class="nt">></span>
<span class="nt"><img</span> <span class="na">src=</span><span class="s">"</span><span class="cp">${</span><span class="n">tg</span><span class="o">.</span><span class="n">url</span><span class="p">(</span><span class="s1">'/static/images/en.png'</span><span class="p">)</span><span class="cp">}</span><span class="s">"/</span><span class="nt">></span>
<span class="nt"></a></span>
<span class="nt"></span></span>
<span class="nt"></div></span><span class="c"><!-- langselector --></span>
<span class="nt"></div></span><span class="c"><!-- footer --></span>
</pre></div>
File downloads with TurboGearshttp://www.enricozini.org/blog/2007/tips/tg-file-download2009-06-05T22:57:39Z2009-06-05T22:57:39Z
<p>In TurboGears, I had to implement a file download method, but the file required
access controls so it was put in a directory not exported by Apache.</p>
<p>In <code>#turbogears</code> I've been pointed at:
<a href="http://cherrypy.org/wiki/FileDownload">http://cherrypy.org/wiki/FileDownload</a>
and this is everything put together:</p>
<div class="codehilite"><pre><span></span><span class="kn">from</span> <span class="nn">cherrypy.lib.cptools</span> <span class="kn">import</span> <span class="n">serveFile</span>
<span class="c1"># In cherrypy 3 it should be:</span>
<span class="c1">#from cherrypy.lib.static import serve_file</span>
<span class="nd">@expose</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="sd">"""Access the file pointed by the given path"""</span>
<span class="n">pathname</span> <span class="o">=</span> <span class="n">check_auth_and_compute_pathname</span><span class="p">()</span>
<span class="k">return</span> <span class="n">serveFile</span><span class="p">(</span><span class="n">pathname</span><span class="p">)</span>
</pre></div>
<p>Then I needed to export some CSV:</p>
<div class="codehilite"><pre><span></span>@expose()
def getcsv(self, *args, **kw):
"""Get the data in CSV format"""
rows = compute_data_rows()
headers = compute_headers(rows)
filename = compute_file_name()
cherrypy.response.headers['Content-Type'] = "application/x-download"
cherrypy.response.headers['Content-Disposition'] = 'attachment; filename="'+filename+'"'
csvdata = StringIO.StringIO()
writer = csv.writer(csvdata)
writer.writerow(headers)
writer.writerows(rows)
return csvdata.getvalue()
</pre></div>
<p>In my case it's not an issue as I can only compute the headers after I computed
all the data, but I still have to find out how to serve the CSV file while I'm
generating it, instead of storing it all into a big string and returning the
big string.</p>
Turbogears quirks when testing controllers that use SingleSelectFieldhttp://www.enricozini.org/blog/2007/tips/tg-more-singleselectfield-quirks2009-06-05T22:57:39Z2009-06-05T22:57:39Z
<p>Suppose you have a <code>User</code> that can be a member of a Company. In SQLObject you
model it somehow like this:</p>
<div class="codehilite"><pre><span></span> <span class="k">class</span> <span class="n">Company</span>(<span class="n">SQLObject</span>):
<span class="nb">name</span> = <span class="n">UnicodeCol</span>(<span class="n">length</span>=<span class="mi">16</span>, <span class="n">alternateID</span>=<span class="nb">True</span>, <span class="n">alternateMethodName</span>=<span class="s">"by_name"</span>)
<span class="n">display_name</span> = <span class="n">UnicodeCol</span>(<span class="n">length</span>=<span class="mi">255</span>)
<span class="k">class</span> <span class="n">User</span>(<span class="n">InheritableSQLObject</span>):
<span class="n">company</span> = <span class="n">ForeignKey</span>(<span class="s">"Company"</span>, <span class="n">notNull</span>=<span class="nb">False</span>, <span class="n">cascade</span>=<span class="s">'null'</span>)
</pre></div>
<p>Then you want to make a form that allows to choose what is the company of a
user:</p>
<div class="codehilite"><pre><span></span><span class="nx">def</span> <span class="nx">companies</span><span class="p">()</span><span class="o">:</span>
<span class="k">return</span> <span class="p">[</span> <span class="p">[</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="s1">'None'</span> <span class="p">]</span> <span class="p">]</span> <span class="o">+</span> <span class="p">[</span> <span class="p">[</span><span class="nx">c</span><span class="p">.</span><span class="nx">id</span><span class="p">,</span> <span class="nx">c</span><span class="p">.</span><span class="nx">display_name</span><span class="p">]</span> <span class="k">for</span> <span class="nx">c</span> <span class="k">in</span> <span class="nx">Company</span><span class="p">.</span><span class="nx">select</span><span class="p">()</span> <span class="p">]</span>
<span class="kr">class</span> <span class="nx">NewUserFields</span><span class="p">(</span><span class="nx">WidgetsList</span><span class="p">)</span><span class="o">:</span>
<span class="s2">"""Fields for editing general settings"""</span>
<span class="nx">user_name</span> <span class="o">=</span> <span class="nx">TextField</span><span class="p">(</span><span class="nx">label</span><span class="o">=</span><span class="s2">"User name"</span><span class="p">)</span>
<span class="nx">companyID</span> <span class="o">=</span> <span class="nx">SingleSelectField</span><span class="p">(</span><span class="nx">label</span><span class="o">=</span><span class="s2">"Company"</span><span class="p">,</span> <span class="nx">options</span><span class="o">=</span><span class="nx">companies</span><span class="p">)</span>
</pre></div>
<p>Ok. Now you want to run tests:</p>
<ol>
<li><code>nosetests</code> imports the controller to see if there's any initialisation code.</li>
<li>The NewUserFields class is created.</li>
<li>The SingleSelectField is created.</li>
<li>The SingleSelectField constructor tries to guess the validator and peeks at the first option.</li>
<li>This calls <code>companies</code>.</li>
<li><code>companies</code> accesses the database.</li>
<li>The testing database has not yet been created because nosetests imported the
module before giving the test code a chance to setup the test database.</li>
<li>Bang.</li>
</ol>
<p>The solution is to add an explicit validator to disable this guessing code that
is a source of so many troubles:</p>
<div class="codehilite"><pre><span></span><span class="kr">class</span> <span class="nx">NewUserFields</span><span class="p">(</span><span class="nx">WidgetsList</span><span class="p">)</span><span class="o">:</span>
<span class="s2">"""Fields for editing general settings"""</span>
<span class="nx">user_name</span> <span class="o">=</span> <span class="nx">TextField</span><span class="p">(</span><span class="nx">label</span><span class="o">=</span><span class="s2">"User name"</span><span class="p">)</span>
<span class="nx">companyID</span> <span class="o">=</span> <span class="nx">SingleSelectField</span><span class="p">(</span><span class="nx">label</span><span class="o">=</span><span class="s2">"Company"</span><span class="p">,</span> <span class="nx">options</span><span class="o">=</span><span class="nx">companies</span><span class="p">,</span> <span class="nx">validator</span><span class="o">=</span><span class="nx">v</span><span class="p">.</span><span class="nx">Int</span><span class="p">(</span><span class="nx">not_empty</span><span class="o">=</span><span class="nx">True</span><span class="p">))</span>
</pre></div>
TurboGears RemoteForm tiphttp://www.enricozini.org/blog/2007/tips/remoteform-weird-error2009-06-05T22:57:39Z2009-06-05T22:57:39Z
<p>In case your RemoteForm misteriously behaves like a normal HTTP form, refreshing
the page on submit, and the only hint that there's something wrong is this bit
in the Iceweasel's error console:</p>
<div class="codehilite"><pre><span></span><span class="n">Errore</span><span class="o">:</span> <span class="n">uncaught</span> <span class="n">exception</span><span class="o">:</span> <span class="o">[</span><span class="n">Exception</span><span class="o">...</span> <span class="s2">"Component returned failure</span>
<span class="s2">code: 0x80070057 (NS_ERROR_ILLEGAL_VALUE) [nsIXMLHttpRequest.open]"</span>
<span class="n">nsresult</span><span class="o">:</span> <span class="s2">"0x80070057 (NS_ERROR_ILLEGAL_VALUE)"</span> <span class="n">location</span><span class="o">:</span> <span class="s2">"JS frame ::</span>
<span class="s2">javascript: eval(__firebugTemp__); :: anonymous :: line 1"</span> <span class="n">data</span><span class="o">:</span> <span class="n">no</span><span class="o">]</span>
</pre></div>
<p>the problem can just be a missing <code>action=</code> attribute to the form.</p>
<p>I found out after:</p>
<ol>
<li>
<p>reading the <a href="http://trac.turbogears.org/wiki/RemoteForm?version=4">TurboGears remoteform
wiki</a>: "<em>For some
reason, the RemoteForm is acting like a regular html form, serving up a new
page instead of performing the replacements we're looking for. I'll update
this page as soon as I figure out why this is happening.</em>"</p>
</li>
<li>
<p>finding <a href="http://www.arnebrodowski.de/blog/352-0x80070057-NS_ERROR_ILLEGAL_VALUE.html">this page</a>
on Google and meditating for a while while staring at it. I don't speak
German, but often enough I manage to solve problems after meditating over
Google results in all sorts of languages unknown or unreadable to me. I
will call this practice <strong>Webomancy</strong>.</p>
</li>
</ol>
Turbogears i18n quirkshttp://www.enricozini.org/blog/2007/tips/tg-i18n-quirks2009-06-05T22:57:39Z2009-06-05T22:57:39Z
<h2>Collecting strings from <code>.kid</code> files</h2>
<p><code>tg-admin i18n collect</code> won't collect strings from your <code>.kid</code> files: you need
the toolbox web interface for that.</p>
<h2>Indentation problems in <code>.kid</code> files</h2>
<p>The toolbox web interface chokes on intentation errors on your <code>.kid</code> files.</p>
<p>To see the name of the <code>.kid</code> file that causes the error, look at the <code>tg-admin
toolbox</code> output in the terminal for lines like <code>Working on app/Foo/templates/bar.kid</code>.</p>
<p>What happens is that the <code>.kid</code> files are converted to python files, and if
there are indentation glitches they end up in the python files, and python will
complain.</p>
<p>Once you see from the <code>tg-admin toolbox</code> standard error what is the <code>.kid</code> file
with the problem, edit it and try to make sure that all closing tags are at the
exact indentation level as their coresponding opening tags. Even a single
space would matter.</p>
<h2>Bad i18n bug in TurboKid versions earlier than 1.0.1</h2>
<p><code>faide</code> on <code>#turbogears</code> also says:</p>
<blockquote>
<p>It is of outmost importance that you use TurboKid 1.0.1
because it is the first version that corrects a BIG bug
regarding i18n filters ...</p>
<p>The version below had a bug where the filters kept being
added at each page load in such a way that after a few
hundreds of pages you could have page loading times as
long as 5 minutes!</p>
</blockquote>
<p>If one has a previous version of TurboKid, one (and only one) of these is
needed:</p>
<ul>
<li>Patch Kid with the patch at <a href="http://www.kid-templating.org/trac/ticket/203">http://www.kid-templating.org/trac/ticket/203</a></li>
<li>Patch TurboKid with the patch at <a href="http://trac.turbogears.org/ticket/1301">http://trac.turbogears.org/ticket/1301</a></li>
<li>Upgrade to TurboGears 1.0.2.2, which also works like a charm with python
2.5, which is a performance boost.</li>
</ul>
<p>So, in short, all i18n users should upgrade to TurboGears 1.0.2.2 or patch
TurboKid using <a href="http://trac.turbogears.org/ticket/1301">http://trac.turbogears.org/ticket/1301</a>.</p>
Passing values to turbogears widgets at display timehttp://www.enricozini.org/blog/2007/tips/passing-values-to-turbogears-widgets2009-06-05T22:57:39Z2009-06-05T22:57:39Z
<p>In turbogears, I often need to pass data to widgets at display time. Sometimes
it works automatically, but sometimes, in cases like passing option lists to
CheckBoxLists or number of repetitions in a RepeatingFieldSet, it doesn't.</p>
<p>All the examples use precomputed lists or pass simple code functions. In most
of my cases, I want them computed by the controller every time.</p>
<p>Passing a function hasn't worked, as I did not find any obvious way to have the
function know about the controller.</p>
<p>So I need to pass things the display() method of the widgets, but I could not
work out how to pass the option list and default list for a CheckBoxList that
is part of a WidgetsList in a TableForm.</p>
<p>On IRC came the answer, thanks to Xentac:</p>
<div class="codehilite"><pre><span></span>you should be able to...
tableform.display(options=dict(checkboxname=[optionlist]))
</pre></div>
<p>And yes, it works. I can pass the default value as one of the normal form
values:</p>
<div class="codehilite"><pre><span></span> tableform.display(values=dict(checkboxname=[values]), options=dict(checkboxname=[optionlist]))
</pre></div>
Turbogears form quirkhttp://www.enricozini.org/blog/2007/tips/tg-quirk2009-06-05T22:57:39Z2009-06-05T22:57:39Z
<p>I had a great idea:</p>
<div class="codehilite"><pre><span></span>@validate(model_form)
@error_handler()
@expose(template='kid:myproject.templates.new')
def new(self, id, tg_errors=None, **kw):
"""Create new records in model"""
if tg_errors:
# Ask until there is still something missing
return dict(record = defaults, form = model_form)
else:
# We have everything: save it
i = Item(**kw)
flash("Item was successfully created.")
raise redirect("../show/%d" % i.id)
</pre></div>
<p>It was perfect: one simple method, simple error handling, nice helpful messages
all around. Except, check boxes and select fields would not get the default
values while all other fields would.</p>
<p>After two hours searching and cursing and tracing things into widget code, I
found this bit in <code>InputWidget.adjust_value</code>:</p>
<div class="codehilite"><pre><span></span># there are some input fields that when nothing is checked/selected
# instead of sending a nice name="" are totally missing from
# input_values, this little workaround let's us manage them nicely
# without interfering with other types of fields, we need this to
# keep track of their empty status otherwise if the form is going to be
# redisplayed for some errors they end up to use their defaults values
# instead of being empty since FE doesn't validate a failing Schema.
# posterity note: this is also why we need if_missing=None in
# validators.Schema, see ticket #696.
</pre></div>
<p>So, what is happening here is that since check boxes and option fields don't
have a nice behaviour when unselected, turbogears has to work around it. So in
order to detect the difference between "I selected 'None'" and "I didn't select
anything", it reasons that if the input has been validated, then the user has
made some selections, so it defaults to "The user selected 'None'". If the
input has not been validated, then we're showing the form for the first time,
then a missing value means "Use the default provided".</p>
<p>Since I was doing the validation all the time, this meant that Checkboxes and
Select fields would never use the default values.</p>
<p>Hence, if you use those fields then you necessarily need two different
controller methods, one to present the form and one to save it:</p>
<div class="codehilite"><pre><span></span>@expose(template='kid:myproject.templates.new')
def new(self, id, **kw):
"""Create new records in model"""
return dict(record = defaults(), form = model_form)
@validate(model_form)
@error_handler(new)
@expose()
def savenew(self, id, **kw):
"""Create new records in model"""
i = Item(**kw)
flash("Item was successfully created.")
raise redirect("../show/%d"%i.id)
</pre></div>
<p>If someone else stumbles on the same problem, I hope they'll find this post and
they won't have to spend another two awful hours tracking it down again.</p>
Quirks when overriding SQLObject settershttp://www.enricozini.org/blog/2007/tips/tg-override-sqlobject2009-06-05T22:57:39Z2009-06-05T22:57:39Z
<p>Let's suppose you have a <code>User</code> that is, optionally, a member of a Company. In
SQLObject you model it somehow like this:</p>
<div class="codehilite"><pre><span></span> <span class="k">class</span> <span class="n">Company</span>(<span class="n">SQLObject</span>):
<span class="nb">name</span> = <span class="n">UnicodeCol</span>(<span class="n">length</span>=<span class="mi">16</span>, <span class="n">alternateID</span>=<span class="nb">True</span>, <span class="n">alternateMethodName</span>=<span class="s">"by_name"</span>)
<span class="n">display_name</span> = <span class="n">UnicodeCol</span>(<span class="n">length</span>=<span class="mi">255</span>)
<span class="k">class</span> <span class="n">User</span>(<span class="n">InheritableSQLObject</span>):
<span class="n">company</span> = <span class="n">ForeignKey</span>(<span class="s">"Company"</span>, <span class="n">notNull</span>=<span class="nb">False</span>, <span class="n">cascade</span>=<span class="s">'null'</span>)
</pre></div>
<p>Then you want to implement a user settings interface that uses a Select box to
choose the company of the user.</p>
<p>For the Select widget to properly handle the validator for your data, you need
to put a number in the first option. As my first option, I want to have the
"None" entry, so I decided to use -1 to mean "None".</p>
<p>Now, to make it all blend nicely, I overrode the <code>company</code> setter to accept -1
and silently convert it to a <code>None</code>:</p>
<div class="codehilite"><pre><span></span> <span class="k">class</span> <span class="n">User</span>(<span class="n">InheritableSQLObject</span>):
<span class="n">company</span> = <span class="n">ForeignKey</span>(<span class="s">"Company"</span>, <span class="n">notNull</span>=<span class="nb">False</span>, <span class="n">cascade</span>=<span class="s">'null'</span>)
<span class="n">def</span> <span class="n">_set_company</span>(<span class="k">self</span>, <span class="n">id</span>):
<span class="s">"Set the company id, using None if -1 is given"</span>
<span class="k">if</span> <span class="n">id</span> == -<span class="mi">1</span>: <span class="n">id</span> = <span class="n">None</span>
<span class="k">self</span>.<span class="n">_SO_set_company</span>(<span class="n">id</span>)
</pre></div>
<p>In the controller, after parsing and validating all the various keyword
arguments, I do something like this:</p>
<div class="codehilite"><pre><span></span> user.set(**kw)
</pre></div>
<p>Now, the overridden method didn't get called.</p>
<p>After some investigation, and with the help of NandoFlorestan on IRC, we
figured out the following things:</p>
<ol>
<li>
<p>That method needs to be rewritten as <code>_set_companyID</code>:</p>
<div class="codehilite"><pre><span></span> def _set_companyID(self, id):
"Set the company id, using None if -1 is given"
if id == -1: id = None
self._SO_set_companyID(id)
</pre></div>
</li>
<li>
<p>Methods overridden in that way are alsop called by <code>user.set(**kw)</code>, but
not by the <code>User(**kw)</code> constructor, so using, for example, a similar
override to transparently encrypt passwords would give you plaintext
passwords for new users and encrypted passwords after they changed it.</p>
</li>
</ol>