¿Cómo puedo usar una aplicación de fábrica en los servidores Flask / WSGI y por qué podría ser inseguro?

Una pregunta sobre callables de aplicaciones, servidores WSGI e importaciones circulares de Flask

Estoy (posiblemente) confundido. Quiero crear de forma segura las aplicaciones Flask / WSGI desde las fábricas de aplicaciones y aún así poder usarlas en los servidores WSGI fácilmente.

tl; dr

  1. ¿Puedo evitar de forma segura crear una aplicación en la importación de init (como se recomienda) y crearla más tarde (es decir, con un método de fábrica)?

  2. ¿Cómo hago que la aplicación funcione correctamente con un servidor WSGI? Especialmente cuando estoy pasando en la configuración y otras configuraciones no sacándolos de ENV

Por ejemplo::

def make_app(configdict, appname): app = Flask(appname) app.config.update(configdict) init_db(configdict) set_app_in_global_namespace(app) #importing now will allow from pkg import app from mypackage import views return app 

Me gustaría usar la “fábrica” ​​anterior, porque quiero fácilmente configurar la configuración para realizar pruebas, etc.

Entonces presumiblemente quiero crear un módulo wsgi.py que proporcione la aplicación a un servidor WSGI.

Así que eventualmente las cosas se ven un poco así

init .py ::

 app = None def make_app(configdict, appname): flaskapp = Flask(appname) flaskapp.config.update(configdict) init_db(configdict) global app app = flaskapp #importing now will allow from pkg import app from mypackage import views return flaskapp 

wsgi.py ::

 from mypackage import app app = make_app(configfromsomewhere, "myname") 

uWSGI ::

 uwsgi --module=mypackage.wsgi:app 

Pero aún así, wsgi.py NO es algo que pueda llamar como wsgi.py –settings = x –host = 10.0.0.1 Así que realmente no sé cómo pasar la configuración.

Lo pregunto porque, si bien esto parece … OK … también es un poco desordenado.

La vida era más fácil cuando todo estaba en la ENV.

Y no solo sino también:

Entonces, ¿qué es inseguro sobre el uso de una aplicación de fábrica?

El consejo dado here _ es:

 1. the Flask application object creation has to be in the __init__.py file. That way each module can import it safely and the __name__ variable will resolve to the correct package. 2. all the view functions (the ones with a route() decorator on top) have to be imported in the __init__.py file. Not the object itself, but the module it is in. Import the view module after the application object is created. 

re: 2., claramente el decorador de rutas espera ciertas habilidades de una aplicación instanciada y no puede funcionar sin ellas. Esta bien.

re: 1. OK, necesitamos el nombre correcto. Pero, ¿qué es inseguro? ¿Y por qué? ¿No es seguro importar y usar la aplicación si no está inicializada? Bueno, se romperá, pero eso no es inseguro. ¿Es el hilo mucho más preciado local? Posiblemente. Pero si estoy arrancando instancias de aplicaciones de módulos aleatorios, no puedo esperar problemas.

Implicaciones: no hacemos referencia al objeto de la aplicación desde otra cosa que no sean las vistas; esencialmente mantenemos nuestra modularización agradable y ajustada, y pasamos los dictados, los objetos de error o incluso los WebObs.

http://flask.pocoo.org/docs/patterns/appdispatch http://flask.pocoo.org/docs/deploying/#deployment http://flask.pocoo.org/docs/patterns/packages/#larger- aplicaciones http://flask.pocoo.org/docs/becomingbig

De acuerdo con la documentación de Flask , una fábrica de aplicaciones es buena porque:

  1. Pruebas. Puede tener instancias de la aplicación con diferentes configuraciones para probar cada caso.

  2. Múltiples instancias. Imagina que quieres ejecutar diferentes versiones de la misma aplicación. Por supuesto, puede tener varias instancias con diferentes configuraciones configuradas en su servidor web, pero si usa fábricas, puede tener varias instancias de la misma aplicación ejecutándose en el mismo proceso de aplicación, lo que puede ser útil.

