TODO: documentation

Miscelaneous notes abotu things to be documented.

Unordered list of topics to be documented

  • routing
    • common regular expressions examples
  • sessions
    • basic usage & configuration
    • using multiple sessions in the same request
    • using different backends
    • using flashes
    • updating session arguments (max_age etc)
    • purging db sessions
  • i18n (increment existing tutorial)
    • basic usage & configuration
    • loading locale/timezone automatically for each request
    • formatting date/time/datetime
    • formatting currency
    • using i18n in templates
  • jinja2 & mako
    • basic usage & configuration
    • setting global filters and variables (using config or factory)
  • auth
    • basic usage & configuration
    • setting up ‘own auth’
    • making user available automatically on each request
    • purging tokens
  • config
    • configuration conventions (“namespaced” configuration for webapp2_extras modules)
  • tricks
    • configuration in a separate file
    • routes in a separate file
    • reduce verbosity when defining routes (R = webapp2.Route)

Common errors

  • “TypeError: ‘unicode’ object is not callable”: one possible reason is that the RequestHandler returned a string. If the handler returns anything, it must be a webapp2.Response object. Or it must not return anything and write to the response instead using self.response.write().

Secret keys

Add a note about how to generate strong session secret keys:

$ openssl genrsa -out ${PWD}/private_rsa_key.pem 2048

Jinja2 factory

To create Jinja2 with custom filters and global variables:

from webapp2_extras import jinja2

def jinja2_factory(app):
    j = jinja2.Jinja2(app)
    j.environment.filters.update({
        'my_filter': my_filter,
    })
    j.environment.globals.update({
        'my_global': my_global,
    })
    return j

# When you need jinja, get it passing the factory.
j = jinja2.get_jinja2(factory=jinja2_factory)

Configuration notes

Notice that configuration is set primarily in the application. See:

By convention, modules that are configurable in webapp2 use the module name as key, to avoid name clashes. Their configuration is then set in a nested dict. So, e.g., i18n, jinja2 and sessions are configured like this:

config = {}
config['webapp2_extras.i18n'] = {
    'default_locale': ...,
}
config['webapp2_extras.jinja2'] = {
    'template_path': ...,
}
config['webapp2_extras.sessions'] = {
    'secret_key': ...,
}
app = webapp2.WSGIApplication(..., config=config)

You only need to set the configuration keys that differ from the default ones. For convenience, configurable modules have a ‘default_config’ variable just for the purpose of documenting the default values, e.g.:

Marketplace integration

<?xml version="1.0" encoding="UTF-8" ?>
<ApplicationManifest xmlns="http://schemas.google.com/ApplicationManifest/2009">
  <!-- Name and description pulled from message bundles -->
  <Name>Tipfy</Name>
  <Description>A simple application for testing the marketplace.</Description>

  <!-- Support info to show in the marketplace & control panel -->
  <Support>
    <!-- URL for application setup as an optional redirect during the install -->
    <Link rel="setup" href="https://app-id.appspot.com/a/${DOMAIN_NAME}/setup" />

    <!-- URL for application configuration, accessed from the app settings page in the control panel -->
    <Link rel="manage" href="https://app-id.appspot.com/a/${DOMAIN_NAME}/manage" />

    <!-- URL explaining how customers get support. -->
    <Link rel="support" href="https://app-id.appspot.com/a/${DOMAIN_NAME}/support" />

    <!-- URL that is displayed to admins during the deletion process, to specify policies such as data retention, how to claim accounts, etc. -->
    <Link rel="deletion-policy" href="https://app-id.appspot.com/a/${DOMAIN_NAME}/deletion-policy" />
  </Support>

  <!-- Show this link in Google's universal navigation for all users -->
  <Extension id="navLink" type="link">
    <Name>Tipfy</Name>
    <Url>https://app-id.appspot.com/a/${DOMAIN_NAME}/</Url>
    <!-- This app also uses the Calendar API -->
    <Scope ref="Users"/>
    <!--
    <Scope ref="Groups"/>
    <Scope ref="Nicknames"/>
    -->
  </Extension>

  <!-- Declare our OpenID realm so our app is white listed -->
  <Extension id="realm" type="openIdRealm">
    <Url>https://app-id.appspot.com</Url>
  </Extension>

  <!-- Special access to APIs -->
  <Scope id="Users">
    <Url>https://apps-apis.google.com/a/feeds/user/#readonly</Url>
    <Reason>Users can be selected to gain special permissions to access or modify content.</Reason>
  </Scope>
  <!--
    <Scope id="Groups">
    <Url>https://apps-apis.google.com/a/feeds/group/#readonly</Url>
    <Reason></Reason>
  </Scope>
  <Scope id="Nicknames">
    <Url>https://apps-apis.google.com/a/feeds/nickname/#readonly</Url>
    <Reason></Reason>
  </Scope>
  -->
</ApplicationManifest>