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

zopefoundation / zope.component / 6272867936

22 Sep 2023 10:01AM UTC coverage: 99.707% (-0.02%) from 99.727%
6272867936

Pull #72

github

dhavlik
Integrate .readthedocs.yaml.
Pull Request #72: Integrate .readthedocs.yaml.

460 of 475 branches covered (0.0%)

Branch coverage included in aggregate %.

4646 of 4646 relevant lines covered (100.0%)

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
1✔
31
from zope.component._declaration import getName
1✔
32
from zope.component.interface import provideInterface
1✔
33

34

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

50
_ = MessageFactory('zope')
1✔
51

52

53
class ComponentConfigurationError(ValueError, ConfigurationError):
1✔
54
    pass
1✔
55

56

57
def handler(methodName, *args, **kwargs):
1✔
58
    method = getattr(getSiteManager(), methodName)
1✔
59
    method(*args, **kwargs)
1✔
60

61

62
class IBasicComponentInformation(Interface):
1✔
63

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

73
    permission = Permission(
1✔
74
        title=_("Permission"),
75
        description=_("Permission required to use this component."),
76
        required=False,
77
    )
78

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

89

90
class IAdapterDirective(Interface):
1✔
91
    """
92
    Register an adapter
93
    """
94

95
    factory = Tokens(
1✔
96
        title=_("Adapter factory/factories"),
97
        description=_("A list of factories (usually just one) that create"
98
                      " the adapter instance."),
99
        required=True,
100
        value_type=GlobalObject()
101
    )
102

103
    provides = GlobalInterface(
1✔
104
        title=_("Interface the component provides"),
105
        description=_("This attribute specifies the interface the adapter"
106
                      " instance must provide."),
107
        required=False,
108
    )
109

110
    for_ = Tokens(
1✔
111
        title=_("Specifications to be adapted"),
112
        description=_("This should be a list of interfaces or classes"),
113
        required=False,
114
        value_type=GlobalObject(
115
            missing_value=object(),
116
        ),
117
    )
118

119
    permission = Permission(
1✔
120
        title=_("Permission"),
121
        description=_("This adapter is only available, if the principal"
122
                      " has this permission."),
123
        required=False,
124
    )
125

126
    name = TextLine(
1✔
127
        title=_("Name"),
128
        description=_("Adapters can have names.\n\n"
129
                      "This attribute allows you to specify the name for"
130
                      " this adapter."),
131
        required=False,
132
    )
133

134
    trusted = Bool(
1✔
135
        title=_("Trusted"),
136
        description=_("""Make the adapter a trusted adapter
137

138
        Trusted adapters have unfettered access to the objects they
139
        adapt.  If asked to adapt security-proxied objects, then,
140
        rather than getting an unproxied adapter of security-proxied
141
        objects, you get a security-proxied adapter of unproxied
142
        objects.
143
        """),
144
        required=False,
145
        default=False,
146
    )
147

148
    locate = Bool(
1✔
149
        title=_("Locate"),
150
        description=_("""Make the adapter a locatable adapter
151

152
        Located adapter should be used if a non-public permission
153
        is used.
154
        """),
155
        required=False,
156
        default=False,
157
    )
158

159

160
def _rolledUpFactory(factories):
1✔
161
    # This has to be named 'factory', aparently, so as not to confuse
162
    # apidoc :(
163
    def factory(ob):
1✔
164
        for f in factories:
1✔
165
            ob = f(ob)
1✔
166
        return ob
1✔
167
    # Store the original factory for documentation
168
    factory.factory = factories[0]
1✔
169
    return factory
1✔
170

171

172
def adapter(_context, factory, provides=None, for_=None, permission=None,
1✔
173
            name='', trusted=False, locate=False):
174

175
    if for_ is None:
1✔
176
        if len(factory) == 1:
1!
177
            for_ = adaptedBy(factory[0])
1✔
178

179
        if for_ is None:
