Skip to content

Latest commit

 

History

History
172 lines (117 loc) · 4.78 KB

Embed.rst

File metadata and controls

172 lines (117 loc) · 4.78 KB

Embedding an application in uWSGI

Starting from uWSGI 0.9.8.2, you can embed files in the server binary. These can be any file type, including configuration files. You can embed directories too, so by hooking the Python module loader you can transparently import packages, 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:

[uwsgi]
inherit = base
main_plugin = python
bin_name = myapp
embed_files = bootstrap.py,myapp.py

myapp.py is a simple flask app.

from flask import Flask
app = Flask(__name__)
app.debug = True

@app.route('/')
def index():
    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 the executable. Dots and dashes, etc. in filenames are thus transformed to underscores.

python uwsgiconfig.py --build flask

As bin_name was myapp, you can now run

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

The sym:// pseudoprotocol enables uWSGI to access the binary's embedded symbols and data, in this case importing bootstrap.py directly from the binary image.

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:

[uwsgi]
socket = 127.0.0.1:3031
import = sym://bootstrap_py
module = myapp:app

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

[uwsgi]
inherit = base
main_plugin = python
bin_name = myapp
embed_files = bootstrap.py,myapp.py
embed_config = flaskconfig.ini

Then, after you rebuild the server

python uwsgiconfig.py --build flask

you can now simply launch

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

Step 3: embedding flask itself

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

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

Note

This time we have used the form "name=directory" to force symbols to a specific names to avoid ending up with a clusterfuck like site_packages_flask___init___py.

Rebuild and re-run. We're adding --no-site when running to show you that the embedded modules are being loaded.

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

Step 4: adding templates

Still not satisfied? WELL YOU SHOULDN'T BE.

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

Templates will be added to the binary... but we'll need to instruct Flask on how to load templates from the binary image by creating a custom Jinja2 template loader.

from flask import Flask, render_template
from flask.templating import DispatchingJinjaLoader

class SymTemplateLoader(DispatchingJinjaLoader):

    def symbolize(self, name):
        return name.replace('.','_').replace('/', '_').replace('-','_')

    def get_source(self, environment, template):
        try:
            import uwsgi
            source = uwsgi.embedded_data("templates_%s" % self.symbolize(template))
            return source, None, lambda: True
        except:
            pass
        return super(SymTemplateLoader, self).get_source(environment, template)

app = Flask(__name__)
app.debug = True

app.jinja_env.loader = SymTemplateLoader(app)

@app.route('/')
def index():
    return render_template('hello.html')

@app.route('/foo')
def foo():
    return render_template('bar/foo.html')

POW! BIFF! NINJA AWESOMENESS.