How do I create request handlers dynamically in CherryPy? - python

How would one go about creating request handler's at runtime with CherryPy? The default dispatch method shows creating the handler objects explicitly and building the tree that way:
class OnePage(object):
def index(self):
return "one page!"
index.exposed = True
class HelloWorld(object):
onepage = OnePage()
def index(self):
return "hello world"
index.exposed = True
cherrypy.quickstart(HelloWorld())
This is fine assuming your URL's are hard-coded. But what about user defined URLs? Is it possible to create the tree at runtime?
The reason I'm asking is I'd like to create a CMS on top of CherryPy where users can specify their own URL schemes. Eg. http://example.com/my/custom/url
Or would it be better to use the root index as a catch-all and simply process the url parameters that way?

You can use routes, if that's your thing, or build a root object with the default-dispatcher approach, I personally use the default routing, is more natural, and goes along with the growing of the code, but some people feel more comfortable with the notion of separated logic from routing to application, your choice.

Related

How to process URLs in Flask before Rules are mapped to endpoints?

I'm trying to implement a feature in my API where a part of a URL is optional. If supplied, I'd like to process it and stick some data in g. If not, I'd put some default info in g. Either way, I'd then remove it from the URL before Rules are mapped to endpoints. So I'd like the following two URLs to end up calling the same endpoint:
/bar/1 (I would fill in a default value for foo here)
/foo/32/bar/1
I want this same optional piece of URL in every endpoint I have. I think I could do this by brute force by decorating every endpoint but I have over 250 of them so I'd like something more elegant.
I'm using multiple Blueprints and I'd like to leave each endpoint as simple as I already have them if possible (the blueprints already have their own prefixes):
#blueprint1.route('/bar/<int:id>', methods=['GET'])
#blueprint2.route('/bar/<int:id>', methods=['GET'])
def get_foo():
I've tried the #url_defaults, #url_value_preprocessor, and #before_request decorators but it seems the Rule has already been mapped to the endpoint by then. Is there a hook to access the URL before mapping is done?
I made this work by subclassing the Flask class and overriding the create_url_adapter() function like so:
class MyFlask(Flask):
"""
MyFlask subclasses the standard Flask app class so that I can hook the URL parsing before the URL
is mapped to a Route. We do this so we can extract an optional "/foo/<int:foo_id>" prefix. If we
see this prefix, we store the int value for later processing and delete the prefix from the URL before
Flask maps it to a route.
"""
def _extract_optional_foo_id(self, path):
....
return path, foo_id # path has the optional part removed
def create_url_adapter(self, request):
"""
Augment the base class's implementation of create_url_adapter() by extracting an optional foo_id and modifying
the URL. The Flask function name is misleading: we aren't creating anything like an object. The "adapter" is
just this function call. We add our own behavior then call the super class's version of this function.
:param request: Flask's Request object
:return: the results of the Flask super class's create_url_adapter()
"""
# Must test for request. It is None when calling this for the app-wide scenario (instead of request-wide).
if request and request.environ:
(new_path, foo_id) = self._extract_optional_foo_id(request.environ['PATH_INFO'])
if foo_id is not None:
request.environ['PATH_INFO'] = new_path
request.foo_id_in_url = foo_id
return super(MyFlask, self).create_url_adapter(request)
Then in my app init code, instead of instantiating an instance of Flask, I do:
app = MyFlask( ... )
Usually URLs are mapped to the endpoints only once during the application initialization. More precisely - each time the code #app.route('/some_route') ... is being encountered by the interpreter first time. It's important to understand that mapping URLs to endpoints doesn't happen per each request (like it is in PHP, for example).
One way to add some default values to the endpoint is to override one of
Flask.route()
Flask.add_url_rule()
Blueprint.route()
in your successor app/blueprint class. Just put it into **options dict.

Pyramid resource: In plain English

