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

zopefoundation / zope.component / 3829079989

pending completion
3829079989

push

github

Jens Vagelpohl
- prepare release 5.1.0

467 of 489 branches covered (95.5%)

Branch coverage included in aggregate %.

4620 of 4625 relevant lines covered (99.89%)

1.0 hits per line

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

98.15
/src/zope/component/zcml.py
1
##############################################################################
2
#
3
# Copyright (c) 2005 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
"""Component Architecture configuration handlers
1✔
15
"""
16
from zope.configuration.exceptions import ConfigurationError
1✔
17
from zope.configuration.fields import Bool
1✔
18
from zope.configuration.fields import GlobalInterface
1✔
19
from zope.configuration.fields import GlobalObject
1✔
20
from zope.configuration.fields import PythonIdentifier
1✔
21
from zope.configuration.fields import Tokens
1✔
22
from zope.i18nmessageid import MessageFactory
1✔
23
from zope.interface import Interface
1✔
24
from zope.interface import implementedBy
1✔
25
from zope.interface import providedBy
1✔
26
from zope.schema import TextLine
1✔
27

28
from zope.component._api import getSiteManager
1✔
29
from zope.component._compat import ZOPE_SECURITY_NOT_AVAILABLE_EX
1✔
30
from zope.component._declaration import adaptedBy, getName
1✔
31
from zope.component.interface import provideInterface
1✔
32

33
try:
1✔
34
    from zope.security.zcml import Permission
1✔
35
except ZOPE_SECURITY_NOT_AVAILABLE_EX: # pragma: no cover
36
    def _no_security(*args, **kw):
37
        raise ConfigurationError(
38
            "security proxied components are not "
39
            "supported because zope.security is not available")
40
    _checker = proxify = protectedFactory = security = _no_security
41
    Permission = TextLine
42
else:
43
    from zope.component.security import _checker
1✔
44
    from zope.component.security import proxify
1✔
45
    from zope.component.security import protectedFactory
1✔
46
    from zope.component.security import securityAdapterFactory
1✔
47

48
_ = MessageFactory('zope')
1✔
49

50
class ComponentConfigurationError(ValueError, ConfigurationError):
1✔
51
    pass
1✔
52

53
def handler(methodName, *args, **kwargs):
1✔
54
    method = getattr(getSiteManager(), methodName)
1✔
55
    method(*args, **kwargs)
1✔
56

57
class IBasicComponentInformation(Interface):
1✔
58

59
    component = GlobalObject(
1✔
60
        title=_("Component to use"),
61
        description=_("Python name of the implementation object.  This"
62
                      " must identify an object in a module using the"
63
                      " full dotted name.  If specified, the"
64
                      " ``factory`` field must be left blank."),
65
        required=False,
66
        )
67

68
    permission = Permission(
1✔
69
        title=_("Permission"),
70
        description=_("Permission required to use this component."),
71
        required=False,
72
        )
73

74
    factory = GlobalObject(
1✔
75
        title=_("Factory"),
76
        description=_("Python name of a factory which can create the"
77
                      " implementation object.  This must identify an"
78
                      " object in a module using the full dotted name."
79
                      " If specified, the ``component`` field must"
80
                      " be left blank."),
81
        required=False,
82
        )
83

84
class IAdapterDirective(Interface):
1✔
85
    """
86
    Register an adapter
87
    """
88

89
    factory = Tokens(
1✔
90
        title=_("Adapter factory/factories"),
91
        description=_("A list of factories (usually just one) that create"
92
                      " the adapter instance."),
93
        required=True,
94
        value_type=GlobalObject()
95
        )
96

97
    provides = GlobalInterface(
1✔
98
        title=_("Interface the component provides"),
99
        description=_("This attribute specifies the interface the adapter"
100
                      " instance must provide."),
101
        required=False,
102
        )
103

104
    for_ = Tokens(
1✔
105
        title=_("Specifications to be adapted"),
106
        description=_("This should be a list of interfaces or classes"),
107
        required=False,
108
        value_type=GlobalObject(
109
            missing_value=object(),
110
        ),
111
    )
112

113
    permission = Permission(
1✔
114
        title=_("Permission"),
115
        description=_("This adapter is only available, if the principal"
116
                      " has this permission."),
117
        required=False,
118
        )
119

