This file is indexed.

/usr/share/pyshared/formless/annotate.py is in python-nevow 0.10.0-4build1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
# -*- test-case-name: formless.test -*-
# Copyright (c) 2004 Divmod.
# See LICENSE for details.


"""And the earth was without form, and void; and darkness was upon the face of the deep.
"""

import os
import sys
import inspect
import warnings
from zope.interface import implements
from zope.interface.interface import InterfaceClass, Attribute

from nevow import util


from formless import iformless


class count(object):
    def __init__(self):
        self.id = 0
    def next(self):
        self.id += 1
        return self.id

nextId = count().next


class InputError(Exception):
    """A Typed instance was unable to coerce from a string to the
    appropriate type.
    """
    def __init__(self, reason):
        self.reason = reason
    
    def __str__(self):
        return self.reason


class ValidateError(Exception):
    """A Binding instance was unable to coerce all it's arguments from a
    dictionary of lists of strings to the appropriate types.

    One use of this is to raise from an autocallable if an input is invalid.
    For example, a password is incorrect.
    
    errors must be a dictionary mapping argument names to error messages
    to display next to the arguments on the form.

    formErrorMessage is a string to display at the top of the form, not tied to
    any specific argument on the form.

    partialForm is a dict mapping argument name to argument value, allowing
    you to have the form save values that were already entered in the form.
    """
    def __init__(self, errors, formErrorMessage=None, partialForm=None):
        self.errors = errors
        self.formErrorMessage = formErrorMessage
        if partialForm is None:
            self.partialForm = {}
        else:
            self.partialForm = partialForm

    def __str__(self):
        return self.formErrorMessage



class Typed(Attribute):
    """A typed value. Subclasses of Typed are constructed inside of
    TypedInterface class definitions to describe the types of properties,
    the parameter types to method calls, and method return types.
    
    @ivar label: The short label which will describe this
        parameter/proerties purpose to the user.
    
    @ivar description: A long description which further describes the
        sort of input the user is expected to provide.
    
    @ivar default: A default value that may be used as an initial
        value in the form.

    @ivar required: Whether the user is required to provide a value

    @ivar null: The value which will be produced if required is False
        and the user does not provide a value

    @ivar unicode: Iff true, try to determine the character encoding
        of the data from the browser and pass unicode strings to
        coerce.
    """
    implements(iformless.ITyped)

    complexType = False
    strip = False
    label = None
    description = None
    default = ''
    required = False
    requiredFailMessage = 'Please enter a value'
    null = None
    unicode = False

    __name__ = ''

    def __init__(
        self,
        label=None,
        description=None,
        default=None,
        required=None,
        requiredFailMessage=None,
        null=None,
        unicode=None,
        **attributes):

        self.id = nextId()
        if label is not None:
            self.label = label
        if description is not None:
            self.description = description
        if default is not None:
            self.default = default
        if required is not None:
            self.required = required
        if requiredFailMessage is not None:
            self.requiredFailMessage = requiredFailMessage
        if null is not None:
            self.null = null
        if unicode is not None:
            self.unicode = unicode
        self.attributes = attributes

    def getAttribute(self, name, default=None):
        return self.attributes.get(name, default)

    def coerce(self, val, configurable):
        raise NotImplementedError, "Implement in %s" % util.qual(self.__class__)


#######################################
## External API; your code will create instances of these objects
#######################################

class String(Typed):
    """A string that is expected to be reasonably short and contain no
    newlines or tabs.

    strip: remove leading and trailing whitespace.
    """

    requiredFailMessage = 'Please enter a string.'
    # iff true, return the stripped value.
    strip = False

    def __init__(self, *args, **kwargs):
        try:
            self.strip = kwargs['strip']
            del kwargs['strip']
        except KeyError:
            pass
        Typed.__init__(self, *args, **kwargs)

    def coerce(self, val, configurable):
        if self.strip:
            val = val.strip()
        return val