I've been reading on the ways to implement authorization (and authentication) to my newly created Pyramid application. I keep bumping into the concept called "Resource". I am using python-couchdb in my application and not using RDBMS at all, hence no SQLAlchemy. If I create a Product object like so:
class Product(mapping.Document):
item = mapping.TextField()
name = mapping.TextField()
sizes = mapping.ListField()
Can someone please tell me if this is also called the resource? I've been reading the entire documentation of Pyramids, but no where does it explain the term resource in plain simple english (maybe I'm just stupid). If this is the resource, does this mean I just stick my ACL stuff in here like so:
class Product(mapping.Document):
__acl__ = [(Allow, AUTHENTICATED, 'view')]
item = mapping.TextField()
name = mapping.TextField()
sizes = mapping.ListField()
def __getitem__(self, key):
return <something>
If I were to also use Traversal, does this mean I add the getitem function in my python-couchdb Product class/resource?
Sorry, it's just really confusing with all the new terms (I came from Pylons 0.9.7).
Thanks in advance.
I think the piece you are missing is the traversal part. Is Product
the resource? Well it depends on what your traversal produces, it
could produce products.....
Perhaps it might be best to walk this through from the view back to
how it gets configured when the application is created...
Here's a typical view.
#view_config(context=Product, permission="view")
def view_product(context, request):
pass # would do stuff
So this view gets called when context is an instance of Product. AND
if the acl attribute of that instance has the "view"
permission. So how would an instance of Product become context?
This is where the magic of traversal comes in. The very logic of
traversal is simply a dictionary of dictionaries. So one way that this
could work for you is if you had a url like
/product/1
Somehow, some resource needs to be traversed by the segments of the
url to determine a context so that a view can be determined. What if
we had something like...
class ProductContainer(object):
"""
container = ProductContainer()
container[1]
>>> <Product(1)>
"""
def __init__(self, request, name="product", parent=None):
self.__name__ = name
self.__parent__ = parent
self._request = request
def __getitem__(self, key):
p = db.get_product(id=key)
if not p:
raise KeyError(key)
else:
p.__acl__ = [(Allow, Everyone,"view")]
p.__name__ = key
p.__parent__ = self
return p
Now this is covered in the documentation and I'm attempting to boil it
down to the basics you need to know. The ProductContainer is an object
that behaves like a dictionary. The "name" and "parent"
attributes are required by pyramid in order for the url generation
methods to work right.
So now we have a resource that can be traversed. How do we tell
pyramid to traverse ProductContainer? We do that through the
Configurator object.
config = Configurator()
config.add_route(name="product",
path="/product/*traverse",
factory=ProductContainer)
config.scan()
application = config.make_wsgi_app()
The factory parameter expects a callable and it hands it the current
request. It just so happens that ProductContainer.init will do
that just fine.
This might seem a little much for such a simple example, but hopefully
you can imagine the possibilities. This pattern allows for very
granular permission models.
If you don't want/need a very granular permission model such as row
level acl's you probably don't need traversal, instead you can use
routes with a single root factory.
class RootFactory(object):
def __init__(self, request):
self._request = request
self.__acl__ = [(Allow, Everyone, "view")] # todo: add more acls
#view_config(permission="view", route_name="orders")
def view_product(context, request):
order_id, product_id = request.matchdict["order_id"], request.matchdict["product_id"]
pass # do what you need to with the input, the security check already happened
config = Configurator(root_factory=RootFactory)
config.add_route(name="orders",
path="/order/{order_id}/products/{product_id}")
config.scan()
application = config.make_wsgi_app()
note: I did the code example from memory, obviously you need all the necessary imports etc. in other words this isn't going to work as a copy/paste
Have you worked through http://michael.merickel.org/projects/pyramid_auth_demo/ ? If not, I suspect it may help. The last section http://michael.merickel.org/projects/pyramid_auth_demo/object_security.html implements the pattern you're after (note the example "model" classes inherit from nothing more complex than object).

AppEngine elegant way to handle similar requests

