I want to serve files from a folder with webob - python

I want to use webob.static.DirectoryApp. I just can't figure out how to do it:
From the example at http://docs.webob.org/en/latest/file-example.html my router looks like:
class Router(object):
def __init__(self, static_path=None):
self.routes = []
self.static_path = static_path if static_path is not None else os.path.join(os.path.dirname(os.path.realpath(__file__)), 'static')
self.static_app = DirectoryApp(self.static_path)
def add_route(self, template, controller, **vars):
if isinstance(controller, basestring):
controller = load_controller(controller)
self.routes.append((re.compile(template_to_regex(template)),
controller,
vars))
def __call__(self, environ, start_response):
req = Request(environ)
for regex, controller, vars in self.routes:
match = regex.match(req.path_info)
if match:
req.urlvars = match.groupdict()
req.urlvars.update(vars)
return controller(environ, start_response)
return exc.HTTPNotFound()(environ, start_response)
To create the application to serve:
def create_app():
router = Router()
#router.add_route('/', controller='app.controllers.default:index')
router.add_route('/', controller=default.index)
return router
This serves routes to my added controllers fine. I also added a self.static_app.
I just have no idea how to use it to serve static files from the static-folder! Can someone please enlighten me?

As #gawel wrote the answer is to add a route with DirectoryApp as the controller. There is another thing to notice for it to work directly from the example.
The extension template_to_regex in the example is adding a $ to the forcing us to write a route expression that covers the entire url to catch. The canbewhatever is never used and can be whatever. The important thing is the regex after the variable.
The add_route should look something like this:
router.add_route('/static{canbewhatever:.*}', controller=static_app)

Related

Get list of all routes defined in the Falcon app

I have RESTful routes in a Falcon app defined as below simplified codes.
My question is how to get list of all routes with their mapped handlers?
My google search results in little-helpful rpages - one similar issue resolved for Flask app here but no page talking about Falcon.
api = falcon.API(middleware=middleware)
api.add_route('/v1/model_names1', SomeHandlerMappingResource1())
api.add_route('/v1/model_names2', SomeHandlerMappingResource2())
class SomeHandlerMappingResource1:
def on_get(self, req, resp):
pass # some biz logic of GET method
def on_post(self, req, resp):
pass # some biz logic of POST method
# etc.
class SomeHandlerMappingResource2:
pass # similar to handler resource 1 above
The below code will return a list of tuple with URL and their respective Resources:
def get_all_routes(api):
routes_list = []
def get_children(node):
if len(node.children):
for child_node in node.children:
get_children(child_node)
else:
routes_list.append((node.uri_template, node.resource))
[get_children(node) for node in api._router._roots]
return routes_list
Output
[
('/v1/things', <v1.v1_app.ThingsResource object at 0x7f555186de10>),
('/v2/things', <v2.v2_app.ThingsResource object at 0x7f5551871470>),
('/v3/things/{name}', <v3.v3_app.ThingsResource object at 0x7f5551871ba8>)
]
I have read the package and derived this, however, I don't know any builtin method that will return this result.
If you don't like the above function, You can achieve something similar by extending the API class.
I have made a Github repo for versioning the Falcon app, from which you can get an idea of separating the URLs and their relative resources. Github Link
You can have a list of routes and add them by Extended the API class
URLs and Resources will be like:
from v1.v1_app import things
urls = [
('/things', things),
]

Routing static pages with Flask or Bottle

When using:
#route('/<filename>')
def server_static(filename):
return static_file(filename, root='.')
it allows to serve the request www.example.com/helloworld with the static HTML file /myapp/helloworld.
How to make www.example.com/anything be served by the static HTML file /myapp/html/anything.html, without having to hardcode each static filename anything in the Python code?
Note: the tricky part is that the request is /anything (and not /anything.html), and the static file is /myapp/html/anything.html (there are 20 or more such files)
If it's always .html you could consider simply doing filename += '.html'.
If your needs are more complex, you could write code that examines the directory to match various extensions. For example, if you wanted to match .html files, something like this would work and would be adjustable to work with various/multiple extensions or other conditions.
def is_html(filename):
return filename.lower().endswith('.html')
#route('/<filename>')
def server_static(filename):
root = '.'
all_filenames = os.listdir(root)
html_files = filter(is_html, all_filenames)
for fname in html_files:
if fname.startswith(filename):
return static_file(fname, root=root)
else:
return "No such file"
This can probably be abbreviated using fnmatch or something similar.
Although, you may want to simply consider simply having a webserver like Apache simply serve that path instead of using Flask. This would probably be a more secure option, too.

