In the "Dispatching / Other Dispatchers" section of the CherryPy documentation, there is an example of Django-style regular-expression-to-view-function mapping definition, but there is no indication on how to attach this to cherrypy.tree.
How are you supposed to register this mapping?
Edit: Based on the "regex URL mapping" thread in the cherrypy-users Google group, I could figure out that to attach views using regular expressions, you need to use routes-style mapping using the cherrypy.dispatch.RoutesDispatcher class like so:
def hello(name='stranger'):
"""Sample view."""
return 'Hello, %s!'%name
dispatch = cherrypy.dispatch.RoutesDispatcher()
dispatch.connect('hello-1', '/hello', hello)
dispatch.connect('hello-2', '/hello/{name:([^/]+)}', hello)
cherrypy.tree.mount(None, config={
'/': {
'request.dispatch': dispatch,
}
})
Note the {argument-name:regular-expression} syntax in the URL pattern.
Is there a way to specifiy the route patterns using the list-of-pairs syntax as shown in the CherryPy documentation?
There's not any extra step required. During a request, cherrypy.tree performs a first routing stage, where the incoming request is mapped to an Application using its path-to-app mapping. When you call tree.mount(root=None, script_name='/', config=conf) at startup, the Tree creates a cherrypy.Application for you behind the scenes and mounts it at '/'.
Once the Application is found, its config takes over, and the "request.dispatch" config for the example app in the docs says "use the RoutesDispatcher for all URI's in this app". That RoutesDispatcher instance will then pass control of the request to the appropriate Controller.
The regex example in the docs isn't even that well-developed. You'd need to write a Dispatcher which uses it. That process "only" needs to find the handler and collect request.config, but those two activities can be very complex depending on the dispatch style chosen. See the existing dispatchers for inspiration.
Related
I have the following routing url rule defined and would like to test it.
app.add_url_rule('/api/v1.0/worker/bbc-stage-0', 'stage0', view_func=BBCStage0TaskView.as_view('bbc_stage0_taskview'))
The following tests if the path is correct:
def test_url_to_view_stage0_exists(self):
self.assertEqual(api.app.url_map._rules_by_endpoint['stage0'][0].rule, '/api/v1.0/worker/bbc-stage-0')
I haven't found a way to test if view_func is pointing to the right class. Is there a way to test that?
Werkzeug's Map maps paths to endpoints. The Flask app maps these endpoints to view functions in app.view_functions, which is used during app.dispatch_request. So to check what view has been connected to an endpoint, simply get it from that map. Since you're using a class based View, the real view function will be different every instantiation, so you instead test that the view_class is the same.
self.assertEqual(api.app.view_functions['stage0'].view_class, BBCStage0Task)
This is sort of a meaningless test, as you're basically testing Flask internals, which are already tested by Flask. Your own tests would be much more useful by simply using the test client to see if a request to a url returns what you expect.
with api.app.test_client() as client:
rv = client.get('/api/v1.0/worker/bbc-stage-0')
# assert something about the response, such as 'expected string' in rv.data
I'm trying to implement a batch request method in pyramid. I see in the docs that it's done with
subrequest = Request.blank('/relative/url')
request.invoke_subrequest(subrequest)
I'm just wondering how do you pass along the headers and cookies? Is it already done for you or is it
request.invoke_subrequest(subrequest, cookies=request.cookies, headers=request.headers)
What about parameters for different methods? The docs only have a POST keyword arg.
I feel like the docs are a little vague, or I can't find the correct docs on how to do this. Thanks
I'm just wondering how do you pass along the headers and cookies?
From http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/subrequest.html#subrequest-chapter :
The pyramid.request.Request.invoke_subrequest() API accepts two
arguments: a positional argument request that must be provided, and
use_tweens keyword argument that is optional; it defaults to False.
This tells us that the constructor only wants a Request object, and optionally a value for use_tweens, so no, this
request.invoke_subrequest(subrequest, cookies=request.cookies, headers=request.headers)
will not work.
Then, from http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/subrequest.html
It's a poor idea to use the original request object as an argument to
invoke_subrequest(). You should construct a new request instead as
demonstrated in the above example, using
pyramid.request.Request.blank(). Once you've constructed a request
object, you'll need to massage it to match the view callable you'd
like to be executed during the subrequest. This can be done by
adjusting the subrequest's URL, its headers, its request method, and
other attributes. The documentation for pyramid.request.Request
exposes the methods you should call and attributes you should set on
the request you create to massage it into something that will actually
match the view you'd like to call via a subrequest.
So basically, you need to configure your request before you pass it to invoke_subrequest().
Luckily there is an entire page that documents the Request class. There we can find a whole lot options to configure it, etc.
What about parameters for different methods? The docs only have a POST keyword arg.
Also on the Request class documentation page, there is this
method
Gets and sets the REQUEST_METHOD key in the environment.
And on http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/narr/viewconfig.html I found
request_method This value can be a string (typically "GET", "POST",
"PUT", "DELETE", or "HEAD") representing an HTTP REQUEST_METHOD
I must agree with you that the documentation is a little vague here and there, but I assume you can use it like this
request.method = 'POST'
# Or
request.method = 'GET'
# Etc.
Summary
You'll want to do it like this
subrequest = Request.blank('/relative/url')
# Configure the subrequest object
# set headers and other options you need.
request.invoke_subrequest(subrequest)
Note
I am aware this is not a 100% complete answer with some code that you can copy paste and it'll just work (especially regarding configuring the request object), but I think this answer contains some information that, at the very least, will get you on the right track and I hope it will be of some help to you.
The following code worked for me. It copies all (headers, cookies, query string, post parameters, etc.):
def subrequest(request, path):
subreq = request.copy()
subreq.path_info = path
response = request.invoke_subrequest(subreq)
return response
Somewhat late, but based on the above two answers here is how I did this. I didn't quite like the above answer to just copy everything. Looking at the documentation of the blank() method there is a kw argument and it says
All necessary keys will be added to the environ, but the values you pass in will take precedence. If you pass in base_url then wsgi.url_scheme, HTTP_HOST, and SCRIPT_NAME will be filled in from that value.
Assuming that the view's request contains the right header information and cookies that you need for your subrequest, you can use the following code:
#view_config( ... )
def something(request):
...
kwargs = { "cookies": request.cookies,
"host" : request.host,
}
req = Request.blank("/relative/url", **kwargs)
resp = request.invoke_subrequest(req)
Other header information (e.g. accept, accept_encoding, etc.) are properties of pyramid.request objects, and can be added to the kwargs dictionary like shown in the code snippet above.
The object returned by invoke_subrequest() is a Response object documented here.
I have such a problem: I want one method of the class to handle different URIs (for URI "/solution/add" and "solution/edit"). So I wrote such routing:
app = webapp2.WSGIApplication([webapp2.Route(r'/solutions/(add|edit)', handler='solution.SolutionPage:add_edit_solution'), ], debug=True)
And webapp2 gives 404 error. Could you please suggest the solution of this problem?
Ofcourse I can write different routes for every URI, but it's not so interesting.)
As the webapp2 docs indicate, you have to put the regex in angle brackets, with a colon separating name and expression. The name is optional, but
everything outside of <> is not interpreted as a regular expression to be matched
So something like this: '/blog/<:\d{4}>/<:\d{2}>' Or in your case, this:
webapp2.Route(r'/solutions/<:(add|edit)>',
handler='solution.SolutionPage:add_edit_solution')
If I can add something.
For my own purpose, I've try to create a handler which perform a little bit similar operation, but the point is that I've used self.request.host or self.request.route instead of arguments.
Doing this, and parsing the result with a switch case or if/elif/else loop, allow me to create a class named URIHandler which is use to route any kind of request to the correct ressources (even 404/500/yyy error pages) dynamicly whithout having to rewrite or add route for each new ressources.
So I'll be interested in comparing the two method to bench them a little bit.
My app serves multiple domains which I understand should be done by namespaces which I'm researching. Since multiple domains should have multiple analytics ID:s I get the analytics ID from the code but I want to make it even more configurable:
if os.environ.get('HTTP_HOST').endswith('.br') \
or os.environ['SERVER_NAME'].endswith('.br'):
data[u'analytics'] = 'UA-637933-12'
else:
data[u'analytics'] = 'UA-637933-18'
self.response.out.write(template.render(os.path.join(os.path.dirname(__file__),
'templates', name + '.html'), data))
The above sets analytics ID to ..-12 if it's my brazilian domain and to the other ID ...-18 if it is my dot com. But this is only for 2 domains and it's not easiliy generalizable. How can I achieve this function in a more scientific and scalable way so that it becomes easy to add my application to a domain without manually adding the domain to my application?
I suppose namespaces is the way to go here since the domains are google apps domains but I don't understand how to use namespaces:
def namespace_manager_default_namespace_for_request():
"""Determine which namespace is to be used for a request.
The value of _NAMESPACE_PICKER has the following effects:
If _USE_SERVER_NAME, we read server name
foo.guestbook-isv.appspot.com and set the namespace.
If _USE_GOOGLE_APPS_DOMAIN, we allow the namespace manager to infer
the namespace from the request.
If _USE_COOKIE, then the ISV might have a gateway page that sets a
cookie called 'namespace', and we set the namespace to the cookie's value
"""
name = None
if _NAMESPACE_PICKER == _USE_SERVER_NAME:
name = os.environ['SERVER_NAME']
elif _NAMESPACE_PICKER == _USE_GOOGLE_APPS_DOMAIN:
name = namespace_manager.google_apps_namespace()
elif _NAMESPACE_PICKER == _USE_COOKIE:
cookies = os.environ.get('HTTP_COOKIE', None)
if cookies:
name = Cookie.BaseCookie(cookies).get('namespace')
return name
I suppose I should use the namespace manager, get the namespace and set the analytics ID according to the namespace but how?
Thank you
The simplest way to do this is with a Python dict:
analytics_ids = {
'mydomain.br': 'UA-637933-12',
'mydomain.com': 'UA-637933-18',
}
data['analytics'] = analytics_ids[self.request.host]
If you have other per-domain stats, you may want to make each dictionary entry a tuple, a nested dict, or a configuration object of some sort, then fetch and store it against the current request for easy reference.
If you want to be able to reconfigure this at runtime, you could use a datastore model, but that will impose extra latency on requests that need to fetch it; it seems likely to me that redeploying each time you add a domain isn't likely to be a problem in your case.
Namespaces are tangential to what you're doing. They're a good way to divide up the rest of your data between different domains, but they're not useful for dividing up configuration data.
I presume you have two instances of the same application running.
Instead of fiddling with namespaces, I suggest you turn the Analytics ID into a configuration variable.
That is, either store it in a config file or a database your web is using. Then set one ID for each deployment (in each place your web is running from) and fetch it in the runtime.
For example:
Config file:
analyticsId="UA-637933-12"
Code:
data[u'analytics'] = getValueFromConfig("analyticsId")
where getValueFromConfig is a function you define to read the appropriate value. (To use configuration files effortlessly, you may use the ConfigParser module.)
Now you've gained a lot more flexibility - you don't have to do any checking and switching at runtime. You only have to define the value once per web site and be done with it.
I have something like using python routes, How to map request method like post, get, delete here...
mapper.connect("/user", controller=user_controller, action="user")
Add a condition, specify the HTTP method required and map to the appropriate controller action. For example, your snippet could be rewritten as:
mapper.connect("/user", controller=user_controller, action="get_user", conditions=dict(method=["GET"]))
mapper.connect("/user", controller=user_controller, action="add_user", conditions=dict(method=["POST"]))
See the docs at http://routes.groovie.org/setting_up.html#conditions
Note that if you only want to specify one HTTP method and have all other requests handled by one route, include a matching route without the condition after the more specific route:
## Handle GET requests
mapper.connect("/user", controller=user_controller, action="get_user", conditions=dict(method=["GET"]))
## Handle all other, non-GET requests
mapper.connect("/user", controller=user_controller, action="add_user")