I'm making a server that can let clients upload and download data of different models. Is there some elegant way handle the requests?
More precisely, I don't want to do something like this,
app = webapp.WSGIApplication([
('/my_upload_and_download_url/ModelA/(.*)', MyRequestHandlerForA),
('/my_upload_and_download_url/ModelB/(.*)', MyRequestHandlerForB),
('/my_upload_and_download_url/ModelC/(.*)', MyRequestHandlerForC),
])
run_wsgi_app(app)
since what I do inside the handler would all be the same. For example,
class MyRequestHandlerForX(webapp.RequestHandler):
def get(self, key=None):
# return the instance with the designated key
def post(self, key=None):
# create/get the model instance
# iterate through the property list of the instance and set the values
the only difference among the handlers is to create instance for different models. The urls are alike, and the handlers are almost the same.
I checked this post about redirect requests to other handlers, and I've also read some methods to create an instance by a class name; but I think neither of them is good.
Anyone has a good solution?
p.s. This is my first post here. If there is anything inappropriate please tell me, thanks.
How you do this depends largely on the details of your code in the request handler. You can do a fairly generic one like this:
class ModelHandler(webapp.RequestHandler):
def get(self, kind, key):
model = db.class_for_kind(kind)
instance = model.get(key)
# Do something with the instance - eg, print it out
def post(self, kind, key):
model = db.class_for_kind(kind)
instance = model.create_from_request(self.request)
application = webapp.WSGIApplication([
('/foo/([^/]+)/([^/]+)', ModelHandler),
])
def main():
run_wsgi_app(application)
if __name__ == '__main__':
main()
This assumes you define a 'create_from_request' class method on each model class; you probably don't want to do it exactly this way, as it tightly couples model definitions with the forms used to input them; instead, you probably want to store a mapping of kind name or class to handler function, or do your forms and creation entirely automatically by reflecting on the properties of the class. Since you haven't specified what it is about doing this you're unsure about, it's hard to be more specific.
Also note the inclusion of a main() and other boilerplate above; while it will work the way you've pasted it, adding a main is substantially more efficient, as it allows the App Engine runtime to avoid having to evaluate your module on every request.
In your case I'd probably just have everything hit the same url path, and put the specifics in the GET parameters, like /my_upload_and_download_url?model=modelA.
You can also use webapp2 (http://webapp-improved.appspot.com/guide/app.html) which has a bunch of url routing support.
You could parse out the url path and do a look up, like this:
import urlparse
model_lookup = {'ModelA':ModelA,'ModelB':ModelB, 'ModelC':ModelC}
class MyRequestHandler(webapp.RequestHandler):
def get(self):
url = urlparse.urlparse(self.request.uri)
path_model = url.path.replace('/my_upload_and_download_url/','')
model = model_lookup[path_model]
...
Which allows you to use the same class for each path:
app = webapp.WSGIApplication([
('/my_upload_and_download_url/ModelA/(.*)', MyRequestHandler),
('/my_upload_and_download_url/ModelB/(.*)', MyRequestHandler),
('/my_upload_and_download_url/ModelC/(.*)', MyRequestHandler),
])
run_wsgi_app(app)

delegate from one view to another

I am using Pyramid with different views. I am wondering if it is possible to "delegate" parts of a views job to another view (another route).
For example:
http://localhost:6543/sample_project/testruns/testrun001/report.html?action=edit
=> delegate to:
http://localhost:6543/sample_project/testruns/testrun001/report.json
the views I am using:
# report:
#view_config(context=Root, route_name='report_route')
def report_view(context, request):
...
if 'edit' in request.GET.getall('action'):
# TODO: delegate to code_view
???
...
# render report from report.json
# editor:
#view_config(context=Root, route_name='report_edit_route')
#view_config(context=Root, route_name='code_route')
def code_view(context, request):
....
You can directly call views, they simply won't be going through the pyramid router mechanism which applies the permission and other such parameters to the view. Presumably if you are trying to call it, however, you already know these things.
In reality, you probably just want to refactor the common functionality into a separate function that each of your views can then delegate part of the work to.

Django: Obtaining the absolute URL without access to a request object

I have a model like the one below. When an instance is created, I want to send out an e-mail to an interested party:
class TrainStop(models.Model):
name = models.CharField(max_length=32)
notify_email = models.EmailField(null=True, blank=True)
def new_stop_created(sender, instance, created, *args, **kwargs):
# Only for new stops
if not created or instance.id is None: return
# Send the status link
if instance.notify_email:
send_mail(
subject='Stop submitted: %s' % instance.name,
message='Check status: %s' % reverse('stop_status', kwargs={'status_id':str(instance.id),}),
from_email='admin#example.com',
recipient_list=[instance.notify_email,]
)
signals.post_save.connect(new_stop_created, sender=TrainStop)
However, the reverse call only returns the path portion of the URL. Example: /stops/9/status/. I need a complete URL like http://example.com/stops/9/status/. How would I go about retrieving the hostname and port (for test instances that do not use port 80) of the current website?
My initial thought was to make this available via a variable in settings.py that I could then access as needed. However, thought someone might have a more robust suggestion.
There's the sites framework, as yedpodtrzitko mentioned, but, as you mentioned, it's very much a manual setup.
There's requiring a setting in settings.py, but it's only slightly less manual than setting up sites. (It can handle multiple domains, just as well as sites and the SITE_ID setting can).
There's an idea for replacing get_absolute_url, that would make stuff like this easier, though I think its implementation suffers from the same problem (how to get the domain, scheme [http vs https], etc).
I've been toying with the idea of a middleware that examines incoming requests and constructs a "most likely domain" setting of some sort based on the frequency of the HTTP HOST header's value. Or perhaps it could set this setting on each request individually, so you could always have the current domain to work with. I haven't gotten to the point of seriously looking into it, but it's a thought.
For getting current site there's object Site:
If you don’t have access to the request object, you can use the
get_current() method of the Site model’s manager. You should then
ensure that your settings file does contain the SITE_ID setting. This
example is equivalent to the previous one:
from django.contrib.sites.models import Site
def my_function_without_request():
current_site = Site.objects.get_current()
if current_site.domain == 'foo.com':
# Do something
pass
else:
# Do something else.
pass
More info: http://docs.djangoproject.com/en/dev/ref/contrib/sites/

Categories

Resources