Reading config file as dictionary in Flask - python

in /instance/app.cfg I've configured :
test=test
In my flask file app.py :
with app.open_instance_resource('app.cfg') as f:
config = f.read()
print('config' , type(config))
Which prints config <class 'bytes'>
Reading the flask doc it does not detail how to read values from configuration files, how is this achieved ?
can config be read a dictionary instead of bytes ?
Update :
app.py :
# Shamelessly copied from http://flask.pocoo.org/docs/quickstart/
from flask import Flask
app = Flask(__name__)
import os
ac = app.config.from_pyfile(os.path.join('.', 'conf/api.conf'), silent=True)
logging_configuration = app.config.get('LOGGING')
if ac:
print(logging.config.dictConfig(ac))
#app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
api.conf :
myvar=tester
returns error :
/./conf/api.conf", line 1, in <module>
myvar=tester
NameError: name 'tester' is not defined
Update 2 :
app.py :
from flask import Flask
app = Flask(__name__)
import os
from logging.config import dictConfig
app.config.from_pyfile(os.path.join('.', 'conf/api.conf'), silent=True)
logging_configuration = app.config.get('LOGGING')
if logging_configuration:
print(dictConfig(logging_configuration))
#app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
api.conf :
LOGGING="tester"
returns error :
ValueError: dictionary update sequence element #0 has length 1; 2 is required

Reading the flask doc it does not detail how to read values from configuration files, how is this achieved ?
You can read about it in flask's doc here (title "configuring-from-files")
open_instance_resource is only a shortcut to make deal with files which are located in "instance folder" (a special place where you can store deploy specific files). It's not supposed to be a way to get your config as a dict.
Flask stores his config variable(app.config) as a dict object. You can update it via a bunch of methods: from_envvar, from_pyfile, from_object etc. Look at the source code
One of the typical ways how people read config files in flask-based apps:
app = Flask('your_app')
...
app.config.from_pyfile(os.path.join(basedir, 'conf/api.conf'), silent=True)
...
After that, you can use your dict-like config object as you want:
...
logging_configuration = app.config.get('LOGGING')
if logging_configuration:
logging.config.dictConfig(logging_configuration)
...
from flask import Flask
app = Flask(__name__)
import os
app.config.from_pyfile(os.path.join('.', 'conf/api.conf'), silent=True)
#app.route('/')
def hello_world():
return 'Hello World! {}'.format(app.config.get('LOGGING'))
if __name__ == '__main__':
app.run()

If you do app.config.from_pyfile('app.cfg') you can obtain your config as a dictionary by dict(app.config).
However, this dictionary will contain the whole configuration for your app not only those variables which were set by the configuration file.

Related

Configure Python Flask RESTplus app via TOML file

Based on the Configuration Handling Documents for Flask the section of Configuring from Files mentions a possibility to configure the App using files however it provides no example or mention of files that are not Python Files.
Is it possible to configure apps via files like config.yml or config.toml?
My Current flask app has configurations for two distinct databases and since I am using flask-restplus there are additional configurations for Swagger documentations.
Snippet:
from flask import Flask
app = Flask(__name__)
def configure_app(flask_app):
# MongoDB Setting
flask_app.config['MONGO_URI'] = 'mongodb://user:password#mongo_db_endpoint:37018/myDB?authSource=admin'
flask_app.config['MONGO_DBNAME'] = 'myDB'
# InfluxDB Setting
flask_app.config['INFLUXDB_HOST'] = 'my_influxdb_endpoint'
flask_app.config['INFLUXDB_PORT'] = 8086
flask_app.config['INFLUXDB_USER'] = 'influx_user'
flask_app.config['INFLUXDB_PASSWORD'] = 'influx_password'
flask_app.config['INFLUXDB_SSL'] = True
flask_app.config['INFLUXDB_VERIFY_SSL'] = False
flask_app.config['INFLUXDB_DATABASE'] = 'IoTData'
# Flask-Restplus Swagger Configuration
flask_app.config['RESTPLUS_SWAGGER_UI_DOC_EXPANSION'] = 'list'
flask_app.config['RESTPLUS_VALIDATE'] = True
flask_app.config['RESTPLUS_MASK_SWAGGER'] = False
flask_app.config['ERROR_404_HELP'] = False
def main():
configure_app(app)
if __name__ == "__main__":
main()
I would like to avoid setting large number of Environment Variables and wish to configure them using a config.toml file?
How is this achieved in flask?
You can use the .cfg files and from_envvar to achieve this. Create config file with all your environment variables.
my_config.cfg
MONGO_URI=mongodb://user:password#mongo_db_endpoint:37018
..
..
ERROR_404_HELP=False
Then set the env var APP_ENVS=my_config.cfg. Now all you need to do is use from_envvars given by Flask.
def configure_app(flask_app):
flask_app.config.from_envvar('APP_ENVS')
# configure any other things
# register blue prints if you have any
Quoting from documentation:
Configuring from Data Files
It is also possible to load configuration from a file in a format of
your choice using from_file(). For example to load from a TOML file:
import toml
app.config.from_file("config.toml", load=toml.load)
Or from a JSON file:
import json
app.config.from_file("config.json", load=json.load)
EDIT: The above feature is new for v2.0.
Link to the documentation reference:
Class Flask.config, method from_file(filename, load, silent=False)

