03: Application Configuration with .ini Files

Use Pyramid's pserve command with a .ini configuration file for simpler, better application running.

Background

Pyramid has a first-class concept of configuration distinct from code. This approach is optional, but its presence makes it distinct from other Python web frameworks. It taps into Python's setuptools library, which establishes conventions for installing and providing "entry points" for Python projects. Pyramid uses an entry point to let a Pyramid application know where to find the WSGI app.

Objectives

  • Modify our setup.py to have an entry point telling Pyramid the location of the WSGI app
  • Create an application driven by a .ini file
  • Startup the application with Pyramid's pserve command
  • Move code into the package's __init__.py

Steps

  1. First we copy the results of the previous step:

    $ cd ..; cp -r package ini; cd ini
    
  2. Our ini/setup.py needs a setuptools "entry point" in the setup() function:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    from setuptools import setup
    
    requires = [
        'pyramid',
    ]
    
    setup(name='tutorial',
          install_requires=requires,
          entry_points="""\
          [paste.app_factory]
          main = tutorial:main
          """,
    )
    
  3. We can now install our project, thus generating (or re-generating) an "egg" at ini/tutorial.egg-info:

    $ $VENV/bin/python setup.py develop
    
  4. Let's make a file ini/development.ini for our configuration:

    1
    2
    3
    4
    5
    6
    7
    [app:main]
    use = egg:tutorial
    
    [server:main]
    use = egg:pyramid#wsgiref
    host = 0.0.0.0
    port = 6543
    
  5. We can refactor our startup code from the previous step's app.py into ini/tutorial/__init__.py:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    from pyramid.config import Configurator
    from pyramid.response import Response
    
    
    def hello_world(request):
        return Response('<body><h1>Hello World!</h1></body>')
    
    
    def main(global_config, **settings):
        config = Configurator(settings=settings)
        config.add_route('hello', '/')
        config.add_view(hello_world, route_name='hello')
        return config.make_wsgi_app()
    
  6. Now that ini/tutorial/app.py isn't used, let's remove it:

    $ rm tutorial/app.py
    
  7. Run your Pyramid application with:

    $ $VENV/bin/pserve development.ini --reload
    
  8. Open http://localhost:6543/.

Analysis

Our development.ini file is read by pserve and serves to bootstrap our application. Processing then proceeds as described in the Pyramid chapter on application startup:

  • pserve looks for [app:main] and finds use = egg:tutorial
  • The projects's setup.py has defined an "entry point" (lines 9-12) for the project "main" entry point of tutorial:main
  • The tutorial package's __init__ has a main function
  • This function is invoked, with the values from certain .ini sections passed in

The .ini file is also used for two other functions:

  • Configuring the WSGI server. [server:main] wires up the choice of which WSGI server for your WSGI application. In this case, we are using wsgiref bundled in the Python library. It also wires up the port number: port = 6543 tells wsgiref to listen on port 6543.
  • Configuring Python logging. Pyramid uses Python standard logging, which needs a number of configuration values. The .ini serves this function. This provides the console log output that you see on startup and each request.

We moved our startup code from app.py to the package's tutorial/__init__.py. This isn't necessary, but it is a common style in Pyramid to take the WSGI app bootstrapping out of your module's code and put it in the package's __init__.py.

The pserve application runner has a number of command-line arguments and options. We are using --reload which tells pserve to watch the filesystem for changes to relevant code (Python files, the INI file, etc.) and, when something changes, restart the application. Very handy during development.

Extra Credit

  1. If you don't like configuration and/or .ini files, could you do this yourself in Python code?
  2. Can we have multiple .ini configuration files for a project? Why might you want to do that?
  3. The entry point in setup.py didn't mention __init__.py when it declared tutorial:main function. Why not?
  4. What is the purpose of **settings? What does the ** signify?