Pero, como se indica en la sección Otros trucos de prueba de la documentación, si está utilizando las fábricas de aplicaciones, las funciones before_request() y after_request() no se activarán automáticamente.

En los siguientes párrafos, mostraré cómo he estado usando el patrón de fábrica de aplicaciones con el servidor de aplicaciones uWSGI y nginx (solo los he usado, pero puedo intentar ayudarlo a configurarlo con otro servidor).

La Fábrica de Aplicaciones

Entonces, digamos que tiene su aplicación dentro de la carpeta su aplicación y dentro de ella está el archivo __init__.py :

 import os from flask import Flask def create_app(cfg=None): app = Flask(__name__) load_config(app, cfg) # import all route modules # and register blueprints return app def load_config(app, cfg): # Load a default configuration file app.config.from_pyfile('config/default.cfg') # If cfg is empty try to load config file from environment variable if cfg is None and 'YOURAPPLICATION_CFG' in os.environ: cfg = os.environ['YOURAPPLICATION_CFG'] if cfg is not None: app.config.from_pyfile(cfg) 

Ahora necesitas un archivo para crear una instancia de la aplicación:

 from yourapplication import create_app app = create_app() if __name__ == "__main__": app.run() 

En el código de arriba, asumo que hay una variable de entorno establecida con la ruta al archivo de configuración, pero podría dar la ruta de configuración a la fábrica, de esta manera:

 app = create_app('config/prod.cfg') 

Alternativamente, podrías tener algo como un diccionario con entornos y archivos de configuración correspondientes:

 CONFIG_FILES = {'development': 'config/development.cfg', 'test' : 'config/test.cfg', 'production' : 'config/production.cfg' } 

En este caso, la función load_config se vería así:

 def load_config(app, env): app.config.from_pyfile('config/default.cfg') var = "YOURAPPLICATION_ENV" if env is None and var in os.environ: env = os.environ[var] if env in CONFIG_FILES: app.config.from_pyfile(CONFIG_FILES[env]) 

Nginx y uWSGI

Aquí hay un ejemplo de un archivo de configuración para nginx:

 server { listen 80; server_name yourapplication.com; access_log /var/www/yourapplication/logs/access.log; error_log /var/www/yourapplication/logs/error.log; location / { try_files $uri @flask; } location @flask { include uwsgi_params; uwsgi_pass unix:/tmp/yourapplication.sock; # /env is the virtualenv directory uwsgi_param UWSGI_PYHOME /var/www/yourapplication/env; # the path where the module run is located uwsgi_param UWSGI_CHDIR /var/www/yourapplication; # the name of the module to be called uwsgi_param UWSGI_MODULE run; # the variable declared in the run module, an instance of Flask uwsgi_param UWSGI_CALLABLE app; } } 

Y el archivo de configuración de uWSGI se ve así:

 [uwsgi] plugins=python vhost=true socket=/tmp/yourapplication.sock env = YOURAPPLICATION_ENV=production logto = /var/www/yourapplication/logs/uwsgi.log 

Cómo usar before_request() y after_request()

El problema con esas funciones es que si los llama en otros módulos, esos módulos no se pueden importar antes de que la aplicación haya sido instanciada. Una vez más, la documentación tiene algo que decir al respecto:

El inconveniente es que no puede utilizar el objeto de la aplicación en los planos en el momento de la importación. Sin embargo, puede utilizarlo desde dentro de una solicitud. ¿Cómo se accede a la aplicación con la configuración? Usa current_app:

 from flask import current_app, Blueprint, render_template admin = Blueprint('admin', __name__, url_prefix='/admin') @admin.route('/') def index(): return render_template(current_app.config['INDEX_TEMPLATE']) 

O podría considerar crear una extensión , luego podría importar la clase sin ninguna instancia existente de Flask, ya que la extensión de la clase solo usaría la instancia de Flask después de crearse.