Accessing OS environment variables from Jinja2 template - python

Is it possible to access a OS environment variable directly from a Jinja2 template?

Following #Renier's pointer about custom filters in the comments, I figured out a possible solution.
Define a custom filter:
def env_override(value, key):
return os.getenv(key, value)
Install the filter in the environment:
env.filters['env_override'] = env_override
Use the filter as follows:
"test" : {{ "default" | env_override('CUSTOM') }}
Where the appropriate environment variable can be set as:
export CUSTOM=some_value
If the environment variable is set the output will be:
"test" : some_value
Otherwise:
"test" : default

I believe you can access environment variables like so:
{{ env['XMPP_DOMAIN'] or "localhost" }}
This is from an example in a config file that I saw recently for a Docker deployment of ejabberd.
hosts:
- "{{ env['XMPP_DOMAIN'] or "localhost" }}"
NOTE: You can see the rest of the example in the run file from the Github repo.
As I understand things the heavy lifting is done by this bit of code:
readonly PYTHON_JINJA2="import os;
import sys;
import jinja2;
sys.stdout.write(
jinja2.Template
(sys.stdin.read()
).render(env=os.environ))
"""
And this code is what is used to generate a template file:
cat ${CONFIGTEMPLATE} \
| python -c "${PYTHON_JINJA2}" \
> ${CONFIGFILE}
References
rroemhild/docker-ejabberd docker image via github

The answer here works beautifully but you can still get rid of the useless use of cat and compress it to a single statement:
python -c 'import os
import sys
import jinja2
sys.stdout.write(
jinja2.Template(sys.stdin.read()
).render(env=os.environ))' <$CONFIGTEMPLATE >$CONFIGFILE
P.S.: Stack Overflow does not allow formatted code in comments. Therefore I had to post this as a separate answer instead of commenting on https://stackoverflow.com/a/27984610/1070890.

In bash let's setup our example
export MYENVVAR=foo
$ nano example.py
from jinja2 import Template
import os
template = Template("Hello {{ env['MYENVVAR'] or 'DefaultVal' }}")
r = template.render(env=os.environ, name='somethingelse')
print(r)
Run template
$ python3 example.py
https://jinja.palletsprojects.com/en/2.11.x/intro/

Here is a list of variables that you can access from your template.
I was trying to access some app.config variables and I managed to do it by calling config:
{% if current_user.id == config['ADMIN_ID'] %}
######## SOME HTML ########
{% endif %}
Flask-Login adds the current_user variable to your templates

My simplest solution for FastAPI
templates.env.globals.update(getenv=os.getenv)
In Jinja2
{{ getenv("FOO") }}

Related

What is the best way to allow user to configure a Python package

I have situation like this. I am creating a Python package. That Python package needs to use Redis, so I want to allow the user of the package to define the Redis url.
Here's how I attempted to do it:
bin/main.py
from my_package.main import run
from my_package.config import config
basicConfig(filename='logs.log', level=DEBUG)
# the user defines the redis url
config['redis_url'] = 'redis://localhost:6379/0'
run()
my_package/config.py
config = {
"redis_url": None
}
my_package/main.py
from .config import config
def run():
print(config["redis_url"]) # prints None instead of what I want
Unfortunately, it doesn't work. In main.py the value of config["redis_url"] is None instead of the url defined in bin/main.py file. Why is that? How can I make it work?
I could pass the config to the run() function, but then if I run some other function I will need to pass the config to that function as well. I'd like to pass it one time ideally.

Basic CGI Example Help in Python

I'm working through Programming Python and am trying to work through the basic CGI example. I so far have the following files saved in the same directory (titled: cgi101.html and webserver.py respectively):
<html>
<title>Interactive Page</title>
<body>
<form method=POST action="cgi-bin/cgi101.py">
<p><b>Enter your name:</b>
<p><input type=text name=user>
<p><input type=submit>
</form>
</body>
import os, sys
from http.server import HTTPServer, CGIHTTPRequestHandler
webdir = '.'
port = 8080
os.chdir(webdir)
srvraddr = ("", port)
srvrobj = HTTPServer(srvraddr, CGIHTTPRequestHandler)
srvrobj.serve_forever()
Then in a sub directory titled cgi-bin I have the following script called cgi101.py saved:
#!/usr/bin/python
import cgi
form = cgi.FieldStorage()
print('Content-type: text/html\n')
print('<title>Reply Page</title>')
if not 'user' in form:
print('<h1>Who are you?</h1>')
else:
print('<h1>Hello <i>%s</i>!</h1>' % cgi.escape(form['user'].value))
I then follow these steps:
Run webserver.py from the directory where webserver.py and cgi101.html are located
Navigate to http://localhost:8080/cgi101.html this opens up as expected
When I enter a name and submit, initially I get an error saying the cgi script is not executable. If I go to cgi101.py and then run chmod 744 to make it executable, when I repeat the above steps the following URL renders: http://localhost:8080/cgi-bin/cgi101.py but no content renders whatsoever and the command line where I ran the server gives a FileNotFound error.
What am I doing wrong?
Any help appreciated.
Richard

build python list from ansible list of values

I'm trying to 12-factor my django settings by passing environment variables from ansible. My problem is that I don't know how to pass the variables host1 and host2 from an ansible yaml file, so that MYSETTING is a python list, not a string.
I'm trying to get:
MYSETTING = ['host1', 'host2'] # this should be a list, not a string
I have tried various options in ansible, but they both seem to result in MYSETTING being a string:
# settings.py
MYSETTING = os.environ.get(ANSIBLE_VALUE, [])
# ansible vars file
ANSIBLE_VALUE: "['host1', 'host2']"
or
# settings.py
MYSETTING = [os.environ.get(ANSIBLE_VALUE, None)]
# ansible vars file
ANSIBLE_VALUE: "'host1', 'host2'"
but neither option seems to work. I'm sure there is something simple, but I can't figure it out.
Your environment variable is a string type. You can change string into a list with ast library
import ast
MYSETTING = ast.literal_eval(os.environ.get(ANSIBLE_VALUE, '[]'))
# In settings.py
ADMINS = ['ws.kwak', 'jm0926.kim', 'viet.long', 'nam.nv12', 'phuong.nv2']
#In other files like views.py
from django.conf import settings
settings.ADMINS

Reload Flask app when template file changes

By default, when running Flask application using the built-in server (Flask.run), it monitors its Python files and automatically reloads the app if its code changes:
* Detected change in '/home/xion/hello-world/app.py', reloading
* Restarting with reloader
Unfortunately, this seems to work for *.py files only, and I don't seem to find any way to extend this functionality to other files. Most notably, it would be extremely useful to have Flask restart the app when a template changes. I've lost count on how many times I was fiddling with markup in templates and getting confused by not seeing any changes, only to find out that the app was still using the old version of Jinja template.
So, is there a way to have Flask monitor files in templates directory, or does it require diving into the framework's source?
Edit: I'm using Ubuntu 10.10. Haven't tried that on any other platforms really.
After further inquiry, I have discovered that changes in templates indeed are updated in real time, without reloading the app itself. However, this seems to apply only to those templates that are passed to flask.render_template.
But it so happens that in my app, I have quite a lot of reusable, parametrized components which I use in Jinja templates. They are implemented as {% macro %}s, reside in dedicated "modules" and are {% import %}ed into actual pages. All nice and DRY... except that those imported templates are apparently never checked for modifications, as they don't pass through render_template at all.
(Curiously, this doesn't happen for templates invoked through {% extends %}. As for {% include %}, I have no idea as I don't really use them.)
So to wrap up, the roots of this phenomenon seems to lie somewhere between Jinja and Flask or Werkzeug. I guess it may warrant a trip to bug tracker for either of those projects :) Meanwhile, I've accepted the jd.'s answer because that's the solution I actually used - and it works like a charm.
you can use
TEMPLATES_AUTO_RELOAD = True
From http://flask.pocoo.org/docs/1.0/config/
Whether to check for modifications of the template source and reload it automatically. By default the value is None which means that Flask checks original file only in debug mode.
In my experience, templates don't even need the application to restart to be refreshed, as they should be loaded from disk everytime render_template() is called. Maybe your templates are used differently though.
To reload your application when the templates change (or any other file), you can pass the extra_files argument to Flask().run(), a collection of filenames to watch: any change on those files will trigger the reloader.
Example:
from os import path, walk
extra_dirs = ['directory/to/watch',]
extra_files = extra_dirs[:]
for extra_dir in extra_dirs:
for dirname, dirs, files in walk(extra_dir):
for filename in files:
filename = path.join(dirname, filename)
if path.isfile(filename):
extra_files.append(filename)
app.run(extra_files=extra_files)
See here: http://werkzeug.pocoo.org/docs/0.10/serving/?highlight=run_simple#werkzeug.serving.run_simple
When you are working with jinja templates, you need to set some parameters. In my case with python3, I solved it with the following code:
if __name__ == '__main__':
app.jinja_env.auto_reload = True
app.config['TEMPLATES_AUTO_RELOAD'] = True
app.run(debug=True, host='0.0.0.0')
You need to set a TEMPLATES_AUTO_RELOAD property as True in your app config:
from flask import Flask
app = Flask(__name__)
app.config["TEMPLATES_AUTO_RELOAD"] = True
See more on http://flask.pocoo.org/docs/1.0/config/
Actually for me TEMPLATES_AUTO_RELOAD = True does not work (0.12 version). I use jinja2 and what i have done:
Create function before_request
def before_request():
app.jinja_env.cache = {}
Register it in application
app.before_request(before_request)
That's it.
Updated as of March 2021:
The flask CLI is recommended over app.run() for running a dev server, so if we want to use the CLI then the accepted solution can't be used.
In Flask 1.1 or later, the environment variable FLASK_RUN_EXTRA_FILES or the option --extra-files effectively do the same thing as the accepted answer. See also this github issue.
Example usage:
flask run --extra-files "app/templates/index.html"
# or
export FLASK_RUN_EXTRA_FILES="app/templates/index.html"
flask run
in Linux. To specify multiple extra files, separate file paths with colons., e.g.
export FLASK_RUN_EXTRA_FILES="app/templates/index.html:app/templates/other.html"
Whole directories are also supported:
flask run --extra-files app/templates/
What worked for me is just adding this:
#app.before_request
def before_request():
# When you import jinja2 macros, they get cached which is annoying for local
# development, so wipe the cache every request.
if 'localhost' in request.host_url or '0.0.0.0' in request.host_url:
app.jinja_env.cache = {}
(taken from #dikkini's answer)
To reload the application on the server AND in the browser I used the livereload package. Installed through the CLI with
$ pip install livereload
and running the code
from flask import Flask, render_template
app = Flask(__name__)
#app.route("/")
def hello():
return render_template("index.html")
if __name__ == '__main__':
from livereload import Server
server = Server(app.wsgi_app)
server.serve(host = '0.0.0.0',port=5000)
all answers here using the extra_files argument or TEMPLATES_AUTO_RELOAD config work to reload it on the server but for a smooth development experience without damaging your keyboard's F5 key I'd go with livereload
Using the latest version of Flask on Windows, using the run command and debug set to true; Flask doesn't need to be reset for changes to templates to be brought in to effect. Try Shift+F5 (or Shift plus the reload button) to make sure nothing it being cached.
See http://flask.pocoo.org/docs/1.0/quickstart/
and use FLASK_ENV=development
I had the same trouble. The solution is really simple though. Instead of this:
if __name__ == '__main__':
app.jinja_env.auto_reload = True
app.config["TEMPLATES_AUTO_RELOAD"] = True
app.run(debug=True)
Put
app.jinja_env.auto_reload = True
app.config["TEMPLATES_AUTO_RELOAD"] = True
above the main function. So final output for example:
from flask import Flask, app,render_template
app= Flask(__name__)
app.jinja_env.auto_reload = True
app.config["TEMPLATES_AUTO_RELOAD"] = True
#app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)
Templates are reloaded automatically, why not doing ctrl+f5 to refresh the webpage,
cause web-browsers usually save cache.
Adding app.config['TEMPLATES_AUTO_RELOAD'] = True after if __name__ == '__main__': doesn't work for me!
What works is adding app.config['TEMPLATES_AUTO_RELOAD'] = True after app = Flask(__name__)
Notice that I am using app.run(debug=True)

Bottle.py caching templates despite being in debug mode

I just built my first Bottle.py app on GAE. It's working except that when I change the templates I have to restart the dev server to see the changes. The docs say that template caching is supposed to be disabled when bottle.debug(True), and that you can call bottle.TEMPLTE.clear() as well, but neither of those work. I also tried setting run(reloader=True) but that causes an error. What am I doing wrong? Does bottle.debug() work for anyone else on GAE?
import bottle
bottle.debug(True)
bottle.TEMPLATES.clear()
#bottle.route('/')
def index(name='World'):
return bottle.template('main')
bottle.run(server='gae')
Update:
Instead of using bottle.run(server='gae'), I included the standard main() function myself and now it works.
def main():
app = bottle.default_app()
util.run_wsgi_app(app)
if __name__ == "__main__":
main()
The standard method introduced by Bottle/GAE doc is:
app = bottle.app()
then invoke dev_appserver.py, it reads app.yaml and import your app from the script you defined, and handle everything else for a GAE environment.
You shouldn't run your .py directly. Running from the bottle way will skip those handles from dev_appserver, including the template cached mechanism. Of course, using the util from Google does trick way and works, but according to uwsgi or other wsgi related projects' documents, the app variable in the script module is the object should be offered for the upper handling.
From the documentation:
Templates are cached in memory after compilation. Modifications made to the template files will have no affect until you clear the template cache. Call bottle.TEMPLATES.clear() to do so. Caching is disabled in debug mode.
The method run:
bottle.run( debug = True )
will enable debuggmode.
The default template is SimpleTemplate in stable version 0.11.6.
You can write your own adapter for your favourite template engine or
use one of the predefined adapters. Currently there are four fully
supported template engines:
Class,URL,Decorator,Render,function
SimpleTemplate, SimpleTemplate, Engine, view(), template()
MakoTemplate, http://www.makotemplates.org, mako_view(), mako_template()
CheetahTemplate, http://www.cheetahtemplate.org/, cheetah_view(), cheetah_template()
Jinja2Template, http://jinja.pocoo.org/, jinja2_view(), jinja2_template()
>>> Try using some other template engine, than the default. <<<
To use MakoTemplate as your default template engine, just import its
specialised decorator and render function:
from bottle import mako_view as view, mako_template as template
>>> Check that you dont have duplicated files in the view paths <<<
TEMPLATE_PATH = ['./', './views/']
>>> Print out templates dictionary <<<
print bottle.TEMPLATES

Categories

Resources