class Text(String):
    """A string that is likely to be of a significant length and
    probably contain newlines and tabs.
    """


class Password(String):
    """Password is used when asking a user for a new password. The renderer
    user interface will possibly ask for the password multiple times to
    ensure it has been entered correctly. Typical use would be for
    registration of a new user."""
    requiredFailMessage = 'Please enter a password.'


class PasswordEntry(String):
    """PasswordEntry is used to ask for an existing password. Typical use
    would be for login to an existing account."""
    requiredFailMessage = 'Please enter a password.'


class FileUpload(Typed):
    requiredFailMessage = 'Please enter a file name.'

    def coerce(self, val, configurable):
        return val.filename


class Integer(Typed):

    requiredFailMessage = 'Please enter an integer.'

    def coerce(self, val, configurable):
        if val is None:
            return None
        try:
            return int(val)
        except ValueError:
            if sys.version_info < (2,3): # Long/Int aren't integrated
                try:
                    return long(val)
                except ValueError:
                    raise InputError("'%s' is not an integer." % val)
            
            raise InputError("'%s' is not an integer." % val)


class Real(Typed):

    requiredFailMessage = 'Please enter a real number.'

    def coerce(self, val, configurable):
        # TODO: This shouldn't be required; check.
        # val should never be None, but always a string.
        if val is None:
            return None
        try:
            return float(val)
        except ValueError:
            raise InputError("'%s' is not a real number." % val)


class Boolean(Typed):
    def coerce(self, val, configurable):
        if val == 'False':
            return False
        elif val == 'True':
            return True
        raise InputError("'%s' is not a boolean" % val)


class FixedDigitInteger(Integer):
    
    def __init__(self, digits = 1, *args, **kw):
        Integer.__init__(self, *args, **kw)
        self.digits = digits
        self.requiredFailMessage = \
            'Please enter a %d digit integer.' % self.digits

    def coerce(self, val, configurable):
        v = Integer.coerce(self, val, configurable)
        if len(str(v)) != self.digits:
            raise InputError("Number must be %s digits." % self.digits)
        return v


class Directory(Typed):
    
    requiredFailMessage = 'Please enter a directory name.'

    def coerce(self, val, configurable):
        # TODO: This shouldn't be required; check.
        # val should never be None, but always a string.
        if val is None:
            return None
        if not os.path.exists(val):
            raise InputError("The directory '%s' does not exist." % val)
        return val


class Choice(Typed):
    """Allow the user to pick from a list of "choices", presented in a drop-down
    menu. The elements of the list will be rendered by calling the function
    passed to stringify, which is by default "str".
    """

    requiredFailMessage = 'Please choose an option.'

    def __init__(self, choices=None, choicesAttribute=None, stringify=str,
                 valueToKey=str, keyToValue=None, keyAndConfigurableToValue=None,
                 *args, **kw):
        """
        Create a Choice.

        @param choices: an object adaptable to IGettable for an iterator (such
        as a function which takes (ctx, data) and returns a list, a list
        itself, a tuple, a generator...)

        @param stringify: a pretty-printer.  a function which takes an object
        in the list of choices and returns a label for it.

        @param valueToKey: a function which converts an object in the list of
        choices to a string that can be sent to a client.

        @param keyToValue: a 1-argument convenience version of
        keyAndConfigurableToValue

        @param keyAndConfigurableToValue:  a 2-argument function which takes a string such as
        one returned from valueToKey and a configurable, and returns an object
        such as one from the list of choices.
        """

        Typed.__init__(self, *args, **kw)
        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

        if keyAndConfigurableToValue is not None:
            assert keyToValue is None, 'This should be *obvious*'
            self.keyAndConfigurableToValue = keyAndConfigurableToValue
        elif keyToValue is not None:
            self.keyAndConfigurableToValue = lambda x,y: keyToValue(x)
        else:
            self.keyAndConfigurableToValue = lambda x,y: str(x)


    def coerce(self, val, configurable):
        """Coerce a value with the help of an object, which is the object
        we are configuring.
        """
        return self.keyAndConfigurableToValue(val, configurable)