1✔
180
            raise TypeError("No for attribute was provided and can't "
1✔
181
                            "determine what the factory adapts.")
182

183
    for_ = tuple(for_)
1✔
184

185
    if provides is None:
1✔
186
        if len(factory) == 1:
1!
187
            p = list(implementedBy(factory[0]))
1✔
188
            if len(p) == 1:
1✔
189
                provides = p[0]
1✔
190

191
        if provides is None:
1✔
192
            raise TypeError("Missing 'provides' attribute")
1✔
193

194
    if name == '':
1✔
195
        if len(factory) == 1:
1✔
196
            name = getName(factory[0])
1✔
197

198
    # Generate a single factory from multiple factories:
199
    factories = factory
1✔
200
    if len(factories) == 1:
1✔
201
        factory = factories[0]
1✔
202
    elif len(factories) < 1:
1✔
203
        raise ComponentConfigurationError("No factory specified")
1✔
204
    elif len(factories) > 1 and len(for_) != 1:
1✔
205
        raise ComponentConfigurationError(
1✔
206
            "Can't use multiple factories and multiple for")
207
    else:
208
        factory = _rolledUpFactory(factories)
1✔
209

210
    if permission is not None:
1✔
211
        factory = protectedFactory(factory, provides, permission)
1✔
212

213
    # invoke custom adapter factories
214
    if locate or permission is not None or trusted:
1✔
215
        factory = securityAdapterFactory(factory, permission, locate, trusted)
1✔
216

217
    _context.action(
1✔
218
        discriminator=('adapter', for_, provides, name),
219
        callable=handler,
220
        args=('registerAdapter',
221
              factory, for_, provides, name, _context.info),
222
    )
223
    _context.action(
1✔
224
        discriminator=None,
225
        callable=provideInterface,
226
        args=('', provides)
227
    )
228
    if for_:
1✔
229
        for iface in for_:
1✔
230
            if iface is not None:
1!
231
                _context.action(
1✔
232
                    discriminator=None,
233
                    callable=provideInterface,
234
                    args=('', iface)
235
                )
236

237

238
class ISubscriberDirective(Interface):
1✔
239
    """
240
    Register a subscriber
241
    """
242

243
    factory = GlobalObject(
1✔
244
        title=_("Subscriber factory"),
245
        description=_("A factory used to create the subscriber instance."),
246
        required=False,
247
    )
248

249
    handler = GlobalObject(
1✔
250
        title=_("Handler"),
251
        description=_("A callable object that handles events."),
252
        required=False,
253
    )
254

255
    provides = GlobalInterface(
1✔
256
        title=_("Interface the component provides"),
257
        description=_("This attribute specifies the interface the adapter"
258
                      " instance must provide."),
259
        required=False,
260
    )
261

262
    for_ = Tokens(
1✔
263
        title=_("Interfaces or classes that this subscriber depends on"),
264
        description=_("This should be a list of interfaces or classes"),
265
        required=False,
266
        value_type=GlobalObject(
267
            missing_value=object(),
268
        ),
269
    )
270

271
    permission = Permission(
1✔
272
        title=_("Permission"),
273
        description=_("This subscriber is only available, if the"
274
                      " principal has this permission."),
275
        required=False,
276
    )
277

278
    trusted = Bool(
1✔
279
        title=_("Trusted"),
280
        description=_("""Make the subscriber a trusted subscriber
281

282
        Trusted subscribers have unfettered access to the objects they
283
        adapt.  If asked to adapt security-proxied objects, then,
284
        rather than getting an unproxied subscriber of security-proxied
285
        objects, you get a security-proxied subscriber of unproxied
286
        objects.
287
        """),
288
        required=False,
289
        default=False,
290
    )
291

292
    locate = Bool(
1✔
293
        title=_("Locate"),
294
        description=_("""Make the subscriber a locatable subscriber
295

296
        Located subscribers should be used if a non-public permission
297
        is used.
298
        """),
299
        required=False,
300
        default=False,
301
    )
