Embedding an application in uWSGI

Starting from uWSGI 0.9.8.2, you can embed files in the server binary. Thesecan be any file type, including configuration files. You can embed directoriestoo, so by hooking the Python module loader you can transparently importpackages, too. In this example we’ll be embedding a full Flask project.

Step 1: creating the build profile

We’re assuming you have your uWSGI source at the ready.

In the buildconf directory, define your profile – let’s call it flask.ini:

  1. [uwsgi]
  2. inherit = base
  3. main_plugin = python
  4. bin_name = myapp
  5. embed_files = bootstrap.py,myapp.py

myapp.py is a simple flask app.

  1. from flask import Flask
  2. app = Flask(__name__)
  3. app.debug = True
  4.  
  5. @app.route('/')
  6. def index():
  7. return "Hello World"

bootstrap.py is included in the source distribution. It will extend the python import subsystem to use files embedded in uWSGI.

Now compile your app-inclusive server. Files will be embedded as symbols in theexecutable. Dots and dashes, etc. in filenames are thus transformed tounderscores.

  1. python uwsgiconfig.py --build flask

As bin_name was myapp, you can now run

  1. ./myapp --socket :3031 --import sym://bootstrap_py --module myapp:app

The sym:// pseudoprotocol enables uWSGI to access the binary’s embeddedsymbols and data, in this case importing bootstrap.py directly from the binaryimage.

Step 2: embedding the config file

We want our binary to automatically load our Flask app without having to pass a long command line.

Let’s create the configuration – flaskconfig.ini:

  1. [uwsgi]
  2. socket = 127.0.0.1:3031
  3. import = sym://bootstrap_py
  4. module = myapp:app

And add it to the build profile as a config file.

  1. [uwsgi]
  2. inherit = default
  3. bin_name = myapp
  4. embed_files = bootstrap.py,myapp.py
  5. embed_config = flaskconfig.ini

Then, after you rebuild the server

  1. python uwsgiconfig.py --build flask

you can now simply launch

  1. ./myapp
  2. # Remember that this new binary continues to be able to take parameters and config files:
  3. ./myapp --master --processes 4

Step 3: embedding flask itself

Now, we are ready to kick asses with uWSGI ninja awesomeness. We want a singlebinary embedding all of the Flask modules, including Werkzeug and Jinja2,Flask’s dependencies. We need to have these packages’ directories and thenspecify them in the build profile.

  1. [uwsgi]
  2. inherit = default
  3. bin_name = myapp
  4. embed_files = bootstrap.py,myapp.py,werkzeug=site-packages/werkzeug,jinja2=site-packages/jinja2,flask=site-packages/flask
  5. embed_config = flaskconfig.ini

Note

This time we have used the form “name=directory” to force symbols toa specific names to avoid ending up with a clusterfuck likesite_packages_flaskinitpy.

Rebuild and re-run. We’re adding –no-site when running to show you that theembedded modules are being loaded.

  1. python uwsgiconfig.py --build flask
  2. ./myapp --no-site --master --processes 4

Step 4: adding templates

Still not satisfied? WELL YOU SHOULDN’T BE.

  1. [uwsgi]
  2. inherit = default
  3. bin_name = myapp
  4. embed_files = bootstrap.py,myapp.py,werkzeug=site-packages/werkzeug,jinja2=site-packages/jinja2,flask=site-packages/flask,templates
  5. embed_config = flaskconfig.ini

Templates will be added to the binary… but we’ll need to instruct Flask onhow to load templates from the binary image by creating a custom Jinja2template loader.

  1. from flask import Flask, render_template
  2. from flask.templating import DispatchingJinjaLoader
  3.  
  4. class SymTemplateLoader(DispatchingJinjaLoader):
  5.  
  6. def symbolize(self, name):
  7. return name.replace('.','_').replace('/', '_').replace('-','_')
  8.  
  9. def get_source(self, environment, template):
  10. try:
  11. import uwsgi
  12. source = uwsgi.embedded_data("templates_%s" % self.symbolize(template))
  13. return source, None, lambda: True
  14. except:
  15. pass
  16. return super(SymTemplateLoader, self).get_source(environment, template)
  17.  
  18. app = Flask(__name__)
  19. app.debug = True
  20.  
  21. app.jinja_env.loader = SymTemplateLoader(app)
  22.  
  23. @app.route('/')
  24. def index():
  25. return render_template('hello.html')
  26.  
  27. @app.route('/foo')
  28. def foo():
  29. return render_template('bar/foo.html')

POW! BIFF! NINJA AWESOMENESS.