class Radio(Choice):
    """Type influencing presentation! horray!

    Show the user radio button choices instead of a picklist.
    """


class Any(object):
    """Marker which indicates any object type.
    """


class Object(Typed):
    complexType = True
    def __init__(self, interface=Any, *args, **kw):
        Typed.__init__(self, *args, **kw)
        self.iface = interface

    def __repr__(self):
        if self.iface is not None:
            return "%s(interface=%s)" % (self.__class__.__name__, util.qual(self.iface))
        return "%s(None)" % (self.__class__.__name__,)



class List(Object):
    implements(iformless.IActionableType)

    complexType = True
    def __init__(self, actions=None, header='', footer='', separator='', *args, **kw):
        """Actions is a list of action methods which may be invoked on one
        or more of the elements of this list. Action methods are defined
        on a TypedInterface and declare that they take one parameter
        of type List. They do not declare themselves to be autocallable
        in the traditional manner. Instead, they are passed in the actions
        list of a list Property to declare that the action may be taken on
        one or more of the list elements.
        """
        if actions is None:
            actions = []
        self.actions = actions
        self.header = header
        self.footer = footer
        self.separator = separator
        Object.__init__(self, *args, **kw)

    def coerce(self, data, configurable):
        return data

    def __repr__(self):
        if self.iface is not None:
            return "%s(interface=%s)" % (self.__class__.__name__, util.qual(self.iface))
        return self.__class__.__name__ + "()"

    def attachActionBindings(self, possibleActions):
        ## Go through and replace self.actions, which is a list of method
        ## references, with the MethodBinding instance which holds 
        ## metadata about this function.
        act = self.actions
        for method, binding in possibleActions:
            if method in act:
                act[act.index(method)] = binding

    def getActionBindings(self):
        return self.actions

class Dictionary(List):
    pass


class Table(Object):
    pass


class Request(Typed):
    """Marker that indicates that an autocallable should be passed the
    request when called. Including a Request arg will not affect the
    appearance of the rendered form.

    >>> def doSomething(request=formless.Request(), name=formless.String()):
    ...     pass
    >>> doSomething = formless.autocallable(doSomething)
    """
    complexType = True ## Don't use the regular form


class Context(Typed):
    """Marker that indicates that an autocallable should be passed the
    context when called. Including a Context arg will not affect the
    appearance of the rendered form.

    >>> def doSomething(context=formless.Context(), name=formless.String()):
    ...     pass
    >>> doSomething = formless.autocallable(doSomething)
    """
    complexType = True ## Don't use the regular form


class Button(Typed):
    def coerce(self, data, configurable):
        return data


class Compound(Typed):
    complexType = True
    def __init__(self, elements=None, *args, **kw):
        assert elements, "What is the sound of a Compound type with no elements?"
        self.elements = elements
        Typed.__init__(self, *args, **kw)

    def __len__(self):
        return len(self.elements)

    def coerce(self, data, configurable):
        return data


class Method(Typed):
    def __init__(self, returnValue=None, arguments=(), *args, **kw):
        Typed.__init__(self, *args, **kw)
        self.returnValue = returnValue
        self.arguments = arguments


class Group(Object):
    pass


def autocallable(method, action=None, visible=False, **kw):
    """Describe a method in a TypedInterface as being callable through the
    UI. The "action" paramter will be used to label the action button, or the
    user interface element which performs the method call.
    
    Use this like a method adapter around a method in a TypedInterface:
    
    >>> class IFoo(TypedInterface):
    ...     def doSomething():
    ...         '''Do Something
    ...         
    ...         Do some action bla bla'''
    ...         return None
    ...     doSomething = autocallable(doSomething, action="Do it!!")
    """
    method.autocallable = True
    method.id = nextId()
    method.action = action
    method.attributes = kw
    return method


