Python: Mako template lookups per app - python

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.

Related

Why does this "after_create" function not initialize default values into my database?

Background/Goal
I'm trying to create a study prep website and as part of it I would like to initialize a database labelled "Topic" with some default values. The contents of this table are just meant to be initialized once when the database is first created. I'm using Flask and SQLAlchemy for this project.
The Problem
I've tried to achieve this by defining a function like so (Note: The "..." mark pieces of code present which I believe aren't very relevant to this problem. If you feel like I'm likely leaving out something important, please inform me)
db = SQLAlchemy()
DB_NAME = "database.db"
def create_app():
app = Flask(__name__)
app.config["SECRET_KEY"] = "hwiu2222"
app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{DB_NAME}"
db.init_app(app)
...
from .models import User, Topic
create_database(app)
...
#listens_for(Topic.__table__, "after_create")
def insert_topics(*args, **kwargs):
topics = (genfromtxt("src/static/topics.csv", delimiter=',', skip_header=1, dtype=None)).tolist()
for topic in topics:
new_topic = Topic(title=topic[0], subject=topic[1], level=topic[2])
db.session.add(new_topic)
db.session.commit()
return app
def create_database(app):
if not path.exists("CIE Website/" + DB_NAME):
db.create_all(app=app)
To clarify, this "create_app" function is located in an init file in my directory and is called within a separate main python script.
from src import create_app
app = create_app()
if __name__ == "__main__":
app.run(debug=True)
This code runs just fine and I'm able to open my website without any errors, but the problem is that the default values I wanted to be initialized don't actually get initialized. My "Topic" table just remains empty.
What I've Tried
So what I assume is going wrong is that the "insert_topics" function is not getting called for whatever reason. To try and fix this I've attempted to simply relocate said function, such as to before the "create_database" call or within my main script after calling the "create_app" function, but all of that just results in the same error (There's even more to this error message, but it's quite long and I'm not sure how important the other information is, so I'm only including this ending statement for now).
RuntimeError: No application found. Either work inside a view function or push an application context. See http://flask-sqlalchemy.pocoo.org/contexts/.
Now, all of this stuff is still very much new to me so what I assume to be going wrong may be completely off the mark. It's also possible that I'm overlooking some glaring mistake that's causing all of this. For example, there could be something wrong with the "insert_topics" function itself. Regardless, I hope that one of you will be able to help me figure this out.
P.S.
I apologize in advance for any mistakes I may have made in writing this quesiton. It's my first time doing anything like this.

Is it possible to display file size in a directory served using http.server in python?

I've served a directory using
python -m http.server
It works well, but it only shows file names. Is it possible to show created/modified dates and file size, like you see in ftp servers?
I looked through the documentation for the module but couldn't find anything related to it.
Thanks!
http.server is meant for dead-simple use cases, and to serve as sample code.1 That's why the docs link right to the source.
That means that, by design, it doesn't have a lot of configuration settings; instead, you configure it by reading the source and choosing what methods you want to override, then building a subclass that does that.
In this case, what you want to override is list_directory. You can see how the base-class version works, and write your own version that does other stuff—either use scandir instead of listdir, or just call stat on each file, and then work out how you want to cram the results into the custom-built HTML.
Since there's little point in doing this except as a learning exercise, I won't give you complete code, but here's a skeleton:
class StattyServer(http.server.HTTPServer):
def list_directory(self, path):
try:
dirents = os.scandir(path)
except OSError:
# blah blah blah
# etc. up to the end of the header-creating bit
for dirent in dirents:
fullname = dirent.path
displayname = linkname = dirent.name
st = dirent.stat()
# pull stuff out of st
# build a table row to append to r
1. Although really, it's sample code for an obsolete and clunky way of building servers, so maybe that should be "to serve as sample code to understand legacy code that you probably won't ever need to look at but just in case…".

Wrong path for statics when Dynamic routing a template Bottle

