CherryPy: Perform specific action if user goes to specific URL - python

I'm new to CherryPy so please bear with me. What I'd like to do is perform a specific action when a user goes to a specific URL. The URL will mostly always be the same except for one part. The URL will be something like: http://myserver.mydomain.com/show_item.cgi?id=12345. The URL will always be the same except for the 12345. I want to take the "string of numbers", plop them into a variable, and re-direct to another URL that will be built on the fly based on that variable. I have the logic for building out the URL -- I just don't know how to "intercept" the incoming URL and extract the "string of numbers". Any help would be greatly appreciated.

Oh, it was not clear from your question that you really wanted to mock CGI-like file handler URL. Though the answer is still there, it may be harder to find because of recent changes in documentation.
You can use dots in a URI like /path/to/my.html, but Python method names don’t
allow dots. To work around this, the default dispatcher converts all dots in the
URI to underscores before trying to find the page handler. In the example,
therefore, you would name your page handler def my_html.
So with the following you can navigate your browser to http://127.0.0.1:8080/show_item.cgi?id=1234.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import cherrypy
config = {
'global' : {
'server.socket_host' : '127.0.0.1',
'server.socket_port' : 8080,
'server.thread_pool' : 8
}
}
class App:
#cherrypy.expose
def show_item_cgi(self, id):
raise cherrypy.HTTPRedirect('https://google.com/search?q={0:d}'.format(int(id)))
if __name__ == '__main__':
cherrypy.quickstart(App(), '/', config)

saaj I think really did answer my question by pointing me here. My follow-up is unique to my scenario so I'll research that more and ask a different question if I need to.

Related

datastore use BlobKey to download

