[Twisted-web] Re: [Nevow-commits] r962 - One billion times better Choice...

Alex Levy mesozoic at polynode.com
Tue Dec 21 08:30:14 MST 2004


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 at 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_



More information about the Twisted-web mailing list