How to change the default folder "static" in web.py

One of the libraries my project requires that a folder with the CSS files that were in the application root called "themes". web.py by default, uses the folder "static" to return the static file and just rename her... not One of the solutions I found online was the following
in urls it is necessary to add the line
'/(?:img|js|css)/.*', 'app.controllers.public.public',
in app.controllers.public
require nex code
class public:
def GET(self):
public_dir = 'themes'
try:
file_name = web.ctx.path.split('/')[-1]
web.header('Content-type', mime_type(file_name))
return open(public_dir + web.ctx.path, 'rb').read()
except IOError:
raise web.notfound()
def mime_type(filename):
return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
but this solution does not work and files are still picked up from static...
is there a simple and clear solution to the problem? maybe we should change the name of the folder inside the web.py?
There's no simple way to change web.py's use of /static/, but there is a really easy way to add one of your own, with no need to add anything to your list of urls.
Look at web.py's code and you'll find web.httpserver.StaticMiddleware is where this is defined. Your job, create another WSGI middleware, with the new prefix. Then, because this is WSGI middleware, add your new class to the run chain.
from web.httpserver import StaticMiddleware
if __name__ == '__main__':
app = web.application(urls, globals())
app.run(lambda app: StaticMiddleware(app, '/themes/')
If that was too terse for you, consider it's the same as explicitly creating a new subclass and passing that subclass to app.run():
from web.httpserver import StaticMiddleware
class MyStaticMiddleware(StaticMiddleware):
def __init__(self, app, prefix='/themes/'):
StaticMiddleware.__init__(self, app, prefix)
if __name__ == '__main__':
app = web.application(urls, globals())
app.run(MyStaticMiddleware)
Note that '/static/' will still work, loading files from the /static/ subdirectory: All you've done is added another processor, which does the same thing, but from the '/themes/' subdirectory.

Retrieve an app configuration setting in a non-request context in Pyramid

In a pyramid app I am building (called pyplay), I need to retrieve an application setting that I have in development.ini. The problem is that the place where I am trying to get that setting cannot access the request variable (e.g. at the top level of a module file).
So, after looking at this example in the documentation: http://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/configuration/django_settings.html I started doing something very simple and hardcoded at first just to make it work.
Since my development.ini has this section: [app:main], then the simple example I tried is as follows:
from paste.deploy.loadwsgi import appconfig
config = appconfig('config:development.ini', 'main', relative_to='.')
but the application refuses to start and displays the following error:
ImportError: <module 'pyplay' from '/home/pish/projects/pyplay/__init__.pyc'> has no 'main' attribute
So, thinking that maybe I should put 'pyplay' instead of 'main', I went ahead, but I get this error instead:
LookupError: No section 'pyplay' (prefixed by 'app' or 'application' or 'composite' or 'composit' or 'pipeline' or 'filter-app') found in config ./development.ini
At this point I am a bit stuck and I don't know what am I doing wrong. Can someone please give me a hand on how to do this?
Thanks in advance!
EDIT: The following are the contents of my development.ini file (note that pish.theparam is the setting I am trying to get):
###
# app configuration
# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
###
[app:main]
use = egg:pyplay
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = en_US.utf8
pyramid.includes =
pyramid_debugtoolbar
pyramid_tm
sqlalchemy.url = mysql://user:passwd#localhost/pyplay?charset=utf8
# By default, the toolbar only appears for clients from IP addresses
# '127.0.0.1' and '::1'.
debugtoolbar.hosts = 127.0.0.1 ::1
pish.theparam = somevalue
###
# wsgi server configuration
###
[server:main]
use = egg:waitress#main
host = 0.0.0.0
port = 6543
###
# logging configuration
# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
###
[loggers]
keys = root, pyplay, sqlalchemy
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = INFO
handlers = console
[logger_pyplay]
level = DEBUG
handlers =
qualname = pyplay
[logger_sqlalchemy]
level = INFO
handlers =
qualname = sqlalchemy.engine
# "level = INFO" logs SQL queries.
# "level = DEBUG" logs SQL queries and results.
# "level = WARN" logs neither. (Recommended for production systems.)
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
The reason it's difficult to do in pyramid is because it's always a bad idea to have module-level settings. It means your module can only ever be used in one way per-process (different code-paths can't use your library in different ways). :-)
A hack around not having access to the request object is to at least hide your global behind a function call, so that the global can be different per-thread (which is basically per-request).
def get_my_param(registry=None):
if registry is None:
registry = pyramid.threadlocals.get_current_registry()
return registry.settings['pyplay.theparam']
Step 1: create a singleton class say in a file xyz_file
class Singleton:
def __init__(self, klass):
self.klass = klass
self.instance = None
def __call__(self, *args, **kwds):
if self.instance == None:
self.instance = self.klass(*args, **kwds)
return self.instance
#Singleton
class ApplicationSettings(object):
def __init__(self, app_settings=None):
if app_settings is not None :
self._settings = app_settings
def get_appsettings_object(self):
return self
def get_application_configuration(self):
return self._settings
Step 2: in "__ init__.py"
def main(global_config, **settings):
....
.......
app_settings = ApplicationSettings(settings)
Step 3: You should be able to access in any part of the code.
from xyz_file import ApplicationSettings
app_settings = ApplicationSettings().get_application_configuration()
Basically, if you don't have access to the request object, you're "off the rails" in Pyramid. To do things the Pyramid way, we make components and figure out where they belong in the Pyramid lifecycle, and they should always have direct access to either or both of the registry (the ZCA) and the request.
If what you're doing doesn't fit in the the request lifecycle, then it's probably something that should be instantiated at server start up time, normally in your init.py where you build and fill the configurator (our access to the registry). Don't be afraid to use the registry to allow other components to get at things 'pseudo-globally' later. So probably you want to make some kind of factory for your thing, call the factory in your start up code, perhaps passing in a reference to the registry as an argument, and then attach the object to the registry. If your component needs to interface with request-lifecycle code, give it a method that takes request as a param. Later anything that needs at this object can get it from registry, and anything this object needs to get at can be done either through registry or request.
You can totally use the hack in the other answer to get at the current global registry, but needing to do so is a code smell, you can def figure out a better design to eliminate that.
pseudo code example, in the server start up code:
# in in the init block where our Configurator has been built
from myfactory import MyFactory
registry.my_component = MyFactory(config.registry)
# you can now get at my_component from anywhere in a pyramid system
your component:
class MyFactory(oject):
def __init__(self, registry):
# server start up lifecycle stuff here
self.registry = registry
def get_something(self, request):
# do stuff with the rest of the system
setting_wanted = self.registry.settings['my_setting']
Pyramid Views work this way. They are actually ZCA multi-adapters of request and context. Their factory is registered in the registry, and then when the view look up process kicks off, the factory instantiates a view passing in request as a param.

Get list of all routes defined in the Flask app

I have a complex Flask-based web app. There are lots of separate files with view functions. Their URLs are defined with the #app.route('/...') decorator. Is there a way to get a list of all the routes that have been declared throughout my app? Perhaps there is some method I can call on the app object?
All the routes for an application are stored on app.url_map which is an instance of werkzeug.routing.Map. You can iterate over the Rule instances by using the iter_rules method:
from flask import Flask, url_for
app = Flask(__name__)
def has_no_empty_params(rule):
defaults = rule.defaults if rule.defaults is not None else ()
arguments = rule.arguments if rule.arguments is not None else ()
return len(defaults) >= len(arguments)
#app.route("/site-map")
def site_map():
links = []
for rule in app.url_map.iter_rules():
# Filter out rules we can't navigate to in a browser
# and rules that require parameters
if "GET" in rule.methods and has_no_empty_params(rule):
url = url_for(rule.endpoint, **(rule.defaults or {}))
links.append((url, rule.endpoint))
# links is now a list of url, endpoint tuples
See Display links to new webpages created for a bit more information.
I just met the same question. Those solutions above are too complex.
Just open a new shell under your project:
>>> from app import app
>>> app.url_map
The first 'app' is my project script: app.py,
another is my web's name.
(this solution is for the tiny web with a little route)
I make a helper method on my manage.py:
#manager.command
def list_routes():
import urllib
output = []
for rule in app.url_map.iter_rules():
options = {}
for arg in rule.arguments:
options[arg] = "[{0}]".format(arg)
methods = ','.join(rule.methods)
url = url_for(rule.endpoint, **options)
line = urllib.unquote("{:50s} {:20s} {}".format(rule.endpoint, methods, url))
output.append(line)
for line in sorted(output):
print line
It solves the the missing argument by building a dummy set of options. The output looks like:
CampaignView:edit HEAD,OPTIONS,GET /account/[account_id]/campaigns/[campaign_id]/edit
CampaignView:get HEAD,OPTIONS,GET /account/[account_id]/campaign/[campaign_id]
CampaignView:new HEAD,OPTIONS,GET /account/[account_id]/new
Then to run it:
python manage.py list_routes
For more on manage.py checkout: http://flask-script.readthedocs.org/en/latest/
Apparently, since version 0.11, Flask has a built-in CLI. One of the built-in commands lists the routes:
FLASK_APP='my_project.app' flask routes
Similar to Jonathan's answer I opted to do this instead. I don't see the point of using url_for as it will break if your arguments are not string e.g. float
#manager.command
def list_routes():
import urllib
output = []
for rule in app.url_map.iter_rules():
methods = ','.join(rule.methods)
line = urllib.unquote("{:50s} {:20s} {}".format(rule.endpoint, methods, rule))
output.append(line)
for line in sorted(output):
print(line)
Use cli command in Directory where your flask project is.
flask routes
Since you did not specify that it has to be run command-line, the following could easily be returned in json for a dashboard or other non-command-line interface. The result and the output really shouldn't be commingled from a design perspective anyhow. It's bad program design, even if it is a tiny program. The result below could then be used in a web application, command-line, or anything else that ingests json.
You also didn't specify that you needed to know the python function associated with each route, so this more precisely answers your original question.
I use below to add the output to a monitoring dashboard myself. If you want the available route methods (GET, POST, PUT, etc.), you would need to combine it with other answers above.
Rule's repr() takes care of converting the required arguments in the route.
def list_routes():
routes = []
for rule in app.url_map.iter_rules():
routes.append('%s' % rule)
return routes
The same thing using a list comprehension:
def list_routes():
return ['%s' % rule for rule in app.url_map.iter_rules()]
Sample output:
{
"routes": [
"/endpoint1",
"/nested/service/endpoint2",
"/favicon.ico",
"/static/<path:filename>"
]
}
If you need to access the view functions themselves, then instead of app.url_map, use app.view_functions.
Example script:
from flask import Flask
app = Flask(__name__)
#app.route('/foo/bar')
def route1():
pass
#app.route('/qux/baz')
def route2():
pass
for name, func in app.view_functions.items():
print(name)
print(func)
print()
Output from running the script above:
static
<bound method _PackageBoundObject.send_static_file of <Flask '__main__'>>
route1
<function route1 at 0x128f1b9d8>
route2
<function route2 at 0x128f1ba60>
(Note the inclusion of the "static" route, which is created automatically by Flask.)
You can view all the Routes via flask shell by running the following commands after exporting or setting FLASK_APP environment variable.
flask shell
app.url_map
inside your flask app do:
flask shell
>>> app.url_map
Map([<Rule '/' (OPTIONS, HEAD, GET) -> helloworld>,
<Rule '/static/<filename>' (OPTIONS, HEAD, GET) -> static>])
print(app.url_map)
That, is, if your Flask application name is 'app'.
It's an attribute of the instance of the Flask App.
See https://flask.palletsprojects.com/en/2.1.x/api/#flask.Flask.url_map

Categories

Resources