In the google appengine datastore, there is a BlobKey (labled as csv). The key is in the following format: encoded_gs_file:we1o5o7klkllfekomvcndhs345uh5pl31l. I would like to provide a download button to save this information. My question is, what is the endpoint that i can use to access this. More information about the BlobKey is below.
The web app is being run using dev_appserver.py and uses python 2.7 (Django) as the backend. Currently, a button exists, but when clicking on it, it returns a 404 error. The download link that the button provides is:
https://localhost:8080/data?key=encoded_gs_file:dwndjndwamwljioihkm
My question is, how can i use the blobkey to generate a url that can be downloaded; or how can i check my code base to find how the url that i can use is being generated?
class BlobstoreDataServer(blobstore_handlers.BlobstoreDownloadHandler):
def get(self):
k = str(urllib.unquote(self.request.params.get('key','')))
logging.debug(k)
blob_info = blobstore.BlobInfo.get(k)
logging.debug(blob_info)
if (not blob_info) or (not blob_info.size):
self.error(404)
return
self.response.headers['X-Blob-Size'] = str(blob_info.size)
self.response.headers['Content-Type'] = blob_info.content_type
self.response.headers['Content-Disposition'] = (u'attachment; filename=%s' % blob_info.filename).encode('utf-8')
self.send_blob(blob_info)
Edit: New Images
Do you have a Request Handler for the route /data that does something like this?
from google.appengine.ext import blobstore
class DisplayBlob(blobstore_handlers.BlobstoreDownloadHandler):
def get(self):
blob_key = self.request.GET['key']
self.send_blob(ndb.BlobKey(blob_key))
self.response.headers['Content-Type'] = 'text/plain'
EDIT:
Ok so the 404 is probably being thrown by you by this line: self.error(404) right? Add a logging.warn('BlobstoreDataServer is throwing 404') right before to make sure. Also are you seeing this line logging.debug(k) print (I want to confirm that BlobstoreDataServer is even getting hit)? You may need to do logging.getLogger().setLevel(logging.DEBUG) to see it.
So that means blobstore.BlobInfo.get(k) is returning None. Focus on making sure that is working first, you can do this in the interactive console.
Go to http://localhost:8000/blobstore
Open one of them and copy the Key (encoded_gs_file:dwndjndwamwljioih...)
Go to the Interactive console (http://localhost:8000/console) and enter this code and hit 'EXECUTE' and make sure it is able to find it:
If that step didn't work, then then something is up with your dev_appserver.py's blobstore emulator
If that works, then just manually paste that same key at the end of your download link:
https://localhost:8080/data?key=<paste_encoded_gs_file_key_here>
If this step didn't work then something is up with your download handler, maybe this line is transforming the key somehow str(urllib.unquote(self.request.params.get('key','')))
If this step worked then something is up with your code that generates this link https://localhost:8080/data?key=..., maybe you're actually writing to a different gcs_filename than what you are constructing a different BlobKey for.

How can I generate multiple URL paths with cherrypy?

I feel like I'm running into a brick wall, as I'm not getting anywhere with this, and I believe, simple task.
I'm trying to generate a URL by the likes of '/path/to/url', but upon gazing at multiple StackOverflow Q&A's, the official documentation for cherrypy, I still cannot seem to wrap my head around the issue.
Here's my code so far:
import details
import input_checker as input
import time
import cherrypy
class Index(object):
#cherrypy.expose
def input(self):
return input.check_input()
#cherrypy.expose
def stream(self):
while True:
return 'Hey'
#return input.check_input()
time.sleep(3)
if __name__ == '__main__':
index = Index()
cherrypy.tree.mount(index.stream(), '/input/stream', {})
cherrypy.config.update(
{'server.socket_host': '0.0.0.0'})
cherrypy.quickstart(index)
So essentially, I want to be able to visit http://127.0.0.1:8080/input/stream, and I will be returned with the given result.
After executing this code, and multiple variants of it, I'm still being returned with a 404 not found error, and I'm not sure what I need to do, in order to get it working.
Any tips and/or supporting documentation that I may have skimmed over?
Thanks guys.
So there are couple problems here, why do you use MethodDispatcher do you actually need it?
To serve you stream function on /input/stream you have to mount it as such:
cherrypy.tree.mount(index.stream(), '/input/stream', your_config)
note /input/stream instead of /stream.
But because you're using MethodDispatcher this will likely make your endpoint return 405 as GET is not allowed on this endpoint - to fix that just remove the MethodDispatcher bit.
But if you do require MethodDispatcher you will have to refactor a bit to something like that:
class Stream:
exposed = True # to let cherrypy know that we're exposing all methods in this one
def GET(self):
return something
stream = Stream()
cherrypy.tree.mount(stream , '/index/stream',
{'/':
{'request.dispatch': cherrypy.dispatch.MethodDispatcher()}
}
)
Also make sure to not actually call your methods when mounting them into cherrypy tree, just pass in the name of the function/class

Web2Py Production - making redirections on default index with parameters (maybe with nginx)

I am trying to create redirects using web2py from effectively the default index page (or just the route of the domain/package).
Some keywords (such as 'about', stored in a list) wouldn't redirect. However, all not in that list would redirect.
The desired behaviour is:
https://startbean.com/about -> No redirect
https://startbean.com/myc -> https://startbean.com/company/myc
The default page that is shown at startbean.com is from the package 'default' and is called 'index'. If the redirect was as in the below, it would be easy:
https://startbean.com/default/about -> No redirect
https://startbean.com/default/index/myc -> https://startbean.com/default/company/myc
because the myc is a URL argument. But when it is from the root, Web2Py tries to open a package called 'myc' and then finds no page (index or controller function) so errors.
What is the best way of handling this? I was trying with routes.py, but couldn't figure out a way to do this (am pretty sure it is not supported). I thought about a redirect for the token after / to a page called /default/redirect/<token> which would then decide about the redirect, but there's no way to stop the infinite loop. Another possible solution was a tweak to the nginx config so redirect when there is one token after the /, but again I think this causes a problem with the about.
Maybe there is a catch-all function for controllers that I haven't found? I've gone through the web2py book and found nothing - any ideas very welcome!
Thanks,
Sam
You can use the parameter-based rewrite system. In routes.py include:
routers = dict(
BASE=dict(
default_application='yourapp',
default_controller='default',
default_function='company',
functions=['index', 'company', 'user', 'download', 'call']
),
)
Note, functions should be a list of all functions in the default.py controller.
If you go to /some_token, it will route to /yourapp/default/company/some_token, unless some_token is "yourapp", "default", "company", or any of the functions in the functions list (in which case, it will assume you are actually requesting the "yourapp" app, the "default" controller, or the particular function from the controller).
Note, if you simply go to the root URL (i.e., http://yourdomain.com/), it will route to /yourapp/default/company/, with no URL arg, so you should be prepared for that case.
So I found the solution (sorry for the delay in posting):
In the routes.py at the route of web2py directory, I added a rule in routes_in, so that this was in the my file:
routes_in = (
('/(?!about)$token', '/company/$token'),
)
to manage the default app (removing the application name and the default package name from the URL), I did this (not necessary for the redirects to work):
routers = dict(
BASE = dict(default_application='startbean'),
)
And it all worked :)

Contents of request.endpoint[:5]

I'm going through Miguel Grinbergs Flask book and at one point he uses the following line:
request.endpoint[:5] != 'auth.'
I know [:5] is a slice operation, but I'm not sure why it is being used here. What does the list consist of that we only want elements 0-5?
What does the list consist of that we only want elements 0-5?
To be precise, request.endpoint is not a list, it's a string. And it doesn't matter what the rest of it contains, the code is only concerned with it beginning with 'auth.':
('auth.somethingsomething'[:5] == 'auth.') is True
request.endpoint is the name the current view function was registered as, for example auth.login is the name of the def login(): view. Views that have a prefix like prefix. were registered on a blueprint, which groups related views. So the code is checking if the current view being handled is part of the auth blueprint.
If you're curious about what value it contains, you can add a debugging breakpoint to the code and inspect it:
# ... previous app code ...
import pdb; pdb.set_trace()
request.endpoint[:5] != 'auth.'
Then run and test the code. When it hits that point, it'll pause execution and give you a pdb shell, which will let you look at the request object and its endpoint attribute.
you can checking on terminal by
venv $ python manage.py shell
import flask from request
print(request.endpoint)

Python: Mako template lookups per app

I'm using cherrypy with Mako as a template engine.
I want Mako to lookup different directories based on what app is being requested.
I.e.
I have three 'apps': Site, Admin and Install.
They all have their own template folder, structure looking something like:
/template
/template/site
/template/admin
/template/install
/template/system
/system contains some system wide templates, like 404 pages, etc.
I'm using Twiseless as a reference whilst trying to get to grips with cherrypy / mako, but I'm stuck with how to do this.
Read on for a brief overview of how I've tried to do this, but a warning: I think I'm going about this completely the wrong way! :) So, if you have any ideas/pointers, it might be a good idea to save yourself the trouble of reading any further than this.
In my main file, server.py, I do something like:
from libs.plugins.template import MakoTemplatePlugin
engine = cherrypy.engine
makoTemplate = MakoTemplatePlugin(engine, self.base_dir)
setTemplateDirs(makoTemplate, self.template_path)
MakoTemplatePlugin is a slightly modified version of the plugin by the same name found in Twiseless, linked above.
What this code does is set the TemplateLookup to use the default template directories from my global config file. i.e.
/template
/template/system
Then, each time an app is loaded, I call a function (setTemplateDirs) to update the directories where Mako searches.
I thought this would work, but it doesn't. Initially I made the error of creating a new instance of MakoTemplatePlugin for each app. This just resulted in them all being called on each page load, starting with the first one instantiated, containing just the basic, non-app specific directories.
As this was called first, it was triggering a 404 error, as it was searching in the wrong folders.
I instead made sure to pass a reference to the MakeTemplatePlugin to all of my apps. I thought if I ran setTemplateDirs each time each app is called, this would solve the problem... but it doesn't.
I don't know where to put the function so it will run every time a page is requested...
e.g.
# /apps/site/app.py
import somemodule.setTemplateDirs
class Site(object, params):
def __init__(self):
self.params = params
self.makoTemplate = params['makoTemplate']
self.base_path = params['base_path']
setTemplateDirs(self.makoTemplate, self.base_path, '', '/')
#cherrypy.expose
#cherrypy.tools.render(template='index.html')
def index(self):
pass
This obviously just works when the application is first loaded... I tried moving the update function call into a seperate method update and tried calling that for each page, e.g:
#cherrypy.exposed
#cherrypy.tools.render(template='index.html')
#update
def index(self):
pass
But this just gives me config related errors.
Rather than to continue to mess about with this, there must be an easier way.
How would you do it?
Thanks a lot,
Tom
I got this working. Thanks to stephan for providing the link to the mako tool example: http://tools.cherrypy.org/wiki/Mako.
I just modified that slightly to get it working.
If anyone's wondering, the basis of it is that you define tools.mako.directories in your global config, you can then override that in individual app config files.
e.g.
server.conf
...
tools.mako.directories: ['', 'system']
...
site.conf
...
tools.mako.directories: ['site', 'system']
...
I did some extra work to translate the relative URIs to absolute paths, but the crux of it is explained above.

Categories