• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zopefoundation / z3c.form / 5104192483

pending completion
5104192483

push

github

web-flow
Config with pure python template (#114)

* Bumped version for breaking release.

* Drop support for Python 2.7, 3.5, 3.6.

* Add support for Python 3.11.

1316 of 1408 branches covered (93.47%)

Branch coverage included in aggregate %.

420 of 420 new or added lines in 39 files covered. (100.0%)

3716 of 3838 relevant lines covered (96.82%)

0.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.17
/src/z3c/form/widget.py
1
##############################################################################
2
#
3
# Copyright (c) 2007 Zope Foundation and Contributors.
4
# All Rights Reserved.
5
#
6
# This software is subject to the provisions of the Zope Public License,
7
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
8
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11
# FOR A PARTICULAR PURPOSE.
12
#
13
##############################################################################
14
"""Widget Framework Implementation."""
1✔
15
__docformat__ = "reStructuredText"
1✔
16

17
import zope.component
1✔
18
import zope.interface
1✔
19
import zope.location
1✔
20
import zope.schema.interfaces
1✔
21
from zope.browserpage.viewpagetemplatefile import ViewPageTemplateFile
1✔
22
from zope.i18n import translate
1✔
23
from zope.pagetemplate.interfaces import IPageTemplate
1✔
24
from zope.schema.fieldproperty import FieldProperty
1✔
25

26
from z3c.form import interfaces
1✔
27
from z3c.form import util
1✔
28
from z3c.form import value
1✔
29

30

31
PLACEHOLDER = object()
1✔
32

33
StaticWidgetAttribute = value.StaticValueCreator(
1✔
34
    discriminators=('context', 'request', 'view', 'field', 'widget')
35
)
36
ComputedWidgetAttribute = value.ComputedValueCreator(
1✔
37
    discriminators=('context', 'request', 'view', 'field', 'widget')
38
)
39

40

41
@zope.interface.implementer(interfaces.IWidget)
1✔
42
class Widget(zope.location.Location):
1✔
43
    """Widget base class."""
44

45
    # widget specific attributes
46
    name = FieldProperty(interfaces.IWidget['name'])
1✔
47
    label = FieldProperty(interfaces.IWidget['label'])
1✔
48
    mode = FieldProperty(interfaces.IWidget['mode'])
1✔
49
    required = FieldProperty(interfaces.IWidget['required'])
1✔
50
    error = FieldProperty(interfaces.IWidget['error'])
1✔
51
    value = FieldProperty(interfaces.IWidget['value'])
1✔
52
    template = None
1✔
53
    layout = None
1✔
54
    ignoreRequest = FieldProperty(interfaces.IWidget['ignoreRequest'])
1✔
55
    ignoreRequiredOnValidation = FieldProperty(
1✔
56
        interfaces.IWidget['ignoreRequiredOnValidation'])
57
    setErrors = FieldProperty(interfaces.IWidget['setErrors'])
1✔
58
    showDefault = FieldProperty(interfaces.IWidget['showDefault'])
1✔
59

60
    # The following attributes are for convenience. They are declared in
61
    # extensions to the simple widget.
62

63
    # See ``interfaces.IContextAware``
64
    context = None
1✔
65
    ignoreContext = False
1✔
66
    # See ``interfaces.IFormAware``
67
    form = None
1✔
68
    # See ``interfaces.IFieldWidget``
69
    field = None
1✔
70

71
    # Internal attributes
72
    _adapterValueAttributes = ('label', 'name', 'required', 'title')
1✔
73

74
    def __init__(self, request):
1✔
75
        self.request = request
1✔
76

77
    def update(self):
1✔
78
        """See z3c.form.interfaces.IWidget."""
79
        # Step 1: Determine the value.
80
        value = interfaces.NO_VALUE
1✔
81
        lookForDefault = False
1✔
82
        # Step 1.1: If possible, get a value from the request
83
        if not self.ignoreRequest:
1✔
84
            # at this turn we do not need errors to be set on widgets
85
            # errors will be set when extract gets called from form.extractData
86
            self.setErrors = False
1✔
87
            widget_value = self.extract()
1✔
88
            if widget_value is not interfaces.NO_VALUE:
1✔
89
                # Once we found the value in the request, it takes precendence
90
                # over everything and nothing else has to be done.
91
                self.value = widget_value
1✔
92
                value = PLACEHOLDER
1✔
93
        # Step 1.2: If we have a widget with a field and we have no value yet,
94
        #           we have some more possible locations to get the value
95
        if (interfaces.IFieldWidget.providedBy(self) and
1✔
96
            value is interfaces.NO_VALUE and
97
                value is not PLACEHOLDER):
98
            # Step 1.2.1: If the widget knows about its context and the
99
            #              context is to be used to extract a value, get
100
            #              it now via a data manager.
101
            if (interfaces.IContextAware.providedBy(self) and
1✔
102
                    not self.ignoreContext):
103
                value = zope.component.getMultiAdapter(
1✔
104
                    (self.context, self.field),
105
                    interfaces.IDataManager).query()
106
            # Step 1.2.2: If we still do not have a value, we can always use
107
            #             the default value of the field, if set
108
            # NOTE: It should check field.default is not missing_value, but
109
            # that requires fixing zope.schema first
110
            # We get a clone of the field with the context binded
111
            field = self.field.bind(self.context)
1✔
112

113
            if value is field.missing_value or value is interfaces.NO_VALUE:
1✔
114
                default_value = field.default
1✔
115
                if default_value is not None and self.showDefault:
1✔
116
                    value = field.default
1✔
117
                    lookForDefault = True
1✔
118

119
        # Step 1.3: If we still have not found a value, then we try to get it
120
        #           from an attribute value
121
        if ((value is interfaces.NO_VALUE or lookForDefault)
1✔
122
                and self.showDefault):
123
            adapter = zope.component.queryMultiAdapter(
1✔
124
                (self.context, self.request, self.form, self.field, self),
125
                interfaces.IValue, name='default')
126
            if adapter:
1✔
127
                value = adapter.get()
1✔
128
        # Step 1.4: Convert the value to one that the widget can understand
129
        if value not in (interfaces.NO_VALUE, PLACEHOLDER):
1✔
130
            converter = interfaces.IDataConverter(self)
1✔
131
            self.value = converter.toWidgetValue(value)
1✔
132
        # Step 2: Update selected attributes
133
        for attrName in self._adapterValueAttributes:
1✔
134
            # only allow to set values for known attributes
135
            if hasattr(self, attrName):
1✔
136
                value = zope.component.queryMultiAdapter(
1✔
137
                    (self.context, self.request, self.form, self.field, self),
138
                    interfaces.IValue, name=attrName)
139
                if value is not None:
1✔
140
                    setattr(self, attrName, value.get())
1✔
141

142
    def extract(self, default=interfaces.NO_VALUE):
1✔
143
        """See z3c.form.interfaces.IWidget."""
144
        return self.request.get(self.name, default)
1✔
145

146
    def render(self):
1✔
147
        """Render the plain widget without additional layout"""
148
        template = self.template
1✔
149
        if template is None:
1!
150
            template = zope.component.getMultiAdapter(
1✔
151
                (self.context, self.request, self.form, self.field, self),
152
                IPageTemplate, name=self.mode)
153
        return template(self)
1✔
154

155
    def json_data(self):
1✔
156
        return {
1✔
157
            'mode': self.mode,
158
            'error': self.error.message if self.error else '',
159
            'value': self.value,
160
            'required': self.required,
161
            'name': self.name,
162
            'id': getattr(self, 'id', ''),
163
            'type': 'text',
164
            'label': self.label or ''
165
        }
166

167
    def __call__(self):
1✔
168
        """Get and return layout template which is calling widget/render"""
169
        layout = self.layout
1✔
170
        if layout is None:
1!
171
            layout = zope.component.getMultiAdapter(
1✔
172
                (self.context, self.request, self.form, self.field, self),
173
                interfaces.IWidgetLayoutTemplate, name=self.mode)
174
        return layout(self)
1✔
175

176
    def __repr__(self):
1✔
177
        return '<{} {!r}>'.format(self.__class__.__name__, self.name)
1✔
178

179

180
@zope.interface.implementer(interfaces.ISequenceWidget)
1✔
181
class SequenceWidget(Widget):
1✔
182
    """Term based sequence widget base.
183

184
    The sequence widget is used for select items from a sequence. Don't get
185
    confused, this widget does support to choose one or more values from a
186
    sequence. The word sequence is not used for the schema field, it's used
187
    for the values where this widget can choose from.
188

189
    This widget base class is used for build single or sequence values based
190
    on a sequence which is in most use case a collection. e.g.
191
    IList of IChoice for sequence values or IChoice for single values.
192

193
    See also the MultiWidget for build sequence values based on none collection
194
    based values. e.g. IList of ITextLine
195
    """
196

197
    value = ()
1✔
198
    terms = None
1✔
199

200
    noValueToken = '--NOVALUE--'
1✔
201

202
    @property
1✔
203
    def displayValue(self):
1✔
204
        value = []
1✔
205
        for token in self.value:
1✔
206
            # Ignore no value entries. They are in the request only.
207
            if token == self.noValueToken:
1✔
208
                continue
1✔
209
            try:
1✔
210
                term = self.terms.getTermByToken(token)
1✔
211
            except LookupError:
1✔
212
                # silently ignore missing tokens, because INPUT_MODE and
213
                # HIDDEN_MODE does that too
214
                continue
1✔
215
            if zope.schema.interfaces.ITitledTokenizedTerm.providedBy(term):
1✔
216
                value.append(translate(
1✔
217
                    term.title, context=self.request, default=term.title))
218
            else:
219
                value.append(term.value)
1✔
220
        return value
1✔
221

222
    def updateTerms(self):
1✔
223
        if self.terms is None:
1✔
224
            self.terms = zope.component.getMultiAdapter(
1✔
225
                (self.context, self.request, self.form, self.field, self),
226
                interfaces.ITerms)
227
        return self.terms
1✔
228

229
    def update(self):
1✔
230
        """See z3c.form.interfaces.IWidget."""
231
        # Create terms first, since we need them for the generic update.
232
        self.updateTerms()
1✔
233
        super().update()
1✔
234

235
    def extract(self, default=interfaces.NO_VALUE):
1✔
236
        """See z3c.form.interfaces.IWidget."""
237
        if (self.name not in self.request and
1✔
238
                self.name + '-empty-marker' in self.request):
239
            return ()
1✔
240
        value = self.request.get(self.name, default)
1✔
241
        if value != default:
1✔
242
            if not isinstance(value, (tuple, list)):
1✔
243
                # this is here to make any single value a tuple
244
                value = (value,)
1✔
245
            if not isinstance(value, tuple):
1✔
246
                # this is here to make a non-tuple (just a list at this point?)
247
                # a tuple. the dance is about making return values uniform
248
                value = tuple(value)
1✔
249
            # do some kind of validation, at least only use existing values
250
            for token in value:
1✔
251
                if token == self.noValueToken:
1✔
252
                    continue
1✔
253
                try:
1✔
254
                    self.terms.getTermByToken(token)
1✔
255
                except LookupError:
1✔
256
                    return default
1✔
257
        return value
1✔
258

259
    def json_data(self):
1✔
260
        data = super().json_data()
1✔
261
        data['type'] = 'sequence'
1✔
262
        return data
1✔
263

264

265
@zope.interface.implementer(interfaces.IMultiWidget)
1✔
266
class MultiWidget(Widget):
1✔
267
    """None Term based sequence widget base.
268

269
    The multi widget is used for ITuple, IList or IDict if no other widget is
270
    defined.
271

272
    Some IList, ITuple or IDict are using another specialized widget if they
273
    can choose from a collection. e.g. a IList of IChoice. The base class of
274
    such widget is the ISequenceWidget.
275

276
    This widget can handle none collection based sequences and offers add or
277
    remove values to or from the sequence. Each sequence value get rendered by
278
    it's own relevant widget. e.g. IList of ITextLine or ITuple of IInt
279

280
    Each widget get rendered within a sequence value. This means each internal
281
    widget will represent one value from the multi widget value. Based on the
282
    nature of this (sub) widget setup the internal widget do not have a real
283
    context and can't get bound to it. This makes it impossible to use a
284
    sequence of collection where the collection needs a context. But that
285
    should not be a problem since sequence of collection will use the
286
    SequenceWidget as base.
287
    """
288

289
    allowAdding = True
1✔
290
    allowRemoving = True
1✔
291

292
    widgets = None
1✔
293
    key_widgets = None
1✔
294
    _value = None
1✔
295
    _widgets_updated = False
1✔
296

297
    _mode = FieldProperty(interfaces.IWidget['mode'])
1✔
298

299
    def __init__(self, request):
1✔
300
        super().__init__(request)
1✔
301
        self.widgets = []
1✔
302
        self.key_widgets = []
1✔
303
        self._value = []
1✔
304

305
    @property
1✔
306
    def is_dict(self):
1✔
307
        return getattr(self.field, 'key_type', None) is not None
1✔
308

309
    @property
1✔
310
    def counterName(self):
1✔
311
        return '%s.count' % self.name
1✔
312

313
    @property
1✔
314
    def counterMarker(self):
1✔
315
        # this get called in render from the template and contains always the
316
        # right amount of widgets we use.
317
        return '<input type="hidden" name="%s" value="%d" />' % (
1✔
318
            self.counterName, len(self.widgets))
319

320
    @property
1✔
321
    def mode(self):
1✔
322
        """This sets the subwidgets modes."""
323
        return self._mode
1✔
324

325
    @mode.setter
1✔
326
    def mode(self, mode):
1✔
327
        self._mode = mode
1✔
328
        # ensure that we apply the new mode to the widgets
329
        for w in self.widgets:
1✔
330
            w.mode = mode
1✔
331
        for w in self.key_widgets:
1✔
332
            if w is not None:
1!
333
                w.mode = mode
×
334

335
    def getWidget(self, idx, prefix=None, type_field="value_type"):
1✔
336
        """Setup widget based on index id with or without value."""
337
        valueType = getattr(self.field, type_field)
1✔
338
        widget = zope.component.getMultiAdapter(
1✔
339
            (valueType, self.request), interfaces.IFieldWidget)
340
        self.setName(widget, idx, prefix)
1✔
341
        widget.mode = self.mode
1✔
342
        # set widget.form (objectwidget needs this)
343
        if interfaces.IFormAware.providedBy(self):
1✔
344
            widget.form = self.form
1✔
345
            zope.interface.alsoProvides(
1✔
346
                widget, interfaces.IFormAware)
347
        widget.update()
1✔
348
        return widget
1✔
349

350
    def setName(self, widget, idx, prefix=None):
1✔
351
        def names(id): return [str(n)
1✔
352
                               for n in [id] + [prefix, idx] if n is not None]
353
        widget.name = '.'.join([str(self.name)] + names(None))
1✔
354
        widget.id = '-'.join([str(self.id)] + names(None))
1✔
355

356
    def appendAddingWidget(self):
1✔
357
        """Simply append a new empty widget with correct (counter) name."""
358
        # since we start with idx 0 (zero) we can use the len as next idx
359
        idx = len(self.widgets)
1✔
360
        widget = self.getWidget(idx)
1✔
361
        self.widgets.append(widget)
1✔
362
        if self.is_dict:
1✔
363
            widget = self.getWidget(idx, "key", "key_type")
1✔
364
            self.key_widgets.append(widget)
1✔
365
        else:
366
            self.key_widgets.append(None)
1✔
367

368
    def removeWidgets(self, names):
1✔
369
        """
370
        :param names: list of widget.name to remove from the value
371
        :return: None
372
        """
373
        zipped = list(zip(self.key_widgets, self.widgets))
1✔
374
        self.key_widgets = [k for k, v in zipped if v.name not in names]
1✔
375
        self.widgets = [v for k, v in zipped if v.name not in names]
1✔
376
        if self.is_dict:
1✔
377
            self.value = [(k.value, v.value)
1✔
378
                          for k, v in zip(self.key_widgets, self.widgets)]
379
        else:
380
            self.value = [widget.value for widget in self.widgets]
1✔
381

382
    def applyValue(self, widget, value=interfaces.NO_VALUE):
1✔
383
        """Validate and apply value to given widget.
384

385
        This method gets called on any multi widget value change and is
386
        responsible for validating the given value and setup an error message.
387

388
        This is internal apply value and validation process is needed because
389
        nothing outside this multi widget does know something about our
390
        internal sub widgets.
391
        """
392
        if value is not interfaces.NO_VALUE:
1!
393
            try:
1✔
394
                # convert widget value to field value
395
                converter = interfaces.IDataConverter(widget)
1✔
396
                fvalue = converter.toFieldValue(value)
1✔
397
                # validate field value
398
                zope.component.getMultiAdapter(
1✔
399
                    (self.context,
400
                     self.request,
401
                     self.form,
402
                     getattr(widget, 'field', None),
403
                     widget),
404
                    interfaces.IValidator).validate(fvalue)
405
                # convert field value to widget value
406
                widget.value = converter.toWidgetValue(fvalue)
1✔
407
            except (zope.schema.ValidationError, ValueError) as error:
1✔
408
                # on exception, setup the widget error message
409
                view = zope.component.getMultiAdapter(
1✔
410
                    (error, self.request, widget, widget.field,
411
                     self.form, self.context), interfaces.IErrorViewSnippet)
412
                view.update()
1✔
413
                widget.error = view
1✔
414
                # set the wrong value as value
415
                widget.value = value
1✔
416

417
    def updateWidgets(self):
1✔
418
        """Setup internal widgets based on the value_type for each value item.
419
        """
420
        oldLen = len(self.widgets)
1✔
421
        # Ensure at least min_length widgets are shown
422
        if (zope.schema.interfaces.IMinMaxLen.providedBy(self.field) and
1✔
423
            self.mode == interfaces.INPUT_MODE and self.allowAdding and
424
                oldLen < self.field.min_length):
425
            oldLen = self.field.min_length
1✔
426
        self.widgets = []
1✔
427
        self.key_widgets = []
1✔
428
        keys = set()
1✔
429
        idx = 0
1✔
430
        if self.value:
1✔
431
            if self.is_dict:
1✔
432
                # mainly sorting for testing reasons
433
                # and dict's have no order!, but
434
                # XXX: this should be done in the converter... here we get
435
                #      always strings as keys, sorting an str(int/date) is lame
436
                #      also, newly added item should be the last...
437
                try:
1✔
438
                    items = util.sortedNone(self.value)
1✔
439
                except BaseException:
×
440
                    # just in case it's impossible to sort don't fail
441
                    items = self.value
×
442
            else:
443
                items = zip([None] * len(self.value), self.value)
1✔
444
            for key, v in items:
1✔
445
                widget = self.getWidget(idx)
1✔
446
                self.applyValue(widget, v)
1✔
447
                self.widgets.append(widget)
1✔
448

449
                if self.is_dict:
1✔
450
                    # This is needed, since sequence widgets (such as for
451
                    # choices) return lists of values.
452
                    hash_key = key if not isinstance(key, list) else tuple(key)
1✔
453
                    widget = self.getWidget(idx, "key", "key_type")
1✔
454
                    self.applyValue(widget, key)
1✔
455
                    if hash_key in keys and widget.error is None:
1✔
456
                        error = zope.interface.Invalid('Duplicate key')
1✔
457
                        view = zope.component.getMultiAdapter(
1✔
458
                            (error, self.request, widget, widget.field,
459
                             self.form, self.context),
460
                            interfaces.IErrorViewSnippet)
461
                        view.update()
1✔
462
                        widget.error = view
1✔
463
                    self.key_widgets.append(widget)
1✔
464
                    keys.add(hash_key)
1✔
465
                else:
466
                    # makes the template easier to have this the same length
467
                    self.key_widgets.append(None)
1✔
468
                idx += 1
1✔
469
        missing = oldLen - len(self.widgets)
1✔
470
        if missing > 0:
1✔
471
            # add previous existing new added widgtes
472
            for i in range(missing):
1✔
473
                widget = self.getWidget(idx)
1✔
474
                self.widgets.append(widget)
1✔
475
                if self.is_dict:
1✔
476
                    widget = self.getWidget(idx, "key", "key_type")
1✔
477
                    self.key_widgets.append(widget)
1✔
478
                else:
479
                    self.key_widgets.append(None)
1✔
480
                idx += 1
1✔
481
        self._widgets_updated = True
1✔
482

483
    def updateAllowAddRemove(self):
1✔
484
        """Update the allowAdding/allowRemoving attributes
485
        basing on field constraints and current number of widgets
486
        """
487
        if not zope.schema.interfaces.IMinMaxLen.providedBy(self.field):
1✔
488
            return
1✔
489
        max_length = self.field.max_length
1✔
490
        min_length = self.field.min_length
1✔
491
        num_items = len(self.widgets)
1✔
492
        self.allowAdding = bool((not max_length) or (num_items < max_length))
1✔
493
        self.allowRemoving = bool(num_items and (num_items > min_length))
1✔
494

495
    @property
1✔
496
    def value(self):
1✔
497
        """This invokes updateWidgets on any value change.
498

499
        e. g. update/extract.
500
        """
501
        return self._value
1✔
502

503
    @value.setter
1✔
504
    def value(self, value):
1✔
505
        self._value = value
1✔
506
        # ensure that we apply our new values to the widgets
507
        self.updateWidgets()
1✔
508

509
    def update(self):
1✔
510
        """See z3c.form.interfaces.IWidget."""
511
        # Ensure that updateWidgets is called.
512
        super().update()
1✔
513
        if not self._widgets_updated:
1✔
514
            self.updateWidgets()
1✔
515

516
    def extract(self, default=interfaces.NO_VALUE):
1✔
517
        # This method is responsible to get the widgets value based on the
518
        # request and nothing else.
519
        # We have to setup the widgets for extract their values, because we
520
        # don't know how to do this for every field without the right widgets.
521
        # Later we will setup the widgets based on this values. This is needed
522
        # because we probably set a new value in the form for our multi widget
523
        # which would generate a different set of widgets.
524
        if self.request.get(self.counterName) is None:
1✔
525
            # counter marker not found
526
            return interfaces.NO_VALUE
1✔
527
        counter = int(self.request.get(self.counterName, 0))
1✔
528
        # extract value for existing widgets
529
        values = []
1✔
530
        append = values.append
1✔
531
        # extract value for existing widgets
532
        for idx in range(counter):
1✔
533
            widget = self.getWidget(idx)
1✔
534
            if self.is_dict:
1✔
535
                key_widget = self.getWidget(idx, "key", "key_type")
1✔
536
                append((key_widget.value, widget.value))
1✔
537
            else:
538
                append(widget.value)
1✔
539
        return values
1✔
540

541
    def json_data(self):
1✔
542
        data = super().json_data()
1✔
543
        data['widgets'] = [widget.json_data() for widget in self.widgets]
1✔
544
        data['type'] = 'multi'
1✔
545
        return data
1✔
546

547

548
def FieldWidget(field, widget):
1✔
549
    """Set the field for the widget."""
550
    widget.field = field
1✔
551
    if not interfaces.IFieldWidget.providedBy(widget):
1!
552
        zope.interface.alsoProvides(widget, interfaces.IFieldWidget)
1✔
553
    # Initial values are set. They can be overridden while updating the widget
554
    # itself later on.
555
    widget.name = field.__name__
1✔
556
    widget.id = field.__name__.replace('.', '-')
1✔
557
    widget.label = field.title
1✔
558
    widget.required = field.required
1✔
559
    return widget
1✔
560

561

562
class WidgetTemplateFactory:
1✔
563
    """Widget template factory."""
564

565
    def __init__(self, filename, contentType='text/html',
1✔
566
                 context=None, request=None, view=None,
567
                 field=None, widget=None):
568
        self.template = ViewPageTemplateFile(
1✔
569
            filename, content_type=contentType)
570
        zope.component.adapter(
1✔
571
            util.getSpecification(context),
572
            util.getSpecification(request),
573
            util.getSpecification(view),
574
            util.getSpecification(field),
575
            util.getSpecification(widget))(self)
576
        zope.interface.implementer(IPageTemplate)(self)
1✔
577

578
    def __call__(self, context, request, view, field, widget):
1✔
579
        return self.template
1✔
580

581

582
class WidgetLayoutFactory:
1✔
583
    """Widget layout template factory."""
584

585
    def __init__(self, filename, contentType='text/html',
1✔
586
                 context=None, request=None, view=None,
587
                 field=None, widget=None):
588
        self.template = ViewPageTemplateFile(
1✔
589
            filename, content_type=contentType)
590
        zope.component.adapter(
1✔
591
            util.getSpecification(context),
592
            util.getSpecification(request),
593
            util.getSpecification(view),
594
            util.getSpecification(field),
595
            util.getSpecification(widget))(self)
596
        zope.interface.implementer(interfaces.IWidgetLayoutTemplate)(self)
1✔
597

598
    def __call__(self, context, request, view, field, widget):
1✔
599
        return self.template
1✔
600

601

602
@zope.interface.implementer(interfaces.IWidgetEvent)
1✔
603
class WidgetEvent:
1✔
604

605
    def __init__(self, widget):
1✔
606
        self.widget = widget
1✔
607

608
    def __repr__(self):
1✔
609
        return '<{} {!r}>'.format(self.__class__.__name__, self.widget)
1✔
610

611

612
@zope.interface.implementer_only(interfaces.IAfterWidgetUpdateEvent)
1✔
613
class AfterWidgetUpdateEvent(WidgetEvent):
1✔
614
    pass
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc