Configuration

All pybald applications must be configured early in the lifecycle of the application (generally before any controllers or models are defined). To configure an application, import the pybald module and run the configure function.

import pybald

# configure our pybald application
pybald.configure(debug=True)

There are several ways to configure a pybald application, but they all consist of building a set of key/value pairs to define how the application will run. In the example above, debug=True was passed as a key/value pair turning on debug.

Generally the config will be where you define things like:

  • whether the project is in debugging mode or not
  • logging directives
  • if using a database the database kind, host, port, username and password
  • paths to static files, templates, etc…
  • template helpers to load into all templates
  • other server configurations, like SMTP servers, search servers, etc…

Defaults

Pybald has a set of default configuration values that will always be used as the basis for a config. This allows starting a simple pybald application with very little configuration. The defaults are stored in the pybald.default module as a dictionary. This default dictionary will automatically be combined with any user supplied values to create the final configuration.

>>> from pybald.default import default_config
>>> from pprint import pprint
>>> pprint(default_config)
{'DEFAULT_PROTOCOL': 'http',
 'STATIC_SOURCES': None,
 'USE_CDN': False,
 'cache_path': 'tmp/viewscache',
 'database_engine_args': {},
 'database_engine_uri': '',
 'debug': True,
 'email_errors': False,
 'env_name': 'Default',
 'global_table_args': {},
 'host_name': 'localhost',
 'page_options': {},
 'path': '',
 'project_name': None,
 'schema_reflection': False,
 'smtp_config': {},
 'static_path': 'public',
 'template_default_filters': ['h', 'unicode'],
 'template_default_helpers': ['from pybald.core.helpers import img, link, humanize, HTMLLiteral as literal, url_for',
                              'from pybald.core import page'],
 'template_filesystem_check': True,
 'template_helpers': [],
 'template_path': 'app/views'}

You don’t need to know what all of the defaults mean right now, but it’s useful to be able to list them all and get a sense of what’s available.

There’s no limit to what values you can add to a config file. If you have project specific configuration variables you’d like to add, they will be included into the config.

Methods of Configuring

While there are several ways to configure a Pybald application, generally only one should be used at a time. Mixing config methods (keyword arguments and file for example) is not currently supported.

Keyword Argument

For the simplest projects, keyword arguments passed to the configure function can be used. This was the way debug=True was set on the configuration for the sample application.

import pybald

# configure our pybald application
pybald.configure(debug=True)

For larger projects, passing all the configuration options as keyword arguments becomes awkward so as a project grows, generally one of the other methods for configuration is used.

Config Dictionary

You can also configure a pybald application by building a dictionary of options and passing it to the configure function with the config_object keyword argument.

import pybald

# configure our pybald application
pybald.configure(config_object={'debug': True})

Config File (Module)

You can also configure a Pybald application by using a configuration file. Pybald configuration files are simple python modules. You can create a python module in the main project path that contains the variables you wish to use for your configuration. Traditionally this file is named project.py and lives in the root path of your project but it can be named anything. You can specify a python module and path to use as the config file by using the config_file keyword argument.

import pybald

# configure our pybald application
pybald.configure(config_file='project.py')

If no config options or keywords are passed to the configure function, pybald will attempt to load a config file named project.py from the project path. If no file with that name is present, then the default configuration will be used.

import pybald

# configure our pybald application, nothing specified
# so attempt to load a project.py file if present
pybald.configure()

This will attempt to load a project.py file if present.

Sample project.py

Project.py files generally look like a list of variable declarations. This doesn’t mean you can’t run python code or do dynamic things with the config options, in fact this is the main use case for using a python module for configuration rather than a static file format like an ini file. For example, one common use case for this pattern is to dynamically generate the database URI for a database connection using string interpolation. Another useful trick is to have a base project.py file that includes an environment.py file with environmental (production, test, development) specific values.

sample_config = True
env_name = "SampleTestProjectEnvironment"
template_path = "sample_project/templates"
cache_path = None
project_name = "Sample Project"
debug = True
BUNDLE_SOURCE_PATHS = ['tests/sample_project/front_end', 'tests/sample_project/sass']
database_engine_uri = 'sqlite:///:memory:'