302

303

304
_handler = handler
1✔
305

306

307
def subscriber(_context, for_=None, factory=None, handler=None, provides=None,
1✔
308
               permission=None, trusted=False, locate=False):
309
    if factory is None:
1✔
310
        if handler is None:
1✔
311
            raise TypeError("No factory or handler provided")
1✔
312
        if provides is not None:
1✔
313
            raise TypeError("Cannot use handler with provides")
1✔
314
        factory = handler
1✔
315
    else:
316
        if handler is not None:
1✔
317
            raise TypeError("Cannot use handler with factory")
1✔
318
        if provides is None:
1✔
319
            p = list(implementedBy(factory))
1✔
320
            if len(p) == 1:
1✔
321
                provides = p[0]
1✔
322
        if provides is None:
1✔
323
            raise TypeError(
1✔
324
                "You must specify a provided interface when registering "
325
                "a factory")
326

327
    if for_ is None:
1✔
328
        for_ = adaptedBy(factory)
1✔
329
        if for_ is None:
1✔
330
            raise TypeError("No for attribute was provided and can't "
1✔
331
                            "determine what the factory (or handler) adapts.")
332

333
    if permission is not None:
1✔
334
        factory = protectedFactory(factory, provides, permission)
1✔
335

336
    for_ = tuple(for_)
1✔
337

338
    # invoke custom adapter factories
339
    if locate or permission is not None or trusted:
1✔
340
        factory = securityAdapterFactory(factory, permission, locate, trusted)
1✔
341

342
    if handler is not None:
1✔
343
        _context.action(
1✔
344
            discriminator=None,
345
            callable=_handler,
346
            args=('registerHandler',
347
                  handler, for_, '', _context.info),
348
        )
349
    else:
350
        _context.action(
1✔
351
            discriminator=None,
352
            callable=_handler,
353
            args=('registerSubscriptionAdapter',
354
                  factory, for_, provides, '', _context.info),
355
        )
356

357
    if provides is not None:
1✔
358
        _context.action(
1✔
359
            discriminator=None,
360
            callable=provideInterface,
361
            args=('', provides)
362
        )
363

364
    # For each interface, state that the adapter provides that interface.
365
    for iface in for_:
1✔
366
        if iface is not None:
1!
367
            _context.action(
1✔
368
                discriminator=None,
369
                callable=provideInterface,
370
                args=('', iface)
371
            )
372

373

374
class IUtilityDirective(IBasicComponentInformation):
1✔
375
    """Register a utility."""
376

377
    provides = GlobalInterface(
1✔
378
        title=_("Provided interface"),
379
        description=_("Interface provided by the utility."),
380
        required=False,
381
    )
382

383
    name = TextLine(
1✔
384
        title=_("Name"),
385
        description=_("Name of the registration.  This is used by"
386
                      " application code when locating a utility."),
387
        required=False,
388
    )
389

390

391
def utility(_context, provides=None, component=None, factory=None,
1✔
392
            permission=None, name=''):
393
    if factory and component:
1✔
394
        raise TypeError("Can't specify factory and component.")
1✔
395

396
    if provides is None:
1✔
397
        if factory:
1✔
398
            provides = list(implementedBy(factory))
1✔
399
        else:
400
            provides = list(providedBy(component))
1✔
401
        if len(provides) == 1:
1✔
402
            provides = provides[0]
1✔
403
        else:
404
            raise TypeError("Missing 'provides' attribute")
1✔
405

406
    if name == '':
1✔
407
        if factory:
1✔
408
            name = getName(factory)
1✔
409
        else:
410
            name = getName(component)
1✔
411

412
    if permission is not None:
1✔
413
        if component:
1✔
414
            component = proxify(component, provides=provides,
1✔
415
                                permission=permission)
416
        if factory:
1✔
417
            factory = protectedFactory(factory, provides, permission)
1✔
418