120
    name = TextLine(
1✔
121
        title=_("Name"),
122
        description=_("Adapters can have names.\n\n"
123
                      "This attribute allows you to specify the name for"
124
                      " this adapter."),
125
        required=False,
126
        )
127

128
    trusted = Bool(
1✔
129
        title=_("Trusted"),
130
        description=_("""Make the adapter a trusted adapter
131

132
        Trusted adapters have unfettered access to the objects they
133
        adapt.  If asked to adapt security-proxied objects, then,
134
        rather than getting an unproxied adapter of security-proxied
135
        objects, you get a security-proxied adapter of unproxied
136
        objects.
137
        """),
138
        required=False,
139
        default=False,
140
        )
141

142
    locate = Bool(
1✔
143
        title=_("Locate"),
144
        description=_("""Make the adapter a locatable adapter
145

146
        Located adapter should be used if a non-public permission
147
        is used.
148
        """),
149
        required=False,
150
        default=False,
151
        )
152

153
def _rolledUpFactory(factories):
1✔
154
    # This has to be named 'factory', aparently, so as not to confuse
155
    # apidoc :(
156
    def factory(ob):
1✔
157
        for f in factories:
1✔
158
            ob = f(ob)
1✔
159
        return ob
1✔
160
    # Store the original factory for documentation
161
    factory.factory = factories[0]
1✔
162
    return factory
1✔
163

164
def adapter(_context, factory, provides=None, for_=None, permission=None,
1✔
165
            name='', trusted=False, locate=False):
166

167
    if for_ is None:
1✔
168
        if len(factory) == 1:
1!
169
            for_ = adaptedBy(factory[0])
1✔
170

171
        if for_ is None:
1✔
172
            raise TypeError("No for attribute was provided and can't "
1✔
173
                            "determine what the factory adapts.")
174

175
    for_ = tuple(for_)
1✔
176

177
    if provides is None:
1✔
178
        if len(factory) == 1:
1!
179
            p = list(implementedBy(factory[0]))
1✔
180
            if len(p) == 1:
1✔
181
                provides = p[0]
1✔
182

183
        if provides is None:
1✔
184
            raise TypeError("Missing 'provides' attribute")
1✔
185

186
    if name == '':
1✔
187
        if len(factory) == 1:
1✔
188
            name = getName(factory[0])
1✔
189

190
    # Generate a single factory from multiple factories:
191
    factories = factory
1✔
192
    if len(factories) == 1:
1✔
193
        factory = factories[0]
1✔
194
    elif len(factories) < 1:
1✔
195
        raise ComponentConfigurationError("No factory specified")
1✔
196
    elif len(factories) > 1 and len(for_) != 1:
1✔
197
        raise ComponentConfigurationError(
1✔
198
            "Can't use multiple factories and multiple for")
199
    else:
200
        factory = _rolledUpFactory(factories)
1✔
201

202
    if permission is not None:
1✔
203
        factory = protectedFactory(factory, provides, permission)
1✔
204

205
    # invoke custom adapter factories
206
    if locate or permission is not None or trusted:
1✔
207
        factory = securityAdapterFactory(factory, permission, locate, trusted)
1✔
208

209
    _context.action(
1✔
210
        discriminator = ('adapter', for_, provides, name),
211
        callable = handler,
212
        args = ('registerAdapter',
213
                factory, for_, provides, name, _context.info),
214
        )
215
    _context.action(
1✔
216
        discriminator = None,
217
        callable = provideInterface,
218
        args = ('', provides)
219
               )
220
    if for_:
1✔
221
        for iface in for_:
1✔
222
            if iface is not None:
1!
223
                _context.action(
1✔
224
                    discriminator = None,
225
                    callable = provideInterface,
226
                    args = ('', iface)
227
                    )
228

229
class ISubscriberDirective(Interface):
1✔
230
    """
231
    Register a subscriber
232
    """
233

234
    factory = GlobalObject(
1✔
235
        title=_("Subscriber factory"),
236
        description=_("A factory used to create the subscriber instance."),
237
        required=False,
238
        )
239

240
    handler = GlobalObject(
1✔
241
        title=_("Handler"),
242
        description=_("A callable object that handles events."),
243
        required=False,
244
        )
245

246
    provides = GlobalInterface(
1✔
247
        title=_("Interface the component provides"),
248
        description=_("This attribute specifies the interface the adapter"
249
                      " instance must provide."),
250
        required=False,
251
        )