i'm working with bottle framework and i found this problem.
When i used static path everything works fine, but when i used dynamic routing the path for .css and .js change and give not file found error.
I have this method:
#get('/mod_user/<id_user>')
def mod_user(id_user):
user = driver.get_user_by_id(id_user)
return template('moduser', user=user)
And i have this one for static files, which works fine for path like '/contact', etc:
#route('/static/:path#.+#', name='static')
def static(path):
return static_file(path, root='static')
the problem come beacuse add /mod_user to the path for search static:
http://10.141.0.63:8080/mod_user/static/css/formularioadd.css
I tried changing path for static in several ways and nothing works.
Some idea?
thank you all
You have no route that matches /mod_user/static.
(Also, you're needlessly using a regex.)
Have you tried something like this?
#route('/mod_user/static/<path:path>')
def static(path):
...

How to abstract 3rd party implementation details from the core functionality?

Here is an example of the problem and requirement .
Scenario : We have a Web App , The Controller has a function that does HTML to PDF conversion . We have to switch to various engines depending on the requirement and advancement in Technology . This means we need to keep changing the core file . It can even be a web service that does my job and not implemented locally .
Overall the current architecture in pseudo Python is :
Controller
def requestEntry():
"""Entry point"""
html = REQUEST['html'] , css = REQUEST['css']
createPDF(html,css)
def createPDF(html,css):
"""Sigh"""
#Hardcoding Begines
configured_engine = getXMLOption('configured_engine')
if configured_engine == "SuperPDFGen":
#Returns SuperGen's PDF Blob
supergen = SuperGen(html,css)
return supergen.pdfout()
if configured_engine = "PISA":
pisa = PISA(html,css)
return pisa.pdf_out()
The problem here is every new engine requirement is a code change in the controller .And suppose we find a new technology we have to upgrade the core software version .
Solving it :
One way I can think of is to define a simple class like this :
def createPdf(data,engine):
pdf_render = PDFRender(html,css,engine)
return pdf_render.pdf_out()
So now we keep out PDFRender astracted - core version need not be changed any more for a implemetation change , but we need to code the engines with a If ladder in the PDFRender Class .
To extend this idea , i could have a convention engine would be the module name too . But this own't adapt to a URL if its given as Engine .
def createPdf(data,engine):
#Convert the string called engine to a Module ! Sigh
import engine Engine!!
pdf_render = engine.Engine(data)
return pdf_render()
Are there any suggested paradigm's or a plugin adapter mechanism for this? It should take a URL or a Python Module as input and get the job done . New implementations should be standalone and pluggable to the existing code with no version changes in core feature . How can I do that ? I think the core should talk in terms of service (either a python module , sub process or a url ) this is what I trying to achieve .
Add a package where you stick your renderers, one Python module for each, the module being named after the "engine" and the renderer class being named "PDFRenderer". Then in the package's __init__ scan the modules in the directory, import the PDFRenderer class from each module, and build a enginename:PDFRenderer mapping, and use it to get the PDFRenderer class for the engine.
It looks like right now you have to change a couple of lines of code each time you change pdf renderer. The code is simple and easy to understand.
Sure, you can abstract it all out, and use configuration files, and add a plugin architecture. But what will you gain? Instead of changing a couple of lines of code, you'll instead change a plugin or a configuration file, and that machinery will need to be maintained: for example, if you decide you need to pass in some flags to one of the renderers, then you have to add that functionality to your configuration file or whatever abstraction you've chosen.
How many times do you think you'll change pdf renderer? My guess would be at most once a year. Keep your code simple, and don't build complex solutions until you really need them.

AppEngine and Django: including a template file

As the title suggests, I'm using Google App Engine and Django.
I have quite a bit of identical code across my templates and would like to reduce this by including template files. So, in my main application directory I have the python handler file, the main template, and the template I want to include in my main template.
I would have thought that including {% include "fileToInclude.html" %} would work on its own but that simply doesn't include anything. I assume I have to set something up, maybe using TEMPLATE_DIRS, but can't figure it out on my own.
EDIT:
I've tried:
TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), 'templates'), )
But to no avail. I'll try some other possibilities too.
First, you should consider using template inheritance rather than the include tag, which is often appropriate but sometimes far inferior to template inheritance.
Unfortunately, I have no experience with App Engine, but from my experience with regular Django, I can tell you that you need to set your TEMPLATE_DIRS list to include the folder from which you want to include a template, as you indicated.
I found that it works "out of the box" if I don't load Templates first and render them with a Context object. Instead, I use the standard method shown in the AppEngine tutorial.
I am having the same problem and tracked it down into the ext.webapp package. In template.py, you'll find this comment on line 33:
Django uses a global setting for the directory in which it looks for templates.
This is not natural in the context of the webapp module, so our load method
takes in a complete template path, and we set these settings on the fly
automatically. Because we have to set and use a global setting on every
method call, this module is not thread safe, though that is not an issue
for applications.
See line 92 in the same file. You can see the template dirs being squashed:
directory, file_name = os.path.split(abspath)
new_settings = {
'TEMPLATE_DIRS': (directory,),
'TEMPLATE_DEBUG': debug,
'DEBUG': debug,
}
UPDATE: Here is the workaround which worked for me - http://groups.google.com/group/google-appengine/browse_thread/thread/c3e0e4c47e4f3680/262b517a723454b6?lnk=gst&q=template_dirs#262b517a723454b6
I've done the following to get around using includes:
def render(file, map={}):
return template.render(
os.path.join(os.path.dirname(__file__), '../templates', file), map)
table = render("table.html", {"headers": headers, "rows": rows})
final = render("final.html", {"table": table})
self.response.out.write(final)

Categories