#######################################
## Internal API; formless uses these objects to keep track of
## what names are bound to what types
#######################################


class Binding(object):
    """Bindings bind a Typed instance to a name. When TypedInterface is subclassed,
    the metaclass looks through the dict looking for all properties and methods.
    
    If a properties is a Typed instance, a Property Binding is constructed, passing
    the name of the binding and the Typed instance.
    
    If a method has been wrapped with the "autocallable" function adapter,
    a Method Binding is constructed, passing the name of the binding and the
    Typed instance. Then, getargspec is called. For each keyword argument
    in the method definition, an Argument is constructed, passing the name
    of the keyword argument as the binding name, and the value of the
    keyword argument, a Typed instance, as the binding typeValue.
    
    One more thing. When an autocallable method is found, it is called with
    None as the self argument. The return value is passed the Method
    Binding when it is constructed to keep track of what the method is
    supposed to return.
    """
    implements(iformless.IBinding)

    label = None
    description = ''

    def __init__(self, name, typedValue, id=0):
        self.id = id
        self.name = name
        self.typedValue = iformless.ITyped(typedValue)

        # pull these out to remove one level of indirection...
        if typedValue.description is not None:
            self.description = typedValue.description
        if typedValue.label is not None:
            self.label = typedValue.label
        if self.label is None:
            self.label = nameToLabel(name)
        self.default = typedValue.default
        self.complexType = typedValue.complexType

    def __repr__(self):
        return "<%s %s=%s at 0x%x>" % (self.__class__.__name__, self.name, self.typedValue.__class__.__name__, id(self))

    def getArgs(self):
        """Return a *copy* of this Binding.
        """
        return (Binding(self.name, self.original, self.id), )

    def getViewName(self):
        return self.original.__class__.__name__.lower()

    def configure(self, boundTo, results):
        raise NotImplementedError, "Implement in %s" % util.qual(self.__class__)

    def coerce(self, val, configurable):
        if hasattr(self.original, 'coerce'):
            return self.original.coerce(val)
        return val

class Argument(Binding):
    pass


class Property(Binding):
    action = 'Change'
    def configure(self, boundTo, results):
        ## set the property!
        setattr(boundTo, self.name, results[self.name])


class MethodBinding(Binding):
    typedValue = None
    def __init__(self, name, typeValue, id=0, action="Call", attributes = {}):
        Binding.__init__(self, name, typeValue,  id)
        self.action = action
        self.arguments = typeValue.arguments
        self.returnValue = typeValue.returnValue
        self.attributes = attributes

    def getAttribute(self, name):
        return self.attributes.get(name, None)

    def configure(self, boundTo, results):
        bound = getattr(boundTo, self.name)
        return bound(**results)

    def getArgs(self):
        """Make sure each form post gets a unique copy of the argument list which it can use to keep
        track of values given in partially-filled forms
        """
        return self.typedValue.arguments[:]


class ElementBinding(Binding):
    """An ElementBinding binds a key to an element of a container.
    For example, ElementBinding('0', Object()) indicates the 0th element
    of a container of Objects. When this ElementBinding is bound to
    the list [1, 2, 3], resolving the binding will result in the 0th element,
    the object 1.
    """
    pass


class GroupBinding(Binding):
    """A GroupBinding is a way of naming a group of other Bindings.
    The typedValue of a GroupBinding should be a Configurable.
    The Bindings returned from this Configurable (usually a TypedInterface)
    will be rendered such that all fields must/may be filled out, and all
    fields will be changed at once upon form submission.
    """
    def __init__(self, name, typedValue, id=0):
        """Hack to prevent adaption to ITyped while the adapters are still
        being registered, because we know that the typedValue should be
        a Group when we are constructing a GroupBinding.
        """
        self.id = id
        self.name = name
        self.typedValue = Group(typedValue)

        # pull these out to remove one level of indirection...
        self.description = typedValue.description
        if typedValue.label:
            self.label = typedValue.label
        else:
            self.label = nameToLabel(name)
        self.default = typedValue.default
        self.complexType = typedValue.complexType

    def configure(self, boundTo, group):
        print "CONFIGURING GROUP BINDING", boundTo, group