419
    _context.action(
1✔
420
        discriminator=('utility', provides, name),
421
        callable=handler,
422
        args=('registerUtility', component, provides, name, _context.info),
423
        kw=dict(factory=factory),
424
    )
425
    _context.action(
1✔
426
        discriminator=None,
427
        callable=provideInterface,
428
        args=('', provides),
429
    )
430

431

432
class IInterfaceDirective(Interface):
1✔
433
    """
434
    Define an interface
435
    """
436

437
    interface = GlobalInterface(
1✔
438
        title=_("Interface"),
439
        required=True,
440
    )
441

442
    type = GlobalInterface(
1✔
443
        title=_("Interface type"),
444
        required=False,
445
    )
446

447
    name = TextLine(
1✔
448
        title=_("Name"),
449
        required=False,
450
    )
451

452

453
def interface(_context, interface, type=None, name=''):
1✔
454
    _context.action(
1✔
455
        discriminator=None,
456
        callable=provideInterface,
457
        args=(name, interface, type)
458
    )
459

460

461
class IBasicViewInformation(Interface):
1✔
462
    """This is the basic information for all views."""
463

464
    for_ = Tokens(
1✔
465
        title=_("Specifications of the objects to be viewed"),
466
        description=_("""This should be a list of interfaces or classes
467
        """),
468
        required=True,
469
        value_type=GlobalObject(
470
            missing_value=object(),
471
        ),
472
    )
473

474
    permission = Permission(
1✔
475
        title=_("Permission"),
476
        description=_("The permission needed to use the view."),
477
        required=False,
478
    )
479

480
    class_ = GlobalObject(
1✔
481
        title=_("Class"),
482
        description=_("A class that provides attributes used by the view."),
483
        required=False,
484
    )
485

486
    allowed_interface = Tokens(
1✔
487
        title=_("Interface that is also allowed if user has permission."),
488
        description=_("""
489
        By default, 'permission' only applies to viewing the view and
490
        any possible sub views. By specifying this attribute, you can
491
        make the permission also apply to everything described in the
492
        supplied interface.
493

494
        Multiple interfaces can be provided, separated by
495
        whitespace."""),
496
        required=False,
497
        value_type=GlobalInterface(),
498
    )
499

500
    allowed_attributes = Tokens(
1✔
501
        title=_("View attributes that are also allowed if the user"
502
                " has permission."),
503
        description=_("""
504
        By default, 'permission' only applies to viewing the view and
505
        any possible sub views. By specifying 'allowed_attributes',
506
        you can make the permission also apply to the extra attributes
507
        on the view object."""),
508
        required=False,
509
        value_type=PythonIdentifier(),
510
    )
511

512

513
class IBasicResourceInformation(Interface):
1✔
514
    """
515
    Basic information for resources
516
    """
517

518
    name = TextLine(
1✔
519
        title=_("The name of the resource."),
520
        description=_("The name shows up in URLs/paths. For example 'foo'."),
521
        required=True,
522
        default='',
523
    )
524

525
    provides = GlobalInterface(
1✔
526
        title=_("The interface this component provides."),
527
        description=_("""
528
        A view can provide an interface.  This would be used for
529
        views that support other views."""),
530
        required=False,
531
        default=Interface,
532
    )
533

534
    type = GlobalInterface(
1✔
535
        title=_("Request type"),
536
        required=True
537
    )
538

539

540
class IViewDirective(IBasicViewInformation, IBasicResourceInformation):
1✔
541
    """Register a view for a component"""
542

543
    factory = Tokens(
1✔
544
        title=_("Factory"),
545
        required=False,
546
        value_type=GlobalObject(),
547
    )
548

549

550
def view(_context, factory, type, name, for_,
1✔
551
         permission=None,
552
         allowed_interface=None,
553
         allowed_attributes=None,
554
         provides=Interface,
555
         ):
556

557
    if ((allowed_attributes or allowed_interface)
1✔
558
            and (not permission)):
559
        raise ComponentConfigurationError(
1✔
560
            "'permission' required with 'allowed_interface' or "
561
            "'allowed_attributes'")
