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
|
from zope.interface import implements
from zope.interface import alsoProvides
from routes import Mapper
from routes import request_config
from repoze.bfg.interfaces import IRoutesContext
from repoze.bfg.interfaces import IContextNotFound
from zope.deferredimport import deprecated
from zope.deprecation import deprecated as deprecated2
_marker = ()
deprecated(
"('from repoze.bfg.urldispatch import RoutesContext' is now "
"deprecated; instead use 'from repoze.bfg.urldispatch import "
"DefaultRoutesContext')",
RoutesContext = "repoze.bfg.urldispatch:DefaultRoutesContext",
)
deprecated2('RoutesMapper',
'Usage of the ``RoutesMapper`` class is deprecated. As of '
'repoze.bfg 0.6.3, you should use the ``<route.. >`` ZCML '
'directive instead of manually creating a RoutesMapper.')
class DefaultRoutesContext(object):
implements(IRoutesContext)
def __init__(self, **kw):
self.__dict__.update(kw)
class RoutesMapper(object):
""" The ``RoutesMapper`` is a wrapper for the ``get_root``
callable passed in to the repoze.bfg ``Router`` at initialization
time. When it is instantiated, it wraps the get_root of an
application in such a way that the `Routes
<http://routes.groovie.org/index.html>`_ engine has the 'first
crack' at resolving the current request URL to a repoze.bfg view.
Any view that claims it is 'for' the interface
``repoze.bfg.interfaces.IRoutesContext`` will be called if its
*name* matches the Routes 'controller' name for the match. It
will be passed a context object that has attributes that match the
Routes match arguments dictionary keys. If no Routes route
matches the current request, the 'fallback' get_root is called.
.. warning:: This class is deprecated. As of :mod:`repoze.bfg`
0.6.3, you should use the ``<route.. >`` ZCML directive instead
of manually creating a RoutesMapper. See :ref:`urldispatch_chapter`
for more information.
"""
def __init__(self, get_root):
self.get_root = get_root
self.mapper = Mapper(controller_scan=None, directory=None,
explicit=True, always_scan=False)
self.mapper.explicit = True
self._regs_created = False
def __call__(self, environ):
if not self._regs_created:
self.mapper.create_regs([])
self._regs_created = True
path = environ.get('PATH_INFO', '/')
self.mapper.environ = environ
args = self.mapper.match(path)
if args:
context_factory = args.get('context_factory', _marker)
if context_factory is _marker:
context_factory = DefaultRoutesContext
else:
args = args.copy()
del args['context_factory']
config = request_config()
config.mapper = self.mapper
config.mapper_dict = args
config.host = environ.get('HTTP_HOST', environ['SERVER_NAME'])
config.protocol = environ['wsgi.url_scheme']
config.redirect = None
context = context_factory(**args)
alsoProvides(context, IRoutesContext)
return context
# fall back to original get_root
return self.get_root(environ)
def connect(self, *arg, **kw):
""" Add a route to the Routes mapper associated with this
request. This method accepts the same arguments as a Routes
*Mapper* object. One differences exists: if the
``context_factory`` is passed in with a value as a keyword
argument, this callable will be called when a model object
representing the ``context`` for the request needs to be
constructed. It will be called with the (all-keyword)
arguments supplied by the Routes mapper's ``match`` method for
this route, and should return an instance of a class. If
``context_factory`` is not supplied in this way for a route, a
default context factory (the ``DefaultRoutesContext`` class)
will be used. The interface
``repoze.bfg.interfaces.IRoutesContext`` will always be tacked
on to the context instance in addition to whatever interfaces
the context instance already supplies.
"""
self.mapper.connect(*arg, **kw)
class RoutesContextNotFound(object):
implements(IContextNotFound)
def __init__(self, msg):
self.msg = msg
class RoutesRootFactory(Mapper):
""" The ``RoutesRootFactory`` is a wrapper for the ``get_root``
callable passed in to the repoze.bfg ``Router`` at initialization
time. When it is instantiated, it wraps the get_root of an
application in such a way that the `Routes
<http://routes.groovie.org/index.html>`_ engine has the 'first
crack' at resolving the current request URL to a repoze.bfg view.
Any view that claims it is 'for' the interface
``repoze.bfg.interfaces.IRoutesContext`` will be called if its
*name* matches the Routes ``view_name`` name for the match and any
of the interfaces named in ``_provides``. It will be
passed a context object that has attributes that match the Routes
match arguments dictionary keys. If no Routes route matches the
current request, the 'fallback' get_root is called."""
def __init__(self, get_root=None, **kw):
self.get_root = get_root
kw['controller_scan'] = None
kw['always_scan'] = False
kw['directory'] = None
kw['explicit'] = True
Mapper.__init__(self, **kw)
self._regs_created = False
def has_routes(self):
return bool(self.matchlist)
def connect(self, *arg, **kw):
# we need to deal with our custom attributes specially :-(
factory = None
provides = ()
if '_provides' in kw:
provides = kw.pop('_provides')
if '_factory' in kw:
factory = kw.pop('_factory')
result = Mapper.connect(self, *arg, **kw)
self.matchlist[-1]._factory = factory
self.matchlist[-1]._provides = provides
return result
def __call__(self, environ):
if not self._regs_created:
self.create_regs([])
self._regs_created = True
path = environ.get('PATH_INFO', '/')
self.environ = environ # sets the thread local
match = self.routematch(path)
if match:
args, route = match
else:
args = None
if args:
args = args.copy()
routepath = route.routepath
factory = route._factory
if not factory:
factory = DefaultRoutesContext
config = request_config()
config.mapper = self
config.mapper_dict = args
config.host = environ.get('HTTP_HOST', environ['SERVER_NAME'])
config.protocol = environ['wsgi.url_scheme']
config.redirect = None
kw = {}
for k, v in args.items():
# Routes "helpfully" converts default parameter names
# into Unicode; these can't be used as attr names
if k.__class__ is unicode:
k = k.encode('utf-8')
kw[k] = v
context = factory(**kw)
provides = route._provides
for iface in provides:
alsoProvides(context, iface)
alsoProvides(context, IRoutesContext)
return context
if self.get_root is None:
# no fallback get_root
return RoutesContextNotFound(
'Routes context cannot be found and no fallback "get_root"')
# fall back to original get_root
return self.get_root(environ)
|