The Pybald context

Regardless of the method used, once a Pybald application is configured a context is created. The configure function call creates the context and an immutable ConfigObject and attaches it to the context. The ConfigObject is the combination of the default configuration values and any user supplied values.

A Pybald context represents the configuration and any globally accessible state for the application. Once an application is configured, importing the context from pybald will give you access to this shared context. This allows you to have access to the configuration from multiple python modules without having to explicitly pass references to the context.

Importing context from pybald gives you access to the current application’s configuration and any shared resource (like caching or database connections).

>>> from pybald import context
>>> context.config
ConfigObject(project_name='sample.py', BUNDLE_ASSETS=False, global_table_args={}, USE_CDN=False, BUNDLE_OUTPUT_PATH='/min', email_errors=False, debug=True, cache_path='tmp/viewscache', template_default_helpers=['from pybald.core.helpers import img, link, humanize, HTMLLiteral as literal, url_for', 'from pybald.core import page'], path='/home/username/projects/sample', template_filesystem_check=True, database_engine_uri='', database_engine_args={}, template_path='app/views', BUNDLE_SOURCE_PATHS=['/front_end', '/sass'], BUNDLE_FILTER_OPTIONS=[], env_name='Default', page_options={}, host_name='localhost', smtp_config={}, template_helpers=[], BUNDLE_AUTO_BUILD=True, template_default_filters=['h', 'unicode'], schema_reflection=False, static_path='public')
>>> context.config.debug
True

Common config arguments

Runtime

The most common runtime configuration arguments are project_name, path, env_name and debug.

  • project_name an identifier for the current project, mostly informational and is used for storing per project comand line history
  • path the root of the current application. This root path is used when determining any relative paths for other configuration options.
  • env_name a meaningful ‘environment’ name useful for identifying the current running environment. Again mostly informational but is sometimes used for triggering behaviors (i.e. only instrument when in production)
  • debug whether the application runs in debug mode or not. Debug mode generally has chattier log output as well as changing the way some component behave. One example is the error handler, when in debug mode, will return a nicely formatted stack trace on exceptions, wheras when not in debug mode a user facing error message is returned.

Templates

There are a few configuration options that change the way the templating system behaves. These include:

  • cache_path - where compiled (Mako) templates will be stored
  • template_default_helpers - python functions to import into all templates to provide functions like link generation. These functions will be available in all templates and can be used directly (see the section on templating).
  • template_filesystem_check - when True, the template engine will check for changes to the underlying template files on every load, otherwise the cached version will always be used.
  • template_path - the path where project templates will be stored. Usually this is a relative path the the project’s root path.
  • template_default_filters - the default filters to run on all template output. By default, the HTML escape filter h which escapes all output to avoide XSS type attacks and unicode are used to make sure all output returns as unicode. You can dynamically add any filters to ouptut in templates, but these defaults will always be applied. Note: think hard before you decide to remove the html escape filter since that’s the root of many security problems!

Databases

Database configuration is done via SQLAlchemy which uses a URI following RFC-1738. The uri is in the config variable database_engine_uri. Generally pybald projects use some string interpolation to create these URLs from configuration dictionaries. This allows creating different environments with different config dictionaries but keep the same underlying config connection string.

Here is a sample project.py with a block to define a simple sqllite database as the database URI. Additionally it contains comments to show some other common URI patterns. These patterns are presented to show how one might create a mysql or postgres connection.

# sqlalchemy engine string examples:
# mysql -         "mysql://{user}:{password}@{host}/{database}"
# postgres - postgresql://{username}:{password}@{host}:{port}/{database}'
# sqllite -       "sqlite:///{filename}"
# sqllite mem -   "sqlite:///:memory:"

# local database connection settings
# default to a sqllite file database based on the project name
database_engine_uri_format = 'sqlite:///{filename}'
db_config = {'filename': os.path.join(path,
             '{project}.sqlite'.format(project=project_name))}

# create the db engine uri
database_engine_uri = database_engine_uri_format.format(**db_config)