Running a simple python flask application using .cfg config files. I can't seem to return the config values, getting a keying error with them

The aim of this program is just to return the values that are passed from a .cfg configuration file called 'defaults.cfg'.
I totally understand what should be getting passed here and to be honest the code for all intents and purposes is copied from an exercise, but it fails with a 'Keying error: (value)' (all values give the keying error, it's just whatever is first) and I don't know why. I've been unable to find a solution online and the code is the same in principle as a friend's more complicated program running a proper web application and his works just fine.
Apparently using capitals for the config keys is a thing and I've done that and I'm sure I have all the necessary libraries/binaries installed.
I'm doing this on Bash on Windows on Ubuntu.
Thanks in advance for any consideration.
default.cfg
[config]
DEBUG = True
IP_ADDRESS = 0.0.0.0
PORT = 5000
configuration.py
import ConfigParser
from flask import Flask
app = Flask(__name__)
#app.route('/')
def root():
return "Sup! Hollerin' at ya from the configuration testing app"
#app.route('/WTF/')
def tellMeh():
return app.config['PORT']
#app.route('/config/')
def config():
str = []
str.append(app.config['DEBUG'])
str.append('port:'+app.config['PORT'])
str.append('ip_address:'+app.config['IP'])
return '\t'.join(str)
def init(app):
config = ConfigParser.ConfigParser()
try:
config_location = "etc/defaults.cfg"
config.read(config_location)
app.config['DEBUG'] = config.get("config", "DEBUG")
app.config['IP'] = config.get("config", "IP_ADDRESS")
app.config['PORT'] = config.get("config", "PORT")
print "Succesfully read configs from: ", config_location
except:
print "Couldn't read configs from: ", config_location
if __name__ == '__main__':
init(app)
app.run(
host=app.config['IP'],
port=int(app.config['PORT']))
You'll get different behavior from that code depending on how you invoke it.
FLASK_APP=configuration.py flask run will skip the section at the bottom where init(app) is called
python configuration.py will run that section, calling init(app).
You might wish to move the call to init() to right below app = Flask(...).

Config parameter not reading from other locations

The parameter is not read from files other than the run.py . run.py is the Flask startup script
from run.py :
from flask import Flask
app = Flask(__name__)
print(app.config["PARAMETER"])
From any other python file in project following error is returned :
KeyError: 'PARAMETER'
How to read configuration file from other locations within project other than startup script file ?
Update :
Reading config file :
app.config.from_pyfile(os.path.join(".", "myconfig.conf"))
Contents of myconfig.conf :
PARAMETER = "test"
I think you just simply missed loading parameters.
If you do inline loadign of parameters, you would specifiy it as:
from flask import Flask
app = Flask(__name__)
app.config['PARAMETER_1'] = 'test'
assert app.config["PARAMETER_1"] == 'test'
The code above works, now how can one load similar data from file?
# prepare file
from pathlib import Path
Path("myconfig.conf").write_text("PARAMETER_2 = 'test2'\n")
# load it
app.config.from_pyfile("myconfig.conf")
# test it
assert app.config["PARAMETER_2"] == 'test2'
You can't print the PARAMETER key after the creation of the app because there is no any key into its configuration object which its name is PARAMETER. In order to do it, first create the app, import a simple object which holds the configuration and then use app.config.from_object(Config)
#config.py
class Config(object):
PARAMETER = 'my-parameter-value'
#you can add another keys
SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'
#__init__.py
from config import Config
app = Flask(__name__);
app.config.from_object(Config)

Python Flask "send_file()" method TypeError

I'm trying to read an image that the user uploads and then display the image back to them. I want to do this without saving the image file that is uploaded.
I have code like this:
from flask import Flask, redirect, render_template, request, url_for, send_file
from PIL import Image, ImageDraw
from io import BytesIO
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
img = Image.open(request.files['file'].stream)
byte_io = BytesIO()
img.save(byte_io, 'PNG')
byte_io.seek(0)
return send_file(byte_io, mimetype='image/png')
if __name__ == '__main__':
app.run(debug=1)
It produces this error:
TypeError: send_file() got an unexpected keyword argument 'mimetype'
I've tried replacing mimetype with other valid parameters and it will just give the same error but with the name of the new parameter. So I think the problem is with my bytes_io.
UPDATE:
To clarify, by send_file() I'm referring to the built in flask.send_file() method:
From flask document
The mimetype guessing requires a filename or an attachment_filename to be provided.
...
mimetype – the mimetype of the file if provided. If a file path is given, auto detection happens as fallback, otherwise an error will be raised.
So, you should provide that like this
return send_file(
io.BytesIO(obj.logo.read()),
download_name='logo.png',
mimetype='image/png'
)
This sample code should run normally,
from flask import Flask, request, send_file
app = Flask(__name__)
#app.route('/get_image')
def get_image():
if request.args.get('type') == '1':
filename = 'ok.gif'
else:
filename = 'error.gif'
return send_file(filename)
if __name__ == '__main__':
app.run()
If you've still got same error, maybe it's an environment problem. You can check your package version using pip freeze.

HTML file response in Pyramid

I am trying to adapt the to-do list app in the Pyramid tutorial here to create a Python hangman game. My directory structure is as follows:
/tasks
tasks.py
/static
custom.css
/templates
main.html
I want to have a single view main.html (i.e., just the game title, the current picture with blanks, score, etc., and a button for each letter to guess) that gets updated every time the user chooses a letter.
I figured I could do this by dynamically creating a new HTML display with each button press, similar to the jpg serving method shown here. But upon running python tasks.py and opening http://localhost:8080/# in the browser, it gives a server error and: "OSError: [Errno 2] No such file or directory: '/templates/main.html' in the terminal.
Here's my tasks.py:
import os
import logging
from pyramid.config import Configurator
from pyramid.events import NewRequest
from pyramid.events import ApplicationCreated
from pyramid.exceptions import NotFound
from pyramid.httpexceptions import HTTPFound
from pyramid.response import FileResponse
from pyramid.view import view_config
from wsgiref.simple_server import make_server
logging.basicConfig()
log = logging.getLogger(__file__)
here = os.path.dirname(os.path.abspath(__file__))
##view_config(route_name='home', renderer='main.mako')
#def main_page(request):
# print request #just checking
# return {}
#view_config(route_name='home')
def main_page(request):
response = FileResponse(os.path.join(here,'/templates/main.html'), \
request=request, content_type='text/html')
return response
if __name__ == '__main__':
settings = {}
settings['reload_all'] = True
settings['debug_all'] = True
settings['mako.directories'] = os.path.join(here, 'templates')
config = Configurator(settings=settings)
config.include('pyramid_mako')
config.add_route('home', '/')
config.add_static_view('static', os.path.join(here, 'static'))
config.scan()
app = config.make_wsgi_app()
server = make_server('0.0.0.0', 8080, app)
print 'server made'
server.serve_forever()
Considering the here declaration, I don't see how the file can't be found. As you can see from the commented out view_config, I tried using my initial HTML file (renamed main.mako) as the renderer at first, which worked fine and displayed the main page nicely.
But I don't think I can use that to update dynamically as desired. What am I missing here? Maybe I need to add something else to the settings dictionary?
You getting this error because you are using absolute path /templates/main.html.
Simple example how os.path.join works:
>>> os.path.join('/some/path/to/project', '/templates/main.html')
'/templates/main.html'
>>> os.path.join('/some/path/to/project', 'templates/main.html')
'/some/path/to/project/templates/main.html'
So try change os.path.join(here,'/templates/main.html') to os.path.join(here,'templates/main.html').

Categories