Re: [Nevow-commits] r962 - One billion times better Choice...
I'm a little behind on Nevow changes, and I just came across this change to the Choice interface. It looks cool (deferred support is a big plus), but so far all the examples I've seen involve declaring the available choices as part of the interface definition, e.g.: class MyInterface(TypedInterface): pickOne = Choice(lambda c,d: [1,2,3,4]) What I do (fairly regularly) with my application involves declaring the available choices as part of the _implementing_ class, not as part of the interface. I typically used choicesAttribute to do this, and was working on something along the lines of a choicesMethod, but now it seems that this behavior has been deprecated. So, what is the new recommended syntax for a Choice where the implementing class can change the choices dynamically? On Thu, Dec 16, 2004 at 01:49:29PM -0500, Donovan Preston wrote:
MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8
Author: dp Date: Thu Dec 16 13:49:28 2004 New Revision: 962
Modified: trunk/examples/formbuilder.py trunk/examples/formpost2.py trunk/formless/annotate.py trunk/formless/webform.py Log: One billion times better Choice implementation; supports lazy choices from a function, a deferred, etc. Has valueToKey and keyToValue methods which are used to serialize/unserialize the choice instead of the index into the list.
Modified: trunk/examples/formbuilder.py ============================================================================== --- trunk/examples/formbuilder.py (original) +++ trunk/examples/formbuilder.py Thu Dec 16 13:49:28 2004 @@ -10,6 +10,9 @@ from formless import webform from formless import configurable
+from twisted.python import reflect + + class BuilderCore(configurable.Configurable): def __init__(self): configurable.Configurable.__init__(self, None) @@ -32,10 +35,11 @@
allTypes = [annotate.String, annotate.Text, annotate.Integer, annotate.Real, annotate.Password] +typeChoice = annotate.Choice(choices=allTypes, valueToKey=reflect.qual, keyToValue=reflect.namedAny, stringify=lambda x: x.__name__)
class IFormBuilder(annotate.TypedInterface): - def addElement(self, name=annotate.String(required=True), type=annotate.Choice(choices=allTypes)): + def addElement(self, name=annotate.String(required=True), type=typeChoice): """Add Element
Add an element to this form.
Modified: trunk/examples/formpost2.py ============================================================================== --- trunk/examples/formpost2.py (original) +++ trunk/examples/formpost2.py Thu Dec 16 13:49:28 2004 @@ -3,15 +3,24 @@ from nevow import loaders from nevow import rend from nevow import tags +from nevow import inevow
from formless import annotate from formless import webform
+from twisted.internet import defer + + +oldChoicesWay = annotate.Choice(choicesAttribute='theChoices') # Doing this gives you a DeprecationWarning now +# If you still want to use an attribute or method of some other object, you should use a function as shown below, +# but look up IResource(ctx) or IConfigurable(ctx), whichever is more appropriate. +newChoicesWay = annotate.Choice(lambda c, d: range(30)) +deferChoicesWay = annotate.Choice(lambda c, d: defer.succeed(['abcd', 'efgh', 'ijkl']))
class IMyForm(annotate.TypedInterface): foo = annotate.Integer()
- def bar(self, baz=annotate.Integer()): + def bar(self, baz=annotate.Integer(), bamf=oldChoicesWay, slam=newChoicesWay, ham=deferChoicesWay): pass bar = annotate.autocallable(bar)
@@ -21,8 +30,10 @@
foo = 5
- def bar(self, baz): - print "baz!", baz + def bar(self, baz, bamf, slam, ham): + return "You called bar! %s %s %s %s" % (baz, bamf, slam, ham) + + theChoices = [1, 2, 3]
class FormPage(rend.Page): @@ -31,12 +42,19 @@
child_webform_css = webform.defaultCSS
+ def render_hand(self, ctx, data): + hand = inevow.IHand(ctx, default=None) + if hand is not None: + return ctx.tag[hand] + return '' + docFactory = loaders.stan( tags.html[ tags.head[ tags.link(rel='stylesheet', type='text/css', href='/webform_css'), ], tags.body[ + tags.h3(render=render_hand, style="color: red; font-size: xx-large"), "Hello! Here is a form:",
# We want to render forms defined by the Implementation instance.
Modified: trunk/formless/annotate.py ============================================================================== --- trunk/formless/annotate.py (original) +++ trunk/formless/annotate.py Thu Dec 16 13:49:28 2004 @@ -10,6 +10,7 @@ import copy import inspect import types +import warnings
from nevow import inevow from nevow import util @@ -238,28 +239,29 @@ are configuring. The elements of the list will be rendered by calling the function passed to stringify, which is by default "str". """ - def __init__(self, choices=None, choicesAttribute=None, stringify=str, *args, **kw): + def __init__(self, choices=None, choicesAttribute=None, stringify=str, valueToKey=str, keyToValue=str, *args, **kw): Typed.__init__(self, *args, **kw) - if choices is None: - self.choices = [] - else: - self.choices = choices - self.choicesAttribute = choicesAttribute + self.choices = choices + if choicesAttribute: + self.choicesAttribute = choicesAttribute + if getattr(self, 'choicesAttribute', None): + warnings.warn( + "Choice.choicesAttribute is deprecated. Please pass a function to choices instead.", + DeprecationWarning, + stacklevel=2) + def findTheChoices(ctx, data): + return getattr(iformless.IConfigurable(ctx).original, self.choicesAttribute) + self.choices = findTheChoices + self.stringify = stringify + self.valueToKey=valueToKey + self.keyToValue=keyToValue
def coerce(self, val, binding): """Coerce a value with the help of an object, which is the object we are configuring. """ - try: - val = int(val) - except ValueError: - raise InputError("%r is an invalid choice." % val) - if self.choicesAttribute is not None: - choices = getattr(binding, self.choicesAttribute) - else: - choices = self.choices - return choices[val] + return self.keyToValue(val)
class Any(object):
Modified: trunk/formless/webform.py ============================================================================== --- trunk/formless/webform.py (original) +++ trunk/formless/webform.py Thu Dec 16 13:49:28 2004 @@ -62,6 +62,7 @@ def rend(self, context, data): defaults = context.locate(iformless.IFormDefaults) value = defaults.getDefault(context.key, context) + context.remember(data.typedValue, iformless.ITyped)
if data.typedValue.getAttribute('immutable'): inp = span(id=keyToXMLID(context.key))[value] @@ -145,32 +146,45 @@ _class='freeform-input-file')]
+class ICurrentlySelectedValue(compy.Interface): + """The currently-selected-value for the ITypedRenderer being rendered. + """ + + +csv = ICurrentlySelectedValue +def valToKey(c, d): + return iformless.ITyped(c).valueToKey(d) + + +def isSelected(c, d): + if csv(c) == valToKey(c, d): + return c.tag(selected='selected') + return c.tag + + +default_select = select(id=slot('id'), name=slot('name'), render=directive('sequence'), foo="bar")[ + option(pattern="item", + value=valToKey, + render=isSelected)[ + lambda c, d: iformless.ITyped(c).stringify(d)]] + + class ChoiceRenderer(BaseInputRenderer): def input(self, context, slot, data, name, value): tv = data.typedValue - if tv.choicesAttribute: - choices = getattr(context.locate(iformless.IConfigurable).boundTo, tv.choicesAttribute) - else: - choices = tv.choices + choices = tv.choices
- numChoices = len(choices) - if numChoices == 0: - return None + if value: + context.remember(self.original.valueToKey(value), csv) + else: + context.remember('', csv)
try: selector = context.tag.patternGenerator( 'selector' ) except NodeNotFound: - selector = select - - selector = selector(id=keyToXMLID(context.key), name=name) - stringify = tv.stringify + selector = default_select
- for index, val in enumerate(choices): - if val == value: - selector[option(value=str(index), selected="selected")[stringify(val)]] - else: - selector[option(value=str(index))[stringify(val)]] - return slot[selector] + return selector(data=choices)
class ObjectRenderer(compy.Adapter): @@ -259,6 +273,7 @@ class PropertyBindingRenderer(BaseBindingRenderer): def rend(self, context, data): context.remember(data, iformless.IBinding) + context.remember(data.typedValue, iformless.ITyped) typedRenderer = iformless.ITypedRenderer(data.typedValue, defaultBindingRenderer, persist=False) if typedRenderer.complexType: return invisible(data=data, render=typedRenderer) @@ -337,11 +352,13 @@ except NodeNotFound: default_content_pattern = freeformDefaultContentPattern content_pattern = default_content_pattern + renderer = iformless.ITypedRenderer( + argument.typedValue, defaultBindingRenderer, persist=False) pat = content_pattern( key=argument.name, data=argument, - render= iformless.ITypedRenderer( - argument.typedValue, defaultBindingRenderer, persist=False)) + render=renderer, + remember={iformless.ITyped: argument.typedValue}) context.fillSlots( 'argument!!%s' % argument.name, pat ) yield pat
_______________________________________________ Nevow-commits mailing list Nevow-commits@divmod.org http://divmod.org/users/mailman.twistd/listinfo/nevow-commits
-- Alex Levy WWW: http://mesozoic.geecs.org/ "Never let your sense of morals prevent you from doing what is right." -- Salvor Hardin, Isaac Asimov's _Foundation_
On Dec 21, 2004, at 7:30 AM, Alex Levy wrote:
What I do (fairly regularly) with my application involves declaring the available choices as part of the _implementing_ class, not as part of the interface. I typically used choicesAttribute to do this, and was working on something along the lines of a choicesMethod, but now it seems that this behavior has been deprecated.
Of course. The interface should never contain implementation code, but in this case the change just made using Choice so much more uniform and simple that it was obviously correct.
So, what is the new recommended syntax for a Choice where the implementing class can change the choices dynamically?
It really depends where you want to look up the method. If it's truly the *implementor* that you want, you can do exactly what the choicesAttribute backwards-compatibility code does: Choices(lambda ctx, data: getattr(iformless.IConfigurable(ctx).original, 'someAttribute')) Of course, you probably want to make using this a little nicer: def choiceMethod(name): return lambda ctx, data: getattr(iformless.IConfigurable(ctx).original, name) Also, perhaps you want to look for an already existing data method on your Page instead, to make your code more uniform: def dataMethod(name): return lambda ctx, data: inevow.IContainer(ctx).child(ctx, name) The point is instead of writing special cases in the actual Choice code, the programmer can plug in exactly what their case is. Oh, and it supports deferreds, too ;-) dp
Hi, I'm interested in using twisted web (based on what i've read - I can't get it to install on my machine yet), and had a question about the design. It seems that the app routes requests to nodes in the resource tree. Is it possible to apply processing to the requests before and after the resource is retrieved? In particular, can I specify that I want all the nodes on certain branches to have the same pre and post processing? I'm thinking of something along the lines of Java Servlet Filters. thanks.
On Dec 21, 2004, at 9:32 AM, Larry White wrote:
Hi, I'm interested in using twisted web (based on what i've read - I can't get it to install on my machine yet), and had a question about the design.
It seems that the app routes requests to nodes in the resource tree. Is it possible to apply processing to the requests before and after the resource is retrieved? In particular, can I specify that I want all the nodes on certain branches to have the same pre and post processing? I'm thinking of something along the lines of Java Servlet Filters.
It's pretty much possible to do anything you want with twisted.web, since it's python and the code is there and fairly short. However, there is currently no support for chaining processors like you describe here. It wouldn't be too hard to add, but you'd probably have to do some monkey patching on request.write to get what you want. I think foom has plans for piping output through multiple processors for web2. I'll let him talk about any relevance if he wants. dp
On Dec 21, 2004, at 12:32 PM, Larry White wrote:
Hi, I'm interested in using twisted web (based on what i've read - I can't get it to install on my machine yet), and had a question about the design.
It seems that the app routes requests to nodes in the resource tree. Is it possible to apply processing to the requests before and after the resource is retrieved? In particular, can I specify that I want all the nodes on certain branches to have the same pre and post processing? I'm thinking of something along the lines of Java Servlet Filters.
Before is easy: just insert a resource in the hierarchy that does its processing and then passes the request along to the real resource. After is pretty hard with the current architecture. I'd suggest treating it like it's impossible, although it may be doable with sufficient amounts of hackery. I'm doing some work to make this sort of thing easier in web2, but I'm not going to make any promises as to when that will be done. The particular use-case I'm working on at the moment is a output filter to add HTTP range support to any resource. James
participants (4)
-
Alex Levy
-
Donovan Preston
-
James Y Knight
-
Larry White