252

253
    for_ = Tokens(
1✔
254
        title=_("Interfaces or classes that this subscriber depends on"),
255
        description=_("This should be a list of interfaces or classes"),
256
        required=False,
257
        value_type=GlobalObject(
258
          missing_value = object(),
259
          ),
260
        )
261

262
    permission = Permission(
1✔
263
        title=_("Permission"),
264
        description=_("This subscriber is only available, if the"
265
                      " principal has this permission."),
266
        required=False,
267
        )
268

269
    trusted = Bool(
1✔
270
        title=_("Trusted"),
271
        description=_("""Make the subscriber a trusted subscriber
272

273
        Trusted subscribers have unfettered access to the objects they
274
        adapt.  If asked to adapt security-proxied objects, then,
275
        rather than getting an unproxied subscriber of security-proxied
276
        objects, you get a security-proxied subscriber of unproxied
277
        objects.
278
        """),
279
        required=False,
280
        default=False,
281
        )
282

283
    locate = Bool(
1✔
284
        title=_("Locate"),
285
        description=_("""Make the subscriber a locatable subscriber
286

287
        Located subscribers should be used if a non-public permission
288
        is used.
289
        """),
290
        required=False,
291
        default=False,
292
        )
293

294
_handler = handler
1✔
295
def subscriber(_context, for_=None, factory=None, handler=None, provides=None,
1✔
296
               permission=None, trusted=False, locate=False):
297
    if factory is None:
1✔
298
        if handler is None:
1✔
299
            raise TypeError("No factory or handler provided")
1✔
300
        if provides is not None:
1✔
301
            raise TypeError("Cannot use handler with provides")
1✔
302
        factory = handler
1✔
303
    else:
304
        if handler is not None:
1✔
305
            raise TypeError("Cannot use handler with factory")
1✔
306
        if provides is None:
1✔
307
            p = list(implementedBy(factory))
1✔
308
            if len(p) == 1:
1✔
309
                provides = p[0]
1✔
310
        if provides is None:
1✔
311
            raise TypeError(
1✔
312
                "You must specify a provided interface when registering "
313
                "a factory")
314

315
    if for_ is None:
1✔
316
        for_ = adaptedBy(factory)
1✔
317
        if for_ is None:
1✔
318
            raise TypeError("No for attribute was provided and can't "
1✔
319
                            "determine what the factory (or handler) adapts.")
320

321
    if permission is not None:
1✔
322
        factory = protectedFactory(factory, provides, permission)
1✔
323

324
    for_ = tuple(for_)
1✔
325

326
    # invoke custom adapter factories
327
    if locate or permission is not None or trusted:
1✔
328
        factory = securityAdapterFactory(factory, permission, locate, trusted)
1✔
329

330
    if handler is not None:
1✔
331
        _context.action(
1✔
332
            discriminator = None,
333
            callable = _handler,
334
            args = ('registerHandler',
335
                    handler, for_, u'', _context.info),
336
            )
337
    else:
338
        _context.action(
1✔
339
            discriminator = None,
340
            callable = _handler,
341
            args = ('registerSubscriptionAdapter',
342
                    factory, for_, provides, u'', _context.info),
343
            )
344

345
    if provides is not None:
1✔
346
        _context.action(
1✔
347
            discriminator = None,
348
            callable = provideInterface,
349
            args = ('', provides)
350
            )
351

352
    # For each interface, state that the adapter provides that interface.
353
    for iface in for_:
1✔
354
        if iface is not None:
1!
355
            _context.action(
1✔
356
                discriminator = None,
357
                callable = provideInterface,
358
                args = ('', iface)
359
                )
360

361
class IUtilityDirective(IBasicComponentInformation):
1✔
362
    """Register a utility."""
363

364
    provides = GlobalInterface(
1✔
365
        title=_("Provided interface"),
366
        description=_("Interface provided by the utility."),
367
        required=False,
368
        )
369

370
    name = TextLine(
1✔
371
        title=_("Name"),
372
        description=_("Name of the registration.  This is used by"
373
                      " application code when locating a utility."),
374
        required=False,
375
        )
376

377
def utility(_context, provides=None, component=None, factory=None,
1✔
378
            permission=None, name=''):
379
    if factory and component:
1✔
380
        raise TypeError("Can't specify factory and component.")
1✔
381

382
    if provides is None:
1✔
383
        if factory:
1✔
384
            provides = list(implementedBy(factory))
1✔
385
        else:
386
            provides = list(providedBy(component))
1✔
387
        if len(provides) == 1:
1✔
388
            provides = provides[0]
1✔
389
        else:
390
            raise TypeError("Missing 'provides' attribute")
1✔
391

392
    if name == '':
1✔
393
        if factory:
1✔
394
            name = getName(factory)
1✔
395
        else:
396
            name = getName(component)
1✔
397

398
    if permission is not None:
1✔
399
        if component:
1✔
400
            component = proxify(component, provides=provides,
1✔
401
                                permission=permission)
402
        if factory:
1✔
403
            factory = protectedFactory(factory, provides, permission)
1✔
404

405
    _context.action(
1✔
406
        discriminator = ('utility', provides, name),
407
        callable = handler,
408
        args = ('registerUtility', component, provides, name, _context.info),
409
        kw = dict(factory=factory),
410
        )
411
    _context.action(
1✔
412
        discriminator = None,
413
        callable = provideInterface,
414
        args = ('', provides),
415
        )
416

417
class IInterfaceDirective(Interface):
1✔
418
    """
419
    Define an interface
420
    """
421

422
    interface = GlobalInterface(
1✔
423
        title=_("Interface"),
424
        required=True,
425
        )
426

427
    type = GlobalInterface(
1✔
428
        title=_("Interface type"),
429
        required=False,
430
        )
431

432
    name = TextLine(
1✔
433
        title=_("Name"),
434
        required=False,
435
        )
436

437
def interface(_context, interface, type=None, name=''):
1✔
438
    _context.action(
1✔
439
        discriminator = None,
440
        callable = provideInterface,
441
        args = (name, interface, type)
442
        )
443

444
class IBasicViewInformation(Interface):
1✔
445
    """This is the basic information for all views."""
446

447
    for_ = Tokens(
1✔
448
        title=_("Specifications of the objects to be viewed"),
449
        description=_("""This should be a list of interfaces or classes
450
        """),
451
        required=True,
452
        value_type=GlobalObject(
453
          missing_value=object(),
454
          ),
455
        )
456

457
    permission = Permission(
1✔
458
        title=_("Permission"),
459
        description=_("The permission needed to use the view."),
460
        required=False,
461
        )
462

463
    class_ = GlobalObject(
1✔
464
        title=_("Class"),
465
        description=_("A class that provides attributes used by the view."),
466
        required=False,
467
        )
468

469
    allowed_interface = Tokens(
1✔
470
        title=_("Interface that is also allowed if user has permission."),
471
        description=_("""
472
        By default, 'permission' only applies to viewing the view and
473
        any possible sub views. By specifying this attribute, you can
474
        make the permission also apply to everything described in the
475
        supplied interface.
476

477
        Multiple interfaces can be provided, separated by
478
        whitespace."""),
479
        required=False,
480
        value_type=GlobalInterface(),
481
        )
482

483
    allowed_attributes = Tokens(
1✔
484
        title=_("View attributes that are also allowed if the user"
485
                " has permission."),
486
        description=_("""
487
        By default, 'permission' only applies to viewing the view and
488
        any possible sub views. By specifying 'allowed_attributes',
489
        you can make the permission also apply to the extra attributes
490
        on the view object."""),
491
        required=False,
492
        value_type=PythonIdentifier(),
493
        )
494

495
class IBasicResourceInformation(Interface):
1✔
496
    """
497
    Basic information for resources
498
    """
499

500
    name = TextLine(
1✔
501
        title=_("The name of the resource."),
502
        description=_("The name shows up in URLs/paths. For example 'foo'."),
503
        required=True,
504
        default=u'',
505
        )
506

507
    provides = GlobalInterface(
1✔
508
        title=_("The interface this component provides."),
509
        description=_("""
510
        A view can provide an interface.  This would be used for
511
        views that support other views."""),
512
        required=False,
513
        default=Interface,
514
        )
515

516
    type = GlobalInterface(
1✔
517
        title=_("Request type"),
518
        required=True
519
        )
520

521

522
class IViewDirective(IBasicViewInformation, IBasicResourceInformation):
1✔
523
    """Register a view for a component"""
524

525
    factory = Tokens(
1✔
526
        title=_("Factory"),
527
        required=False,
528
        value_type=GlobalObject(),
529
        )
530