def _sorter(x, y):
    return cmp(x.id, y.id)


class _Marker(object):
    pass


def caps(c):
    return c.upper() == c


def nameToLabel(mname):
    labelList = []
    word = ''
    lastWasUpper = False
    for letter in mname:
        if caps(letter) == lastWasUpper:
            # Continuing a word.
            word += letter
        else:
            # breaking a word OR beginning a word
            if lastWasUpper:
                # could be either
                if len(word) == 1:
                    # keep going
                    word += letter
                else:
                    # acronym
                    # we're processing the lowercase letter after the acronym-then-capital
                    lastWord = word[:-1]
                    firstLetter = word[-1]
                    labelList.append(lastWord)
                    word = firstLetter + letter
            else:
                # definitely breaking: lower to upper
                labelList.append(word)
                word = letter
        lastWasUpper = caps(letter)
    if labelList:
        labelList[0] = labelList[0].capitalize()
    else:
        return mname.capitalize()
    labelList.append(word)
    return ' '.join(labelList)


def labelAndDescriptionFromDocstring(docstring):
    if docstring is None:
        docstring = ''
    docs = filter(lambda x: x, [x.strip() for x in docstring.split('\n')])
    if len(docs) > 1:
        return docs[0], '\n'.join(docs[1:])
    else:
        return None, '\n'.join(docs)


_typedInterfaceMetadata = {}


class MetaTypedInterface(InterfaceClass):
    """The metaclass for TypedInterface. When TypedInterface is subclassed,
    this metaclass' __new__ method is invoked. The Typed Binding introspection
    described in the Binding docstring occurs, and when it is all done, there will
    be three attributes on the TypedInterface class:
    
     - __methods__: An ordered list of all the MethodBinding instances
       produced by introspecting all autocallable methods on this
       TypedInterface

     - __properties__: An ordered list of all the Property Binding
       instances produced by introspecting all properties which have
       Typed values on this TypedInterface

     - __spec__: An ordered list of all methods and properties
    
    These lists are sorted in the order that the methods and properties appear
    in the TypedInterface definition.
    
    For example:
    
    >>> class Foo(TypedInterface):
    ...     bar = String()
    ...     baz = Integer()
    ...     
    ...     def frotz(): pass
    ...     frotz = autocallable(frotz)
    ...     
    ...     xyzzy = Float()
    ...     
    ...     def blam(): pass
    ...     blam = autocallable(blam)

    Once the metaclass __new__ is done, the Foo class instance will have three
    properties, __methods__, __properties__, and __spec__,
    """
    __methods__ = property(lambda self: _typedInterfaceMetadata[self, '__methods__'])
    __id__ = property(lambda self: _typedInterfaceMetadata[self, '__id__'])
    __properties__ = property(lambda self: _typedInterfaceMetadata[self, '__properties__'])
    __spec__ = property(lambda self: _typedInterfaceMetadata[self, '__spec__'])
    name = property(lambda self: _typedInterfaceMetadata[self, 'name'])
    label = property(lambda self: _typedInterfaceMetadata[self, 'label'])
    description = property(lambda self: _typedInterfaceMetadata[self, 'description'])
    default = property(lambda self: _typedInterfaceMetadata.get((self, 'default'), 'DEFAULT'))
    complexType = property(lambda self: _typedInterfaceMetadata.get((self, 'complexType'), True))

    def __new__(cls, name, bases, dct):
        rv = cls = InterfaceClass.__new__(cls, name, bases, dct)
        _typedInterfaceMetadata[cls, '__id__'] = nextId()
        _typedInterfaceMetadata[cls, '__methods__'] = methods = []
        _typedInterfaceMetadata[cls, '__properties__'] = properties = []
        possibleActions = []
        actionAttachers = []
        for key, value in dct.items():
            if key[0] == '_': continue

            if isinstance(value, MetaTypedInterface):
                ## A Nested TypedInterface indicates a GroupBinding
                properties.append(GroupBinding(key, value, value.__id__))

                ## zope.interface doesn't like these
                del dct[key]
                setattr(cls, key, value)
            elif callable(value):
                names, _, _, typeList = inspect.getargspec(value)

                _testCallArgs = ()

                if typeList is None:
                    typeList = []

                if len(names) == len(typeList) + 1:
                    warnings.warn(
                        "TypeInterface method declarations should not have a 'self' parameter",
                        DeprecationWarning,
                        stacklevel=2)
                    del names[0]
                    _testCallArgs = (_Marker,)

                if len(names) != len(typeList):
                    ## Allow non-autocallable methods in the interface; ignore them
                    continue

                argumentTypes = [
                    Argument(n, argtype, argtype.id) for n, argtype in zip(names[-len(typeList):], typeList)
                ]

                result = value(*_testCallArgs)

                label = None
                description = None
                if getattr(value, 'autocallable', None):
                    # autocallables have attributes that can set label and description
                    label = value.attributes.get('label', None)
                    description = value.attributes.get('description', None)

                adapted = iformless.ITyped(result, None)
                if adapted is None:
                    adapted = Object(result)

                # ITyped has label and description we can use
                if label is None:
                    label = adapted.label
                if description is None:
                    description = adapted.description

                defaultLabel, defaultDescription = labelAndDescriptionFromDocstring(value.__doc__)
                if defaultLabel is None:
                    # docstring had no label, try the action if it is an autocallable
                    if getattr(value, 'autocallable', None):
                        if label is None and value.action is not None:
                            # no explicit label, but autocallable has action we can use
                            defaultLabel = value.action

                if defaultLabel is None:
                    # final fallback: use the function name as label
                    defaultLabel = nameToLabel(key)

                if label is None:
                    label = defaultLabel
                if description is None:
                    description = defaultDescription

                theMethod = Method(
                    adapted, argumentTypes, label=label, description=description
                )

                if getattr(value, 'autocallable', None):
                    methods.append(
                        MethodBinding(
                            key, theMethod, value.id, value.action, value.attributes))
                else:
                    possibleActions.append((value, MethodBinding(key, theMethod)))
            else:
                if not value.label:
                    value.label = nameToLabel(key)
                if iformless.IActionableType.providedBy(value):
                    actionAttachers.append(value)
                properties.append(
                    Property(key, value, value.id)
                )
        for attacher in actionAttachers:
            attacher.attachActionBindings(possibleActions)
        methods.sort(_sorter)
        properties.sort(_sorter)
        _typedInterfaceMetadata[cls, '__spec__'] = spec = methods + properties
        spec.sort(_sorter)
        _typedInterfaceMetadata[cls, 'name'] = name

        # because attributes "label" and "description" would become Properties,
        # check for ones with an underscore prefix.
        _typedInterfaceMetadata[cls, 'label'] = dct.get('_label', None)
        _typedInterfaceMetadata[cls, 'description'] = dct.get('_description', None)
        defaultLabel, defaultDescription = labelAndDescriptionFromDocstring(dct.get('__doc__'))
        if defaultLabel is None:
            defaultLabel = nameToLabel(name)
        if _typedInterfaceMetadata[cls, 'label'] is None:
            _typedInterfaceMetadata[cls, 'label'] = defaultLabel
        if _typedInterfaceMetadata[cls, 'description'] is None:
            _typedInterfaceMetadata[cls, 'description'] = defaultDescription

        return rv


#######################################
## External API; subclass this to create a TypedInterface
#######################################

TypedInterface = MetaTypedInterface('TypedInterface', (InterfaceClass('TypedInterface'), ), {})