562

563
    if permission is not None:
1✔
564

565
        checker = _checker(_context, permission,
1✔
566
                           allowed_interface, allowed_attributes)
567

568
        class ProxyView:
1✔
569
            """Class to create simple proxy views."""
570

571
            def __init__(self, factory, checker):
1✔
572
                self.factory = factory
1✔
573
                self.checker = checker
1✔
574

575
            def __call__(self, *objects):
1✔
576
                return proxify(self.factory(*objects), self.checker)
1✔
577

578
        factory[-1] = ProxyView(factory[-1], checker)
1✔
579

580
    if not for_:
1✔
581
        raise ComponentConfigurationError("No for interfaces specified")
1✔
582
    for_ = tuple(for_)
1✔
583

584
    # Generate a single factory from multiple factories:
585
    factories = factory
1✔
586
    if len(factories) == 1:
1✔
587
        factory = factories[0]
1✔
588
    elif len(factories) < 1:
1✔
589
        raise ComponentConfigurationError("No view factory specified")
1✔
590
    elif len(factories) > 1 and len(for_) > 1:
1✔
591
        raise ComponentConfigurationError(
1✔
592
            "Can't use multiple factories and multiple for")
593
    else:
594
        def factory(ob, request):
1✔
595
            for f in factories[:-1]:
1✔
596
                ob = f(ob)
1✔
597
            return factories[-1](ob, request)
1✔
598
        factory.factory = factories[0]
1✔
599

600
    for_ = for_ + (type,)
1✔
601

602
    _context.action(
1✔
603
        discriminator=('view', for_, name, provides),
604
        callable=handler,
605
        args=('registerAdapter',
606
              factory, for_, provides, name, _context.info),
607
    )
608

609
    _context.action(
1✔
610
        discriminator=None,
611
        callable=provideInterface,
612
        args=('', provides)
613
    )
614

615
    if for_ is not None:
1!
616
        for iface in for_:
1✔
617
            if iface is not None:
1!
618
                _context.action(
1✔
619
                    discriminator=None,
620
                    callable=provideInterface,
621
                    args=('', iface)
622
                )
623

624

625
class IResourceDirective(IBasicComponentInformation,
1✔
626
                         IBasicResourceInformation):
627
    """Register a resource"""
628

629
    allowed_interface = Tokens(
1✔
630
        title=_("Interface that is also allowed if user has permission."),
631
        required=False,
632
        value_type=GlobalInterface(),
633
    )
634

635
    allowed_attributes = Tokens(
1✔
636
        title=_("View attributes that are also allowed if user"
637
                " has permission."),
638
        required=False,
639
        value_type=PythonIdentifier(),
640
    )
641

642

643
def resource(_context, factory, type, name,
1✔
644
             permission=None,
645
             allowed_interface=None, allowed_attributes=None,
646
             provides=Interface):
647

648
    if ((allowed_attributes or allowed_interface)
1✔
649
            and (not permission)):
650
        raise ComponentConfigurationError(
1✔
651
            "Must use name attribute with allowed_interface or "
652
            "allowed_attributes"
653
        )
654

655
    if permission is not None:
1✔
656

657
        checker = _checker(_context, permission,
1✔
658
                           allowed_interface, allowed_attributes)
659

660
        def proxyResource(request, factory=factory, checker=checker):
1✔
661
            return proxify(factory(request), checker)
1✔
662
        proxyResource.factory = factory
1✔
663

664
        factory = proxyResource
1✔
665

666
    _context.action(
1✔
667
        discriminator=('resource', name, type, provides),
668
        callable=handler,
669
        args=('registerAdapter',
670
              factory, (type,), provides, name, _context.info))
671
    _context.action(
1✔
672
        discriminator=None,
673
        callable=provideInterface,
674
        args=('', type))
675
    _context.action(
1✔
676
        discriminator=None,
677
        callable=provideInterface,
678
        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