531
def view(_context, factory, type, name, for_,
1✔
532
         permission=None,
533
         allowed_interface=None,
534
         allowed_attributes=None,
535
         provides=Interface,
536
        ):
537

538
    if ((allowed_attributes or allowed_interface)
1✔
539
        and (not permission)):
540
        raise ComponentConfigurationError(
1✔
541
            "'permission' required with 'allowed_interface' or "
542
            "'allowed_attributes'")
543

544
    if permission is not None:
1✔
545

546
        checker = _checker(_context, permission,
1✔
547
                           allowed_interface, allowed_attributes)
548

549
        class ProxyView(object):
1✔
550
            """Class to create simple proxy views."""
551

552
            def __init__(self, factory, checker):
1✔
553
                self.factory = factory
1✔
554
                self.checker = checker
1✔
555

556
            def __call__(self, *objects):
1✔
557
                return proxify(self.factory(*objects), self.checker)
1✔
558

559
        factory[-1] = ProxyView(factory[-1], checker)
1✔
560

561

562
    if not for_:
1✔
563
        raise ComponentConfigurationError("No for interfaces specified");
1✔
564
    for_ = tuple(for_)
1✔
565

566
    # Generate a single factory from multiple factories:
567
    factories = factory
1✔
568
    if len(factories) == 1:
1✔
569
        factory = factories[0]
1✔
570
    elif len(factories) < 1:
1✔
571
        raise ComponentConfigurationError("No view factory specified")
1✔
572
    elif len(factories) > 1 and len(for_) > 1:
1✔
573
        raise ComponentConfigurationError(
1✔
574
            "Can't use multiple factories and multiple for")
575
    else:
576
        def factory(ob, request):
1✔
577
            for f in factories[:-1]:
1✔
578
                ob = f(ob)
1✔
579
            return factories[-1](ob, request)
1✔
580
        factory.factory = factories[0]
1✔
581

582
    for_ = for_ + (type,)
1✔
583

584
    _context.action(
1✔
585
        discriminator = ('view', for_, name, provides),
586
        callable = handler,
587
        args = ('registerAdapter',
588
                factory, for_, provides, name, _context.info),
589
        )
590

591
    _context.action(
1✔
592
        discriminator = None,
593
        callable = provideInterface,
594
        args = ('', provides)
595
        )
596

597
    if for_ is not None:
1!
598
        for iface in for_:
1✔
599
            if iface is not None:
1!
600
                _context.action(
1✔
601
                    discriminator = None,
602
                    callable = provideInterface,
603
                    args = ('', iface)
604
                    )
605

606

607
class IResourceDirective(IBasicComponentInformation,
1✔
608
                         IBasicResourceInformation):
609
    """Register a resource"""
610

611
    allowed_interface = Tokens(
1✔
612
        title=_("Interface that is also allowed if user has permission."),
613
        required=False,
614
        value_type=GlobalInterface(),
615
        )
616

617
    allowed_attributes = Tokens(
1✔
618
        title=_("View attributes that are also allowed if user"
619
                " has permission."),
620
        required=False,
621
        value_type=PythonIdentifier(),
622
        )
623

624
def resource(_context, factory, type, name,
1✔
625
             permission=None,
626
             allowed_interface=None, allowed_attributes=None,
627
             provides=Interface):
628

629
    if ((allowed_attributes or allowed_interface)
1✔
630
        and (not permission)):
631
        raise ComponentConfigurationError(
1✔
632
            "Must use name attribute with allowed_interface or "
633
            "allowed_attributes"
634
            )
635

636
    if permission is not None:
1✔
637

638
        checker = _checker(_context, permission,
1✔
639
                           allowed_interface, allowed_attributes)
640

641
        def proxyResource(request, factory=factory, checker=checker):
1✔
642
            return proxify(factory(request), checker)
1✔
643
        proxyResource.factory = factory
1✔
644

645
        factory = proxyResource
1✔
646

647
    _context.action(
1✔
648
        discriminator = ('resource', name, type, provides),
649
        callable = handler,
650
        args = ('registerAdapter',
651
                factory, (type,), provides, name, _context.info))
652
    _context.action(
1✔
653
        discriminator = None,
654
        callable = provideInterface,
655
        args = ('', type))
656
    _context.action(
1✔
657
        discriminator = None,
658
        callable = provideInterface,
659
        args = ('', provides))
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