Related
I'm developing a simple FastAPI app and I'm using Pydantic for storing app settings.
Some settings are populated from the environment variables set by Ansible deployment tools but some other settings are needed to be set explicitly from a separate env file.
So I have this in config.py
class Settings(BaseSettings):
# Project wide settings
PROJECT_MODE: str = getenv("PROJECT_MODE", "sandbox")
VERSION: str
class Config:
env_file = "config.txt"
And I have this config.txt
VERSION="0.0.1"
So project_mode env var is being set by deployment script and version is being set from the env file. The reason for that is that we'd like to keep deployment script similar across all projects, so any custom vars are populated from the project specific env files.
But the problem is that when I run the app, it fails with:
pydantic.error_wrappers.ValidationError: 1 validation error for Settings
VERSION
field required (type=value_error.missing)
So how can I populate Pydantic settings model from the local ENV file?
If the environment file isn't being picked up, most of the time it's because it isn't placed in the current working directory. In your application in needs to be in the directory where the application is run from (or if the application manages the CWD itself, to where it expects to find it).
In particular when running tests this can be a bit confusing, and you might have to configure your IDE to run tests with the CWD set to the project root if you run the tests from your IDE.
The path of env_file is relative to the current working directory, which confused me as well. In order to always use a path relative to the config module I set it up like this:
env_file: f"{pathlib.Path(__file__).resolve().parent}/config.txt"
I've built the flask app that designed to run inside docker container. It will
accept POST HTTP Methods and return appropriate JSON response if the header key matched with the key that I put inside docker-compose environment.
...
environment:
- SECRET_KEY=fakekey123
...
The problem is: when it comes to testing. The app or the client fixture of
flask (pytest) of course can't find the docker-compose environment. Cause the app didn't start from docker-compose but from pytest.
secret_key = os.environ.get("SECRET_KEY")
# ^^ the key loaded to OS env by docker-compose
post_key = headers.get("X-Secret-Key")
...
if post_key == secret_key:
RETURN APPROPRIATE RESPONSE
.....
What is the (best/recommended) approach to this problem?
I find some plugins
one,
two,
three to do this. But I asked here if there is any more "simple"/"common" approach. Cause I also want to automate this test using CI/CD tools.
You most likely need to run py.test from inside of your container. If you are running locally, then there's going to be a conflict between what your host machine is seeing and what your container is seeing.
So option #1 would be to using docker exec:
$ docker exec -it $containerid py.test
Then option #2 would be to create a script or task in your setup.py so that you can run a simpler command like:
$ python setup.py test
My current solution is to mock the function that read the OS environment. OS ENV is loaded if the app started using docker. In order to make it easy for the test, I just mock that function.
def fake_secret_key(self):
return "ffakefake11"
def test_app(self, client):
app.secret_key = self.fake_secret_key
# ^^ real func ^^ fake func
Or another alternative is using pytest-env as #bufh suggested in comment.
Create pytest.ini file, then put:
[pytest]
env =
APP_KEY=ffakefake11
I'm investigating how to develop a decent web app with Python. Since I don't want some high-order structures to get in my way, my choice fell on the lightweight Flask framework. Time will tell if this was the right choice.
So, now I've set up an Apache server with mod_wsgi, and my test site is running fine. However, I'd like to speed up the development routine by making the site automatically reload upon any changes in py or template files I make. I see that any changes in site's .wsgi file causes reloading (even without WSGIScriptReloading On in the apache config file), but I still have to prod it manually (ie, insert extra linebreak, save). Is there some way how to cause reload when I edit some of the app's py files? Or, I am expected to use IDE that refreshes the .wsgi file for me?
Run the flask run CLI command with debug mode enabled, which will automatically enable the reloader. As of Flask 2.2, you can pass --app and --debug options on the command line.
$ flask --app main.py --debug run
--app can also be set to module:app or module:create_app instead of module.py. See the docs for a full explanation.
More options are available with:
$ flask run --help
Prior to Flask 2.2, you needed to set the FLASK_APP and FLASK_ENV=development environment variables.
$ export FLASK_APP=main.py
$ export FLASK_ENV=development
$ flask run
It is still possible to set FLASK_APP and FLASK_DEBUG=1 in Flask 2.2.
If you are talking about test/dev environments, then just use the debug option. It will auto-reload the flask app when a code change happens.
app.run(debug=True)
Or, from the shell:
$ export FLASK_DEBUG=1
$ flask run
http://flask.palletsprojects.com/quickstart/#debug-mode
In test/development environments
The werkzeug debugger already has an 'auto reload' function available that can be enabled by doing one of the following:
app.run(debug=True)
or
app.debug = True
You can also use a separate configuration file to manage all your setup if you need be. For example I use 'settings.py' with a 'DEBUG = True' option. Importing this file is easy too;
app.config.from_object('application.settings')
However this is not suitable for a production environment.
Production environment
Personally I chose Nginx + uWSGI over Apache + mod_wsgi for a few performance reasons but also the configuration options. The touch-reload option allows you to specify a file/folder that will cause the uWSGI application to reload your newly deployed flask app.
For example, your update script pulls your newest changes down and touches 'reload_me.txt' file. Your uWSGI ini script (which is kept up by Supervisord - obviously) has this line in it somewhere:
touch-reload = '/opt/virtual_environments/application/reload_me.txt'
I hope this helps!
If you're running using uwsgi look at the python auto reload option:
uwsgi --py-autoreload 1
Example uwsgi-dev-example.ini:
[uwsgi]
socket = 127.0.0.1:5000
master = true
virtualenv = /Users/xxxx/.virtualenvs/sites_env
chdir = /Users/xxx/site_root
module = site_module:register_debug_server()
callable = app
uid = myuser
chmod-socket = 660
log-date = true
workers = 1
py-autoreload = 1
site_root/__init__.py
def register_debug_server():
from werkzeug.debug import DebuggedApplication
app = Flask(__name__)
app.debug = True
app = DebuggedApplication(app, evalex=True)
return app
Then run:
uwsgi --ini uwsgi-dev-example.ini
Note: This example also enables the debugger.
I went this route to mimic production as close as possible with my nginx setup. Simply running the flask app with it's built in web server behind nginx it would result in a bad gateway error.
For Flask 1.0 until 2.2, the basic approach to hot re-loading is:
$ export FLASK_APP=my_application
$ export FLASK_ENV=development
$ flask run
you should use FLASK_ENV=development (not FLASK_DEBUG=1)
as a safety check, you can run flask run --debugger just to make sure it's turned on
the Flask CLI will now automatically read things like FLASK_APP and FLASK_ENV if you have an .env file in the project root and have python-dotenv installed
app.run(use_reloader=True)
we can use this, use_reloader so every time we reload the page our code changes will be updated.
I got a different idea:
First:
pip install python-dotenv
Install the python-dotenv module, which will read local preference for your project environment.
Second:
Add .flaskenv file in your project directory. Add following code:
FLASK_ENV=development
It's done!
With this config for your Flask project, when you run flask run and you will see this output in your terminal:
And when you edit your file, just save the change. You will see auto-reload is there for you:
With more explanation:
Of course you can manually hit export FLASK_ENV=development every time you need. But using different configuration file to handle the actual working environment seems like a better solution, so I strongly recommend this method I use.
Use this method:
app.run(debug=True)
It will auto-reload the flask app when a code change happens.
Sample code:
from flask import Flask
app = Flask(__name__)
#app.route("/")
def index():
return "Hello World"
if __name__ == '__main__':
app.run(debug=True)
Well, if you want save time not reloading the webpage everytime when changes happen, then you can try the keyboard shortcut Ctrl + R to reload the page quickly.
From the terminal you can simply say
export FLASK_APP=app_name.py
export FLASK_ENV=development
flask run
or in your file
if __name__ == "__main__":
app.run(debug=True)
Enable the reloader in flask 2.2:
flask run --reload
Flask applications can optionally be executed in debug mode. In this mode, two very convenient modules of the development server called the reloader and the debugger are enabled by default.
When the reloader is enabled, Flask watches all the source code files of your project and automatically restarts the server when any of the files are modified.
By default, debug mode is disabled. To enable it, set a FLASK_DEBUG=1 environment variable before invoking flask run:
(venv) $ export FLASK_APP=hello.py for Windows use > set FLASK_APP=hello.py
(venv) $ export FLASK_DEBUG=1 for Windows use > set FLASK_DEBUG=1
(venv) $ flask run
* Serving Flask app "hello"
* Forcing debug mode on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 273-181-528
Having a server running with the reloader enabled is extremely useful during development, because every time you modify and save a source file, the server automatically restarts and picks up the change.
To achieve this in PyCharm set 'Environment Variables' section to:
PYTHONUNBUFFERED=1;
FLASK_DEBUG=1
For Flask 'run / debug configurations'.
To help with fast automatic change in browser:
pip install livereload
from livereload import Server
if __name__ == '__main__':
server = Server(app.wsgi_app)
server.serve()
Next, Start your server again:
eg. your .py file is app.py
python app.py
I believe a better solution is to set the app configuration. For me, I built the tool and then pushed it to a development server where I had to set up a WSGI pipeline to manage the flask web app. I had some data being updated to a template and I wanted it to refresh every X minutes (WSGI deployment for the Flask site through APACHE2 on UBUNTU 18). In your app.py or whatever your main app is, add app.config.update dictionary below and mark TEMPLATES_AUTO_RELOAD=True, you will find that any templates that are automatically updated on the server will be reflected in the browser. There is some great documentation on the Flask site for configuration handling found here.
app = Flask(__name__)
app.config.update(
TEMPLATES_AUTO_RELOAD=True
)
I'm investigating how to develop a decent web app with Python. Since I don't want some high-order structures to get in my way, my choice fell on the lightweight Flask framework. Time will tell if this was the right choice.
So, now I've set up an Apache server with mod_wsgi, and my test site is running fine. However, I'd like to speed up the development routine by making the site automatically reload upon any changes in py or template files I make. I see that any changes in site's .wsgi file causes reloading (even without WSGIScriptReloading On in the apache config file), but I still have to prod it manually (ie, insert extra linebreak, save). Is there some way how to cause reload when I edit some of the app's py files? Or, I am expected to use IDE that refreshes the .wsgi file for me?
Run the flask run CLI command with debug mode enabled, which will automatically enable the reloader. As of Flask 2.2, you can pass --app and --debug options on the command line.
$ flask --app main.py --debug run
--app can also be set to module:app or module:create_app instead of module.py. See the docs for a full explanation.
More options are available with:
$ flask run --help
Prior to Flask 2.2, you needed to set the FLASK_APP and FLASK_ENV=development environment variables.
$ export FLASK_APP=main.py
$ export FLASK_ENV=development
$ flask run
It is still possible to set FLASK_APP and FLASK_DEBUG=1 in Flask 2.2.
If you are talking about test/dev environments, then just use the debug option. It will auto-reload the flask app when a code change happens.
app.run(debug=True)
Or, from the shell:
$ export FLASK_DEBUG=1
$ flask run
http://flask.palletsprojects.com/quickstart/#debug-mode
In test/development environments
The werkzeug debugger already has an 'auto reload' function available that can be enabled by doing one of the following:
app.run(debug=True)
or
app.debug = True
You can also use a separate configuration file to manage all your setup if you need be. For example I use 'settings.py' with a 'DEBUG = True' option. Importing this file is easy too;
app.config.from_object('application.settings')
However this is not suitable for a production environment.
Production environment
Personally I chose Nginx + uWSGI over Apache + mod_wsgi for a few performance reasons but also the configuration options. The touch-reload option allows you to specify a file/folder that will cause the uWSGI application to reload your newly deployed flask app.
For example, your update script pulls your newest changes down and touches 'reload_me.txt' file. Your uWSGI ini script (which is kept up by Supervisord - obviously) has this line in it somewhere:
touch-reload = '/opt/virtual_environments/application/reload_me.txt'
I hope this helps!
If you're running using uwsgi look at the python auto reload option:
uwsgi --py-autoreload 1
Example uwsgi-dev-example.ini:
[uwsgi]
socket = 127.0.0.1:5000
master = true
virtualenv = /Users/xxxx/.virtualenvs/sites_env
chdir = /Users/xxx/site_root
module = site_module:register_debug_server()
callable = app
uid = myuser
chmod-socket = 660
log-date = true
workers = 1
py-autoreload = 1
site_root/__init__.py
def register_debug_server():
from werkzeug.debug import DebuggedApplication
app = Flask(__name__)
app.debug = True
app = DebuggedApplication(app, evalex=True)
return app
Then run:
uwsgi --ini uwsgi-dev-example.ini
Note: This example also enables the debugger.
I went this route to mimic production as close as possible with my nginx setup. Simply running the flask app with it's built in web server behind nginx it would result in a bad gateway error.
For Flask 1.0 until 2.2, the basic approach to hot re-loading is:
$ export FLASK_APP=my_application
$ export FLASK_ENV=development
$ flask run
you should use FLASK_ENV=development (not FLASK_DEBUG=1)
as a safety check, you can run flask run --debugger just to make sure it's turned on
the Flask CLI will now automatically read things like FLASK_APP and FLASK_ENV if you have an .env file in the project root and have python-dotenv installed
app.run(use_reloader=True)
we can use this, use_reloader so every time we reload the page our code changes will be updated.
I got a different idea:
First:
pip install python-dotenv
Install the python-dotenv module, which will read local preference for your project environment.
Second:
Add .flaskenv file in your project directory. Add following code:
FLASK_ENV=development
It's done!
With this config for your Flask project, when you run flask run and you will see this output in your terminal:
And when you edit your file, just save the change. You will see auto-reload is there for you:
With more explanation:
Of course you can manually hit export FLASK_ENV=development every time you need. But using different configuration file to handle the actual working environment seems like a better solution, so I strongly recommend this method I use.
Use this method:
app.run(debug=True)
It will auto-reload the flask app when a code change happens.
Sample code:
from flask import Flask
app = Flask(__name__)
#app.route("/")
def index():
return "Hello World"
if __name__ == '__main__':
app.run(debug=True)
Well, if you want save time not reloading the webpage everytime when changes happen, then you can try the keyboard shortcut Ctrl + R to reload the page quickly.
From the terminal you can simply say
export FLASK_APP=app_name.py
export FLASK_ENV=development
flask run
or in your file
if __name__ == "__main__":
app.run(debug=True)
Enable the reloader in flask 2.2:
flask run --reload
Flask applications can optionally be executed in debug mode. In this mode, two very convenient modules of the development server called the reloader and the debugger are enabled by default.
When the reloader is enabled, Flask watches all the source code files of your project and automatically restarts the server when any of the files are modified.
By default, debug mode is disabled. To enable it, set a FLASK_DEBUG=1 environment variable before invoking flask run:
(venv) $ export FLASK_APP=hello.py for Windows use > set FLASK_APP=hello.py
(venv) $ export FLASK_DEBUG=1 for Windows use > set FLASK_DEBUG=1
(venv) $ flask run
* Serving Flask app "hello"
* Forcing debug mode on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 273-181-528
Having a server running with the reloader enabled is extremely useful during development, because every time you modify and save a source file, the server automatically restarts and picks up the change.
To achieve this in PyCharm set 'Environment Variables' section to:
PYTHONUNBUFFERED=1;
FLASK_DEBUG=1
For Flask 'run / debug configurations'.
To help with fast automatic change in browser:
pip install livereload
from livereload import Server
if __name__ == '__main__':
server = Server(app.wsgi_app)
server.serve()
Next, Start your server again:
eg. your .py file is app.py
python app.py
I believe a better solution is to set the app configuration. For me, I built the tool and then pushed it to a development server where I had to set up a WSGI pipeline to manage the flask web app. I had some data being updated to a template and I wanted it to refresh every X minutes (WSGI deployment for the Flask site through APACHE2 on UBUNTU 18). In your app.py or whatever your main app is, add app.config.update dictionary below and mark TEMPLATES_AUTO_RELOAD=True, you will find that any templates that are automatically updated on the server will be reflected in the browser. There is some great documentation on the Flask site for configuration handling found here.
app = Flask(__name__)
app.config.update(
TEMPLATES_AUTO_RELOAD=True
)
I have a Python application that is set up using the new New Relic configuration variables in the dotcloud.yml file, which works fine.
However I want to run a sandbox instance as a test/staging environment, so I want to be able to set the environment of the newrelic agent so that it uses the different configuration sections of the ini configuration. My dotcloud.yml is set up as follows:
www:
type: python
config:
python_version: 'v2.7'
enable_newrelic: True
environment:
NEW_RELIC_LICENSE_KEY: *****************************************
NEW_RELIC_APP_NAME: Application Name
NEW_RELIC_LOG: /var/log/supervisor/newrelic.log
NEW_RELIC_LOG_LEVEL: info
NEW_RELIC_CONFIG_FILE: /home/dotcloud/current/newrelic.ini
I have custom environment variables so that the sanbox is set as "test" and the live application is set to "production"
I am then calling the following in my uswsgi.py
NEWRELIC_CONFIG = os.environ.get('NEW_RELIC_CONFIG_FILE')
ENVIRONMENT = os.environ.get('MY_ENVIRONMENT', 'test')
newrelic.agent.initialize(NEWRELIC_CONFIG, ENVIRONMENT)
However the dotcloud instance is already enabling newrelic because I get this in the uwsgi.log file:
Sun Nov 18 18:50:12 2012 - unable to load app 0 (mountpoint='') (callable not found or import error)
Traceback (most recent call last):
File "/home/dotcloud/current/wsgi.py", line 15, in <module>
newrelic.agent.initialize(NEWRELIC_CONFIG, ENVIRONMENT)
File "/opt/ve/2.7/local/lib/python2.7/site-packages/newrelic-1.8.0.13/newrelic/config.py", line 1414, in initialize
log_file, log_level)
File "/opt/ve/2.7/local/lib/python2.7/site-packages/newrelic-1.8.0.13/newrelic/config.py", line 340, in _load_configuration
'environment "%s".' % (_config_file, _environment))
newrelic.api.exceptions.ConfigurationError: Configuration has already been done against differing configuration file or environment. Prior configuration file used was "/home/dotcloud/current/newrelic.ini" and environment "None".
So it would seem that the newrelic agent is being initialised before uwsgi.py is called.
So my question is:
Is there a way to initialise the newrelic environment?
The easiest way to do this, without changing any code would be to do the following.
Create a new sandbox app on dotCloud (see http://docs.dotcloud.com/0.9/guides/flavors/ for more information about creating apps in sandbox mode)
$ dotcloud create -f sandbox <app_name>
Deploy your code to the new sandbox app.
$ dotcloud push
Now you should have the same code running in both your live and sandbox apps. But because you want to change some of the ENV variables for the sandbox app, you need to do one more step.
According to this page http://docs.dotcloud.com/0.9/guides/environment/#adding-environment-variables there are 2 different ways of adding ENV variables.
Using the dotcloud.yml's environment section.
Using the dotcloud env cli command
Whereas dotcloud.yml allows you to define different environment variables for each service, dotcloud env set environment variables for the whole application. Moreover, environment variables set with dotcloud env supersede environment variables defined in dotcloud.yml.
That means that if we want to have different values for our sandbox app, we just need to run a dotcloud env command to set those variables on the sandbox app, which will override the ones in your dotcloud.yml
If we just want to change on variable we would run this command.
$ dotcloud env set NEW_RELIC_APP_NAME='Test Application Name'
If we want to update more then one at a time we would do the following.
$ dotcloud env set \
'NEW_RELIC_APP_NAME="Test Application Name"' \
'NEW_RELIC_LOG_LEVEL=debug'
To make sure that you have your env varibles set correctly you can run the following command.
$ dotcloud env list
Notes
The commands above, are using the new dotCloud 0.9.x CLI, if you are using the older one, you will need to either upgrade to the new one, or refer to the documentation for the old CLI http://docs.dotcloud.com/0.4/guides/environment/
When you set your environment variables it will restart your application so that it can install the variables, so to limit your downtime, set all of them in one command.
Unless they are doing something odd, you should be able to override the app_name supplied by the agent configuration file by doing:
import newrelic.agent
newrelic.agent.global_settings().app_name = 'Test Application Name'
Don't call newrelic.agent.initialize() a second time.
This will only work if app_name is listing a single application to report data to.