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
|
import re
from zope.component import queryUtility
from zope.deprecation import deprecated
from zope.interface import implements
from zope.interface import alsoProvides
from zope.interface import classProvides
from routes import Mapper
from routes import request_config
from routes import url_for
from repoze.bfg.interfaces import IContextNotFound
from repoze.bfg.interfaces import IContextURL
from repoze.bfg.interfaces import IRoutesContext
from repoze.bfg.interfaces import IRoutesContextFactory
from repoze.bfg.interfaces import ITraverser
from repoze.bfg.interfaces import ITraverserFactory
_marker = ()
class DefaultRoutesContext(object):
implements(IRoutesContext)
def __init__(self, **kw):
self.__dict__.update(kw)
RoutesContext = DefaultRoutesContext
deprecated('RoutesContext',
"('from repoze.bfg.urldispatch import RoutesContext' is now "
"deprecated; instead use 'from repoze.bfg.urldispatch import "
"DefaultRoutesContext')",
)
class RoutesContextNotFound(object):
implements(IContextNotFound)
def __init__(self, msg):
self.msg = msg
_notfound = RoutesContextNotFound(
'Routes context cannot be found and no fallback "get_root"')
class RoutesRootFactory(Mapper):
""" The ``RoutesRootFactory`` is a wrapper for the root factory
callable passed in to the repoze.bfg ``Router`` at initialization
time. When it is instantiated, it wraps the root factory 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 route ``name`` name for the match. It
will be passed a context object that has attributes that are
present as Routes match arguments dictionary keys. If no Routes
route matches the current request, the 'fallback' get_root is
called."""
decorate_context = True
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
context_factory = queryUtility(IRoutesContextFactory,
default=DefaultRoutesContext)
if IRoutesContext.implementedBy(context_factory):
self.decorate_context = False
self.default_context_factory = context_factory
def has_routes(self):
return bool(self.matchlist)
def connect(self, *arg, **kw):
# we need to deal with our custom attributes specially :-(
factory = None
if '_factory' in kw:
factory = kw.pop('_factory')
result = Mapper.connect(self, *arg, **kw)
self.matchlist[-1]._factory = factory
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 isinstance(args, dict): # might be an empty dict
args = args.copy()
routepath = route.routepath
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
factory = route._factory
if factory is None:
context = self.default_context_factory(**kw)
if self.decorate_context:
alsoProvides(context, IRoutesContext)
else:
context = factory(**kw)
alsoProvides(context, IRoutesContext)
environ['wsgiorg.routing_args'] = ((), kw)
environ['bfg.route'] = route
return context
if self.get_root is None:
return _notfound
return self.get_root(environ)
class RoutesModelTraverser(object):
classProvides(ITraverserFactory)
implements(ITraverser)
def __init__(self, context):
self.context = context
def __call__(self, environ):
route = environ['bfg.route']
match = environ['wsgiorg.routing_args'][1]
subpath = match.get('subpath', [])
if subpath:
subpath = filter(None, subpath.split('/'))
if 'path_info' in match:
# this is stolen from routes.middleware; if the route map
# has a *path_info capture, use it to influence the path
# info and script_name of the generated environment
oldpath = environ['PATH_INFO']
newpath = match['path_info'] or ''
environ['PATH_INFO'] = newpath
if not environ['PATH_INFO'].startswith('/'):
environ['PATH_INFO'] = '/' + environ['PATH_INFO']
pattern = r'^(.*?)/' + re.escape(newpath) + '$'
environ['SCRIPT_NAME'] += re.sub(pattern, r'\1', oldpath)
if environ['SCRIPT_NAME'].endswith('/'):
environ['SCRIPT_NAME'] = environ['SCRIPT_NAME'][:-1]
return self.context, route.name, subpath, None, self.context, None
class RoutesContextURL(object):
""" The IContextURL adapter used to generate URLs for a context
object obtained via Routes URL dispatch. This implementation
juses the ``url_for`` Routes API to generate a URL based on
``environ['wsgiorg.routing_args']``. Routes context objects,
unlike traversal-based context objects, cannot have a virtual root
that differs from its physical root; furthermore, the physical
root of a Routes context is always itself, so the ``virtual_root``
function returns the context of this adapter unconditionally."""
implements(IContextURL)
def __init__(self, context, request):
self.context = context
self.request = request
def virtual_root(self):
return self.context
def __call__(self):
return url_for(**self.request.environ['wsgiorg.routing_args'][1])
|