1import warnings
2from zope.interface import implementer
3
4from pyramid.interfaces import (
5    IDefaultRootFactory,
6    IRequestFactory,
7    IResponseFactory,
8    IRequestExtensions,
9    IRootFactory,
10    ISessionFactory,
11    )
12
13from pyramid.traversal import DefaultRootFactory
14
15from pyramid.util import (
16    action_method,
17    get_callable_name,
18    InstancePropertyHelper,
19    )
20
21
22class FactoriesConfiguratorMixin(object):
23    @action_method
24    def set_root_factory(self, factory):
25        """ Add a :term:`root factory` to the current configuration
26        state.  If the ``factory`` argument is ``None`` a default root
27        factory will be registered.
28
29        .. note::
30
31           Using the ``root_factory`` argument to the
32           :class:`pyramid.config.Configurator` constructor can be used to
33           achieve the same purpose.
34        """
35        factory = self.maybe_dotted(factory)
36        if factory is None:
37            factory = DefaultRootFactory
38
39        def register():
40            self.registry.registerUtility(factory, IRootFactory)
41            self.registry.registerUtility(factory, IDefaultRootFactory)  # b/c
42
43        intr = self.introspectable('root factories',
44                                   None,
45                                   self.object_description(factory),
46                                   'root factory')
47        intr['factory'] = factory
48        self.action(IRootFactory, register, introspectables=(intr,))
49
50    _set_root_factory = set_root_factory  # bw compat
51
52    @action_method
53    def set_session_factory(self, factory):
54        """
55        Configure the application with a :term:`session factory`.  If this
56        method is called, the ``factory`` argument must be a session
57        factory callable or a :term:`dotted Python name` to that factory.
58
59        .. note::
60
61           Using the ``session_factory`` argument to the
62           :class:`pyramid.config.Configurator` constructor can be used to
63           achieve the same purpose.
64        """
65        factory = self.maybe_dotted(factory)
66
67        def register():
68            self.registry.registerUtility(factory, ISessionFactory)
69        intr = self.introspectable('session factory', None,
70                                   self.object_description(factory),
71                                   'session factory')
72        intr['factory'] = factory
73        self.action(ISessionFactory, register, introspectables=(intr,))
74
75    @action_method
76    def set_request_factory(self, factory):
77        """ The object passed as ``factory`` should be an object (or a
78        :term:`dotted Python name` which refers to an object) which
79        will be used by the :app:`Pyramid` router to create all
80        request objects.  This factory object must have the same
81        methods and attributes as the
82        :class:`pyramid.request.Request` class (particularly
83        ``__call__``, and ``blank``).
84
85        See :meth:`pyramid.config.Configurator.add_request_method`
86        for a less intrusive way to extend the request objects with
87        custom methods and properties.
88
89        .. note::
90
91           Using the ``request_factory`` argument to the
92           :class:`pyramid.config.Configurator` constructor
93           can be used to achieve the same purpose.
94        """
95        factory = self.maybe_dotted(factory)
96
97        def register():
98            self.registry.registerUtility(factory, IRequestFactory)
99        intr = self.introspectable('request factory', None,
100                                   self.object_description(factory),
101                                   'request factory')
102        intr['factory'] = factory
103        self.action(IRequestFactory, register, introspectables=(intr,))
104
105    @action_method
106    def set_response_factory(self, factory):
107        """ The object passed as ``factory`` should be an object (or a
108        :term:`dotted Python name` which refers to an object) which
109        will be used by the :app:`Pyramid` as the default response
110        objects. The factory should conform to the
111        :class:`pyramid.interfaces.IResponseFactory` interface.
112
113        .. note::
114
115           Using the ``response_factory`` argument to the
116           :class:`pyramid.config.Configurator` constructor
117           can be used to achieve the same purpose.
118        """
119        factory = self.maybe_dotted(factory)
120
121        def register():
122            self.registry.registerUtility(factory, IResponseFactory)
123
124        intr = self.introspectable('response factory', None,
125                                   self.object_description(factory),
126                                   'response factory')
127        intr['factory'] = factory
128        self.action(IResponseFactory, register, introspectables=(intr,))
129
130    @action_method
131    def add_request_method(self,
132                           callable=None,
133                           name=None,
134                           property=False,
135                           reify=False):
136        """ Add a property or method to the request object.
137
138        When adding a method to the request, ``callable`` may be any
139        function that receives the request object as the first
140        parameter. If ``name`` is ``None`` then it will be computed
141        from the name of the ``callable``.
142
143        When adding a property to the request, ``callable`` can either
144        be a callable that accepts the request as its single positional
145        parameter, or it can be a property descriptor. If ``name`` is
146        ``None``, the name of the property will be computed from the
147        name of the ``callable``.
148
149        If the ``callable`` is a property descriptor a ``ValueError``
150        will be raised if ``name`` is ``None`` or ``reify`` is ``True``.
151
152        See :meth:`pyramid.request.Request.set_property` for more
153        details on ``property`` vs ``reify``. When ``reify`` is
154        ``True``, the value of ``property`` is assumed to also be
155        ``True``.
156
157        In all cases, ``callable`` may also be a
158        :term:`dotted Python name` which refers to either a callable or
159        a property descriptor.
160
161        If ``callable`` is ``None`` then the method is only used to
162        assist in conflict detection between different addons requesting
163        the same attribute on the request object.
164
165        This is the recommended method for extending the request object
166        and should be used in favor of providing a custom request
167        factory via
168        :meth:`pyramid.config.Configurator.set_request_factory`.
169
170        .. versionadded:: 1.4
171        """
172        if callable is not None:
173            callable = self.maybe_dotted(callable)
174
175        property = property or reify
176        if property:
177            name, callable = InstancePropertyHelper.make_property(
178                callable, name=name, reify=reify)
179        elif name is None:
180            name = callable.__name__
181        else:
182            name = get_callable_name(name)
183
184        def register():
185            exts = self.registry.queryUtility(IRequestExtensions)
186
187            if exts is None:
188                exts = _RequestExtensions()
189                self.registry.registerUtility(exts, IRequestExtensions)
190
191            plist = exts.descriptors if property else exts.methods
192            plist[name] = callable
193
194        if callable is None:
195            self.action(('request extensions', name), None)
196        elif property:
197            intr = self.introspectable('request extensions', name,
198                                       self.object_description(callable),
199                                       'request property')
200            intr['callable'] = callable
201            intr['property'] = True
202            intr['reify'] = reify
203            self.action(('request extensions', name), register,
204                        introspectables=(intr,))
205        else:
206            intr = self.introspectable('request extensions', name,
207                                       self.object_description(callable),
208                                       'request method')
209            intr['callable'] = callable
210            intr['property'] = False
211            intr['reify'] = False
212            self.action(('request extensions', name), register,
213                        introspectables=(intr,))
214
215    @action_method
216    def set_request_property(self, callable, name=None, reify=False):
217        """ Add a property to the request object.
218
219        .. deprecated:: 1.5
220            :meth:`pyramid.config.Configurator.add_request_method` should be
221            used instead.  (This method was docs-deprecated in 1.4 and
222            issues a real deprecation warning in 1.5).
223
224        .. versionadded:: 1.3
225        """
226        warnings.warn(
227            'set_request_propery() is deprecated as of Pyramid 1.5; use '
228            'add_request_method() with the property=True argument instead',
229            DeprecationWarning,
230        )
231        self.add_request_method(
232            callable, name=name, property=not reify, reify=reify)
233
234
235@implementer(IRequestExtensions)
236class _RequestExtensions(object):
237    def __init__(self):
238        self.descriptors = {}
239        self.methods = {}
240