-
Notifications
You must be signed in to change notification settings - Fork 882
/
config.py
3324 lines (2851 loc) · 143 KB
/
config.py
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
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import inspect
import os
import re
import sys
import types
import traceback
import warnings
import venusian
from translationstring import ChameleonTranslate
from zope.configuration.config import GroupingContextDecorator
from zope.configuration.config import ConfigurationMachine
from zope.configuration.xmlconfig import registerCommonDirectives
from zope.interface import Interface
from zope.interface import implementedBy
from zope.interface.interfaces import IInterface
from zope.interface import implements
from zope.interface import classProvides
from pyramid.interfaces import IAuthenticationPolicy
from pyramid.interfaces import IAuthorizationPolicy
from pyramid.interfaces import IChameleonTranslate
from pyramid.interfaces import IDebugLogger
from pyramid.interfaces import IDefaultPermission
from pyramid.interfaces import IDefaultRootFactory
from pyramid.interfaces import IException
from pyramid.interfaces import IExceptionResponse
from pyramid.interfaces import IExceptionViewClassifier
from pyramid.interfaces import ILocaleNegotiator
from pyramid.interfaces import IMultiView
from pyramid.interfaces import IPackageOverrides
from pyramid.interfaces import IRendererFactory
from pyramid.interfaces import IRendererGlobalsFactory
from pyramid.interfaces import IRequest
from pyramid.interfaces import IRequestFactory
from pyramid.interfaces import IResponse
from pyramid.interfaces import IRootFactory
from pyramid.interfaces import IRouteRequest
from pyramid.interfaces import IRoutesMapper
from pyramid.interfaces import ISecuredView
from pyramid.interfaces import ISessionFactory
from pyramid.interfaces import IStaticURLInfo
from pyramid.interfaces import ITranslationDirectories
from pyramid.interfaces import ITraverser
from pyramid.interfaces import IView
from pyramid.interfaces import IViewClassifier
from pyramid.interfaces import IViewMapper
from pyramid.interfaces import IViewMapperFactory
from pyramid import renderers
from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.compat import all
from pyramid.compat import md5
from pyramid.compat import any
from pyramid.events import ApplicationCreated
from pyramid.exceptions import ConfigurationError
from pyramid.exceptions import PredicateMismatch
from pyramid.httpexceptions import default_exceptionresponse_view
from pyramid.httpexceptions import HTTPForbidden
from pyramid.httpexceptions import HTTPNotFound
from pyramid.i18n import get_localizer
from pyramid.log import make_stream_logger
from pyramid.mako_templating import renderer_factory as mako_renderer_factory
from pyramid.path import caller_package
from pyramid.path import package_path
from pyramid.path import package_of
from pyramid.registry import Registry
from pyramid.renderers import RendererHelper
from pyramid.request import route_request_iface
from pyramid.asset import PackageOverrides
from pyramid.asset import resolve_asset_spec
from pyramid.settings import Settings
from pyramid.static import StaticURLInfo
from pyramid.threadlocal import get_current_registry
from pyramid.threadlocal import get_current_request
from pyramid.threadlocal import manager
from pyramid.traversal import DefaultRootFactory
from pyramid.traversal import find_interface
from pyramid.traversal import traversal_path
from pyramid.urldispatch import RoutesMapper
from pyramid.util import DottedNameResolver
from pyramid.util import WeakOrderedSet
from pyramid.view import render_view_to_response
DEFAULT_RENDERERS = (
('.mak', mako_renderer_factory),
('.mako', mako_renderer_factory),
('json', renderers.json_renderer_factory),
('string', renderers.string_renderer_factory),
)
try:
from pyramid import chameleon_text
DEFAULT_RENDERERS += (('.txt', chameleon_text.renderer_factory),)
except TypeError: # pragma: no cover
pass # pypy
try:
from pyramid import chameleon_zpt
DEFAULT_RENDERERS += (('.pt', chameleon_zpt.renderer_factory),)
except TypeError: # pragma: no cover
pass #pypy
MAX_ORDER = 1 << 30
DEFAULT_PHASH = md5().hexdigest()
def action_method(wrapped):
""" Wrapper to provide the right conflict info report data when a method
that calls Configurator.action calls another that does the same"""
def wrapper(self, *arg, **kw):
if self._ainfo is None:
self._ainfo = []
info = kw.pop('_info', None)
if info is None:
try:
f = traceback.extract_stack(limit=3)
info = f[-2]
except: # pragma: no cover
info = ''
self._ainfo.append(info)
try:
result = wrapped(self, *arg, **kw)
finally:
self._ainfo.pop()
return result
wrapper.__name__ = wrapped.__name__
wrapper.__doc__ = wrapped.__doc__
wrapper.__docobj__ = wrapped # for sphinx
return wrapper
class Configurator(object):
"""
A Configurator is used to configure a :app:`Pyramid`
:term:`application registry`.
The Configurator accepts a number of arguments: ``registry``,
``package``, ``settings``, ``root_factory``, ``authentication_policy``,
``authorization_policy``, ``renderers`` ``debug_logger``,
``locale_negotiator``, ``request_factory``, ``renderer_globals_factory``,
``default_permission``, ``session_factory``, ``default_view_mapper``,
``autocommit``, and ``exceptionresponse_view``.
If the ``registry`` argument is passed as a non-``None`` value, it
must be an instance of the :class:`pyramid.registry.Registry`
class representing the registry to configure. If ``registry`` is
``None``, the configurator will create a
:class:`pyramid.registry.Registry` instance itself; it will
also perform some default configuration that would not otherwise
be done. After construction, the configurator may be used to add
configuration to the registry. The overall state of a registry is
called the 'configuration state'.
.. warning:: If a ``registry`` is passed to the Configurator
constructor, all other constructor arguments except ``package``
are ignored.
If the ``package`` argument is passed, it must be a reference to a
Python :term:`package` (e.g. ``sys.modules['thepackage']``) or a
:term:`dotted Python name` to same. This value is used as a basis
to convert relative paths passed to various configuration methods,
such as methods which accept a ``renderer`` argument, into
absolute paths. If ``None`` is passed (the default), the package
is assumed to be the Python package in which the *caller* of the
``Configurator`` constructor lives.
If the ``settings`` argument is passed, it should be a Python dictionary
representing the deployment settings for this application. These are
later retrievable using the :attr:`pyramid.registry.Registry.settings`
attribute (aka ``request.registry.settings``).
If the ``root_factory`` argument is passed, it should be an object
representing the default :term:`root factory` for your application
or a :term:`dotted Python name` to same. If it is ``None``, a
default root factory will be used.
If ``authentication_policy`` is passed, it should be an instance
of an :term:`authentication policy` or a :term:`dotted Python
name` to same.
If ``authorization_policy`` is passed, it should be an instance of
an :term:`authorization policy` or a :term:`dotted Python name` to
same.
.. note:: A ``ConfigurationError`` will be raised when an
authorization policy is supplied without also supplying an
authentication policy (authorization requires authentication).
If ``renderers`` is passed, it should be a list of tuples
representing a set of :term:`renderer` factories which should be
configured into this application (each tuple representing a set of
positional values that should be passed to
:meth:`pyramid.config.Configurator.add_renderer`). If
it is not passed, a default set of renderer factories is used.
If ``debug_logger`` is not passed, a default debug logger that
logs to stderr will be used. If it is passed, it should be an
instance of the :class:`logging.Logger` (PEP 282) standard library
class or a :term:`dotted Python name` to same. The debug logger
is used by :app:`Pyramid` itself to log warnings and
authorization debugging information.
If ``locale_negotiator`` is passed, it should be a :term:`locale
negotiator` implementation or a :term:`dotted Python name` to
same. See :ref:`custom_locale_negotiator`.
If ``request_factory`` is passed, it should be a :term:`request
factory` implementation or a :term:`dotted Python name` to same.
See :ref:`changing_the_request_factory`. By default it is ``None``,
which means use the default request factory.
If ``renderer_globals_factory`` is passed, it should be a :term:`renderer
globals` factory implementation or a :term:`dotted Python name` to same.
See :ref:`adding_renderer_globals`. By default, it is ``None``, which
means use no renderer globals factory.
.. warning:: as of Pyramid 1.1, ``renderer_globals_factory`` is
deprecated. Instead, use a BeforeRender event subscriber as per
:ref:`beforerender_event`.
If ``default_permission`` is passed, it should be a
:term:`permission` string to be used as the default permission for
all view configuration registrations performed against this
Configurator. An example of a permission string:``'view'``.
Adding a default permission makes it unnecessary to protect each
view configuration with an explicit permission, unless your
application policy requires some exception for a particular view.
By default, ``default_permission`` is ``None``, meaning that view
configurations which do not explicitly declare a permission will
always be executable by entirely anonymous users (any
authorization policy in effect is ignored). See also
:ref:`setting_a_default_permission`.
If ``session_factory`` is passed, it should be an object which
implements the :term:`session factory` interface. If a nondefault
value is passed, the ``session_factory`` will be used to create a
session object when ``request.session`` is accessed. Note that
the same outcome can be achieved by calling
:meth:`pyramid.config.Configurator.set_session_factory`. By
default, this argument is ``None``, indicating that no session
factory will be configured (and thus accessing ``request.session``
will throw an error) unless ``set_session_factory`` is called later
during configuration.
If ``autocommit`` is ``True``, every method called on the configurator
will cause an immediate action, and no configuration conflict detection
will be used. If ``autocommit`` is ``False``, most methods of the
configurator will defer their action until
:meth:`pyramid.config.Configurator.commit` is called. When
:meth:`pyramid.config.Configurator.commit` is called, the actions implied
by the called methods will be checked for configuration conflicts unless
``autocommit`` is ``True``. If a conflict is detected a
``ConfigurationConflictError`` will be raised. Calling
:meth:`pyramid.config.Configurator.make_wsgi_app` always implies a final
commit.
If ``default_view_mapper`` is passed, it will be used as the default
:term:`view mapper` factory for view configurations that don't otherwise
specify one (see :class:`pyramid.interfaces.IViewMapperFactory`). If a
default_view_mapper is not passed, a superdefault view mapper will be
used.
If ``exceptionresponse_view`` is passed, it must be a :term:`view
callable` or ``None``. If it is a view callable, it will be used as an
exception view callable when an :term:`exception response` is raised. If
``exceptionresponse_view`` is ``None``, no exception response view will
be registered, and all raised exception responses will be bubbled up to
Pyramid's caller. By
default, the ``pyramid.httpexceptions.default_exceptionresponse_view``
function is used as the ``exceptionresponse_view``. This argument is new
in Pyramid 1.1. """
manager = manager # for testing injection
venusian = venusian # for testing injection
_ctx = None
_ainfo = None
def __init__(self,
registry=None,
package=None,
settings=None,
root_factory=None,
authentication_policy=None,
authorization_policy=None,
renderers=DEFAULT_RENDERERS,
debug_logger=None,
locale_negotiator=None,
request_factory=None,
renderer_globals_factory=None,
default_permission=None,
session_factory=None,
default_view_mapper=None,
autocommit=False,
exceptionresponse_view=default_exceptionresponse_view,
):
if package is None:
package = caller_package()
name_resolver = DottedNameResolver(package)
self.name_resolver = name_resolver
self.package_name = name_resolver.package_name
self.package = name_resolver.package
self.registry = registry
self.autocommit = autocommit
if registry is None:
registry = Registry(self.package_name)
self.registry = registry
self.setup_registry(
settings=settings,
root_factory=root_factory,
authentication_policy=authentication_policy,
authorization_policy=authorization_policy,
renderers=renderers,
debug_logger=debug_logger,
locale_negotiator=locale_negotiator,
request_factory=request_factory,
renderer_globals_factory=renderer_globals_factory,
default_permission=default_permission,
session_factory=session_factory,
default_view_mapper=default_view_mapper,
exceptionresponse_view=exceptionresponse_view,
)
def _set_settings(self, mapping):
if not mapping:
mapping = {}
settings = Settings(mapping)
self.registry.settings = settings
return settings
@action_method
def _set_root_factory(self, factory):
""" Add a :term:`root factory` to the current configuration
state. If the ``factory`` argument is ``None`` a default root
factory will be registered."""
factory = self.maybe_dotted(factory)
if factory is None:
factory = DefaultRootFactory
def register():
self.registry.registerUtility(factory, IRootFactory)
self.registry.registerUtility(factory, IDefaultRootFactory) # b/c
self.action(IRootFactory, register)
@action_method
def _set_authentication_policy(self, policy):
""" Add a :app:`Pyramid` :term:`authentication policy` to
the current configuration."""
policy = self.maybe_dotted(policy)
self.registry.registerUtility(policy, IAuthenticationPolicy)
self.action(IAuthenticationPolicy)
@action_method
def _set_authorization_policy(self, policy):
""" Add a :app:`Pyramid` :term:`authorization policy` to
the current configuration state (also accepts a :term:`dotted
Python name`."""
policy = self.maybe_dotted(policy)
self.registry.registerUtility(policy, IAuthorizationPolicy)
self.action(IAuthorizationPolicy, None)
def _make_spec(self, path_or_spec):
package, filename = resolve_asset_spec(path_or_spec,
self.package_name)
if package is None:
return filename # absolute filename
return '%s:%s' % (package, filename)
def _split_spec(self, path_or_spec):
return resolve_asset_spec(path_or_spec, self.package_name)
# b/w compat
def _derive_view(self, view, permission=None, predicates=(),
attr=None, renderer=None, wrapper_viewname=None,
viewname=None, accept=None, order=MAX_ORDER,
phash=DEFAULT_PHASH, decorator=None,
mapper=None, http_cache=None):
view = self.maybe_dotted(view)
mapper = self.maybe_dotted(mapper)
if isinstance(renderer, basestring):
renderer = RendererHelper(name=renderer, package=self.package,
registry = self.registry)
if renderer is None:
# use default renderer if one exists
if self.registry.queryUtility(IRendererFactory) is not None:
renderer = RendererHelper(name=None,
package=self.package,
registry=self.registry)
deriver = ViewDeriver(registry=self.registry,
permission=permission,
predicates=predicates,
attr=attr,
renderer=renderer,
wrapper_viewname=wrapper_viewname,
viewname=viewname,
accept=accept,
order=order,
phash=phash,
package=self.package,
mapper=mapper,
decorator=decorator,
http_cache=http_cache)
return deriver(view)
@action_method
def _set_security_policies(self, authentication, authorization=None):
if (authorization is not None) and (not authentication):
raise ConfigurationError(
'If the "authorization" is passed a value, '
'the "authentication" argument must also be '
'passed a value; authorization requires authentication.')
if authorization is None:
authorization = ACLAuthorizationPolicy() # default
self._set_authentication_policy(authentication)
self._set_authorization_policy(authorization)
def _fix_registry(self):
""" Fix up a ZCA component registry that is not a
pyramid.registry.Registry by adding analogues of ``has_listeners``,
``notify``, ``queryAdapterOrSelf``, and ``registerSelfAdapter``
through monkey-patching."""
_registry = self.registry
if not hasattr(_registry, 'notify'):
def notify(*events):
[ _ for _ in _registry.subscribers(events, None) ]
_registry.notify = notify
if not hasattr(_registry, 'has_listeners'):
_registry.has_listeners = True
if not hasattr(_registry, 'queryAdapterOrSelf'):
def queryAdapterOrSelf(object, interface, default=None):
if not interface.providedBy(object):
return _registry.queryAdapter(object, interface,
default=default)
return object
_registry.queryAdapterOrSelf = queryAdapterOrSelf
if not hasattr(_registry, 'registerSelfAdapter'):
def registerSelfAdapter(required=None, provided=None,
name=u'', info=u'', event=True):
return _registry.registerAdapter(lambda x: x,
required=required,
provided=provided, name=name,
info=info, event=event)
_registry.registerSelfAdapter = registerSelfAdapter
def _make_context(self, autocommit=False):
context = PyramidConfigurationMachine()
registerCommonDirectives(context)
context.registry = self.registry
context.autocommit = autocommit
return context
# API
def action(self, discriminator, callable=None, args=(), kw=None, order=0):
""" Register an action which will be executed when
:meth:`pyramid.config.Configuration.commit` is called (or executed
immediately if ``autocommit`` is ``True``).
.. note:: This method is typically only used by :app:`Pyramid`
framework extension authors, not by :app:`Pyramid` application
developers.
The ``discriminator`` uniquely identifies the action. It must be
given, but it can be ``None``, to indicate that the action never
conflicts. It must be a hashable value.
The ``callable`` is a callable object which performs the action. It
is optional. ``args`` and ``kw`` are tuple and dict objects
respectively, which are passed to ``callable`` when this action is
executed.
``order`` is a crude order control mechanism, only rarely used (has
no effect when autocommit is ``True``).
"""
if kw is None:
kw = {}
context = self._ctx
if context is None:
autocommit = self.autocommit
else:
autocommit = context.autocommit
if autocommit:
if callable is not None:
callable(*args, **kw)
else:
if context is None: # defer expensive creation of context
context = self._ctx = self._make_context(self.autocommit)
if not context.info:
# Try to provide more accurate info for conflict reports by
# wrapping the context in a decorator and attaching caller info
# to it, unless the context already has info (if it already has
# info, it's likely a context generated by a ZCML directive).
context = GroupingContextDecorator(context)
if self._ainfo:
info = self._ainfo[0]
else:
info = ''
context.info = info
context.action(discriminator, callable, args, kw, order)
def commit(self):
""" Commit any pending configuration actions. If a configuration
conflict is detected in the pending configuration actins, this method
will raise a :exc:`ConfigurationConflictError`; within the traceback
of this error will be information about the source of the conflict,
usually including file names and line numbers of the cause of the
configuration conflicts."""
if self._ctx is None:
return
self._ctx.execute_actions()
# unwrap and reset the context
self._ctx = None
def include(self, *callables):
"""Include one or more configuration callables, to support imperative
application extensibility.
A configuration callable should be a callable that accepts a single
argument named ``config``, which will be an instance of a
:term:`Configurator` (be warned that it will not be the same
configurator instance on which you call this method, however). The
code which runs as the result of calling the callable should invoke
methods on the configurator passed to it which add configuration
state. The return value of a callable will be ignored.
Values allowed to be presented via the ``*callables`` argument to
this method: any callable Python object or any :term:`dotted Python
name` which resolves to a callable Python object. It may also be a
Python :term:`module`, in which case, the module will be searched for
a callable named ``includeme``, which will be treated as the
configuration callable.
For example, if the ``includeme`` function below lives in a module
named ``myapp.myconfig``:
.. code-block:: python
:linenos:
# myapp.myconfig module
def my_view(request):
from pyramid.response import Response
return Response('OK')
def includeme(config):
config.add_view(my_view)
You might cause it be included within your Pyramid application like
so:
.. code-block:: python
:linenos:
from pyramid.config import Configurator
def main(global_config, **settings):
config = Configurator()
config.include('myapp.myconfig.includeme')
Because the function is named ``includeme``, the function name can
also be omitted from the dotted name reference:
.. code-block:: python
:linenos:
from pyramid.config import Configurator
def main(global_config, **settings):
config = Configurator()
config.include('myapp.myconfig')
Included configuration statements will be overridden by local
configuration statements if an included callable causes a
configuration conflict by registering something with the same
configuration parameters."""
_context = self._ctx
if _context is None:
_context = self._ctx = self._make_context(self.autocommit)
for c in callables:
c = self.maybe_dotted(c)
module = inspect.getmodule(c)
if module is c:
c = getattr(module, 'includeme')
spec = module.__name__ + ':' + c.__name__
sourcefile = inspect.getsourcefile(c)
if _context.processSpec(spec):
context = GroupingContextDecorator(_context)
context.basepath = os.path.dirname(sourcefile)
context.includepath = _context.includepath + (spec,)
context.package = package_of(module)
config = self.__class__.with_context(context)
c(config)
def add_directive(self, name, directive, action_wrap=True):
"""
Add a directive method to the configurator.
Framework extenders can add directive methods to a configurator by
instructing their users to call ``config.add_directive('somename',
'some.callable')``. This will make ``some.callable`` accessible as
``config.somename``. ``some.callable`` should be a function which
accepts ``config`` as a first argument, and arbitrary positional and
keyword arguments following. It should use config.action as
necessary to perform actions. Directive methods can then be invoked
like 'built-in' directives such as ``add_view``, ``add_route``, etc.
The ``action_wrap`` argument should be ``True`` for directives which
perform ``config.action`` with potentially conflicting
discriminators. ``action_wrap`` will cause the directive to be
wrapped in a decorator which provides more accurate conflict
cause information.
``add_directive`` does not participate in conflict detection, and
later calls to ``add_directive`` will override earlier calls.
"""
c = self.maybe_dotted(directive)
if not hasattr(self.registry, '_directives'):
self.registry._directives = {}
self.registry._directives[name] = (c, action_wrap)
def __getattr__(self, name):
# allow directive extension names to work
directives = getattr(self.registry, '_directives', {})
c = directives.get(name)
if c is None:
raise AttributeError(name)
c, action_wrap = c
if action_wrap:
c = action_method(c)
m = types.MethodType(c, self, self.__class__)
return m
@classmethod
def with_context(cls, context):
"""A classmethod used by ZCML directives,
:meth:`pyramid.config.Configurator.with_package`, and
:meth:`pyramid.config.Configurator.include` to obtain a configurator
with 'the right' context. Returns a new Configurator instance."""
configurator = cls(registry=context.registry, package=context.package,
autocommit=context.autocommit)
configurator._ctx = context
return configurator
def with_package(self, package):
""" Return a new Configurator instance with the same registry
as this configurator using the package supplied as the
``package`` argument to the new configurator. ``package`` may
be an actual Python package object or a Python dotted name
representing a package."""
context = self._ctx
if context is None:
context = self._ctx = self._make_context(self.autocommit)
context = GroupingContextDecorator(context)
context.package = package
return self.__class__.with_context(context)
def maybe_dotted(self, dotted):
""" Resolve the :term:`dotted Python name` ``dotted`` to a
global Python object. If ``dotted`` is not a string, return
it without attempting to do any name resolution. If
``dotted`` is a relative dotted name (e.g. ``.foo.bar``,
consider it relative to the ``package`` argument supplied to
this Configurator's constructor."""
return self.name_resolver.maybe_resolve(dotted)
def absolute_asset_spec(self, relative_spec):
""" Resolve the potentially relative :term:`asset
specification` string passed as ``relative_spec`` into an
absolute asset specification string and return the string.
Use the ``package`` of this configurator as the package to
which the asset specification will be considered relative
when generating an absolute asset specification. If the
provided ``relative_spec`` argument is already absolute, or if
the ``relative_spec`` is not a string, it is simply returned."""
if not isinstance(relative_spec, basestring):
return relative_spec
return self._make_spec(relative_spec)
absolute_resource_spec = absolute_asset_spec # b/w compat forever
def setup_registry(self, settings=None, root_factory=None,
authentication_policy=None, authorization_policy=None,
renderers=DEFAULT_RENDERERS, debug_logger=None,
locale_negotiator=None, request_factory=None,
renderer_globals_factory=None, default_permission=None,
session_factory=None, default_view_mapper=None,
exceptionresponse_view=default_exceptionresponse_view):
""" When you pass a non-``None`` ``registry`` argument to the
:term:`Configurator` constructor, no initial 'setup' is performed
against the registry. This is because the registry you pass in may
have already been initialized for use under :app:`Pyramid` via a
different configurator. However, in some circumstances (such as when
you want to use the Zope 'global` registry instead of a registry
created as a result of the Configurator constructor), or when you
want to reset the initial setup of a registry, you *do* want to
explicitly initialize the registry associated with a Configurator for
use under :app:`Pyramid`. Use ``setup_registry`` to do this
initialization.
``setup_registry`` configures settings, a root factory, security
policies, renderers, a debug logger, a locale negotiator, and various
other settings using the configurator's current registry, as per the
descriptions in the Configurator constructor."""
registry = self.registry
self._fix_registry()
self._set_settings(settings)
self._set_root_factory(root_factory)
# cope with WebOb response objects that aren't decorated with IResponse
from webob import Response as WebobResponse
# cope with WebOb exc objects not decoratored with IExceptionResponse
from webob.exc import WSGIHTTPException as WebobWSGIHTTPException
registry.registerSelfAdapter((WebobResponse,), IResponse)
debug_logger = self.maybe_dotted(debug_logger)
if debug_logger is None:
debug_logger = make_stream_logger('pyramid.debug', sys.stderr)
registry.registerUtility(debug_logger, IDebugLogger)
if authentication_policy or authorization_policy:
self._set_security_policies(authentication_policy,
authorization_policy)
for name, renderer in renderers:
self.add_renderer(name, renderer)
if exceptionresponse_view is not None:
exceptionresponse_view = self.maybe_dotted(exceptionresponse_view)
self.add_view(exceptionresponse_view, context=IExceptionResponse)
self.add_view(exceptionresponse_view,context=WebobWSGIHTTPException)
if locale_negotiator:
locale_negotiator = self.maybe_dotted(locale_negotiator)
registry.registerUtility(locale_negotiator, ILocaleNegotiator)
if request_factory:
request_factory = self.maybe_dotted(request_factory)
self.set_request_factory(request_factory)
if renderer_globals_factory:
warnings.warn(
'Passing ``renderer_globals_factory`` as a Configurator '
'constructor parameter is deprecated as of Pyramid 1.1. '
'Use a BeforeRender event subscriber as documented in the '
'"Hooks" chapter of the Pyramid narrative documentation '
'instead',
DeprecationWarning,
2)
renderer_globals_factory = self.maybe_dotted(
renderer_globals_factory)
self.set_renderer_globals_factory(renderer_globals_factory,
warn=False)
if default_permission:
self.set_default_permission(default_permission)
if session_factory is not None:
self.set_session_factory(session_factory)
# commit before adding default_view_mapper, as the
# exceptionresponse_view above requires the superdefault view
# mapper
self.commit()
if default_view_mapper is not None:
self.set_view_mapper(default_view_mapper)
self.commit()
def hook_zca(self):
""" Call :func:`zope.component.getSiteManager.sethook` with
the argument
:data:`pyramid.threadlocal.get_current_registry`, causing
the :term:`Zope Component Architecture` 'global' APIs such as
:func:`zope.component.getSiteManager`,
:func:`zope.component.getAdapter` and others to use the
:app:`Pyramid` :term:`application registry` rather than the
Zope 'global' registry. If :mod:`zope.component` cannot be
imported, this method will raise an :exc:`ImportError`."""
from zope.component import getSiteManager
getSiteManager.sethook(get_current_registry)
def unhook_zca(self):
""" Call :func:`zope.component.getSiteManager.reset` to undo
the action of
:meth:`pyramid.config.Configurator.hook_zca`. If
:mod:`zope.component` cannot be imported, this method will
raise an :exc:`ImportError`."""
from zope.component import getSiteManager
getSiteManager.reset()
def begin(self, request=None):
""" Indicate that application or test configuration has begun.
This pushes a dictionary containing the :term:`application
registry` implied by ``registry`` attribute of this
configurator and the :term:`request` implied by the
``request`` argument on to the :term:`thread local` stack
consulted by various :mod:`pyramid.threadlocal` API
functions."""
self.manager.push({'registry':self.registry, 'request':request})
def end(self):
""" Indicate that application or test configuration has ended.
This pops the last value pushed on to the :term:`thread local`
stack (usually by the ``begin`` method) and returns that
value.
"""
return self.manager.pop()
def derive_view(self, view, attr=None, renderer=None):
"""
Create a :term:`view callable` using the function, instance,
or class (or :term:`dotted Python name` referring to the same)
provided as ``view`` object.
This is API is useful to framework extenders who create
pluggable systems which need to register 'proxy' view
callables for functions, instances, or classes which meet the
requirements of being a :app:`Pyramid` view callable. For
example, a ``some_other_framework`` function in another
framework may want to allow a user to supply a view callable,
but he may want to wrap the view callable in his own before
registering the wrapper as a :app:`Pyramid` view callable.
Because a :app:`Pyramid` view callable can be any of a
number of valid objects, the framework extender will not know
how to call the user-supplied object. Running it through
``derive_view`` normalizes it to a callable which accepts two
arguments: ``context`` and ``request``.
For example:
.. code-block:: python
def some_other_framework(user_supplied_view):
config = Configurator(reg)
proxy_view = config.derive_view(user_supplied_view)
def my_wrapper(context, request):
do_something_that_mutates(request)
return proxy_view(context, request)
config.add_view(my_wrapper)
The ``view`` object provided should be one of the following:
- A function or another non-class callable object that accepts
a :term:`request` as a single positional argument and which
returns a :term:`response` object.
- A function or other non-class callable object that accepts
two positional arguments, ``context, request`` and which
returns a :term:`response` object.
- A class which accepts a single positional argument in its
constructor named ``request``, and which has a ``__call__``
method that accepts no arguments that returns a
:term:`response` object.
- A class which accepts two positional arguments named
``context, request``, and which has a ``__call__`` method
that accepts no arguments that returns a :term:`response`
object.
- A :term:`dotted Python name` which refers to any of the
kinds of objects above.
This API returns a callable which accepts the arguments
``context, request`` and which returns the result of calling
the provided ``view`` object.
The ``attr`` keyword argument is most useful when the view
object is a class. It names the method that should be used as
the callable. If ``attr`` is not provided, the attribute
effectively defaults to ``__call__``. See
:ref:`class_as_view` for more information.
The ``renderer`` keyword argument should be a renderer
name. If supplied, it will cause the returned callable to use
a :term:`renderer` to convert the user-supplied view result to
a :term:`response` object. If a ``renderer`` argument is not
supplied, the user-supplied view must itself return a
:term:`response` object. """
return self._derive_view(view, attr=attr, renderer=renderer)
@action_method
def add_subscriber(self, subscriber, iface=None):
"""Add an event :term:`subscriber` for the event stream
implied by the supplied ``iface`` interface. The
``subscriber`` argument represents a callable object (or a
:term:`dotted Python name` which identifies a callable); it
will be called with a single object ``event`` whenever
:app:`Pyramid` emits an :term:`event` associated with the
``iface``, which may be an :term:`interface` or a class or a
:term:`dotted Python name` to a global object representing an
interface or a class. Using the default ``iface`` value,
``None`` will cause the subscriber to be registered for all
event types. See :ref:`events_chapter` for more information
about events and subscribers."""
dotted = self.maybe_dotted
subscriber, iface = dotted(subscriber), dotted(iface)
if iface is None:
iface = (Interface,)
if not isinstance(iface, (tuple, list)):
iface = (iface,)
def register():
self.registry.registerHandler(subscriber, iface)
self.action(None, register)
return subscriber
@action_method
def add_response_adapter(self, adapter, type_or_iface):
""" When an object of type (or interface) ``type_or_iface`` is
returned from a view callable, Pyramid will use the adapter
``adapter`` to convert it into an object which implements the
:class:`pyramid.interfaces.IResponse` interface. If ``adapter`` is
None, an object returned of type (or interface) ``type_or_iface``
will itself be used as a response object.
``adapter`` and ``type_or_interface`` may be Python objects or
strings representing dotted names to importable Python global
objects.
See :ref:`using_iresponse` for more information."""
adapter = self.maybe_dotted(adapter)
type_or_iface = self.maybe_dotted(type_or_iface)
def register():
reg = self.registry
if adapter is None:
reg.registerSelfAdapter((type_or_iface,), IResponse)
else:
reg.registerAdapter(adapter, (type_or_iface,), IResponse)
self.action((IResponse, type_or_iface), register)
def add_settings(self, settings=None, **kw):
"""Augment the ``settings`` argument passed in to the Configurator
constructor with one or more 'setting' key/value pairs. A setting is
a single key/value pair in the dictionary-ish object returned from
the API :attr:`pyramid.registry.Registry.settings` and
:meth:`pyramid.config.Configurator.get_settings`.
You may pass a dictionary::
config.add_settings({'external_uri':'http://example.com'})
Or a set of key/value pairs::
config.add_settings(external_uri='http://example.com')
This function is useful when you need to test code that accesses the
:attr:`pyramid.registry.Registry.settings` API (or the
:meth:`pyramid.config.Configurator.get_settings` API) and
which uses values from that API.
"""
if settings is None:
settings = {}
utility = self.registry.settings
if utility is None:
utility = self._set_settings(settings)
utility.update(settings)
utility.update(kw)
def get_settings(self):
"""
Return a :term:`deployment settings` object for the current
application. A deployment settings object is a dictionary-like
object that contains key/value pairs based on the dictionary passed
as the ``settings`` argument to the
:class:`pyramid.config.Configurator` constructor or the
:func:`pyramid.router.make_app` API.
.. note:: For backwards compatibility, dictionary keys can also be
looked up as attributes of the settings object.
.. note:: the :attr:`pyramid.registry.Registry.settings` API
performs the same duty.
"""
return self.registry.settings
def make_wsgi_app(self):
""" Commits any pending configuration statements, sends a
:class:`pyramid.events.ApplicationCreated` event to all listeners,
and returns a :app:`Pyramid` WSGI application representing the
committed configuration state."""
self.commit()
from pyramid.router import Router # avoid circdep
app = Router(self.registry)
global_registries.add(self.registry)
# We push the registry on to the stack here in case any code
# that depends on the registry threadlocal APIs used in
# listeners subscribed to the IApplicationCreated event.
self.manager.push({'registry':self.registry, 'request':None})
try:
self.registry.notify(ApplicationCreated(app))
finally:
self.manager.pop()
return app
@action_method
def add_view(self, view=None, name="", for_=None, permission=None,
request_type=None, route_name=None, request_method=None,
request_param=None, containment=None, attr=None,