Ramverk

Ramverk is a Werkzeug framework built around cooperative mixins. At the base is a WSGI application class implementing the “least common denominator”. Framework components such as data persistence and markup templating are all composable mixins building on the application base that can be combined and cherry-picked to create a custom-tailored application. For the most part Ramverk is a collection of small pieces of glue for third-party technologies and Ramverk itself tries to do and be as little as possible.

Though the source code is published on GitHub, there are currently no plans to release Ramverk officially. Ramverk is being built as a custom Werkzeug configuration for me personally and this document serves as documentation for contributors to Ramverk applications and as a design document under construction, for discussing choices with other developers and as a memory aid for myself.

I welcome you to study my decisions and maybe learn from them, or conversely teach me why I should do something differently. I advice against using Ramverk for yourself however, unless you create your own fork. One upside to not comitting to a release is that I can change anything at any time and not bother myself with backwards-compatibility. In the end if something useful and stable emerges and there is interest, I’ll reconsider releasing, but really, there’s an abundance of great frameworks in existence and if you want something more custom I recommend you do like me and build your own glue around Werkzeug. It’s easier than you might think and a fun, rewarding activity that gives you insight into what makes other frameworks tick and why they do what they do.

Sample Application: Hello World with Writable Greeting

Our sample application is writable which means we’ll need persistence. To that end we create an object that we will use as the root of our persistent object tree. We use the Persistent base class which is simply an object() that tracks modifications so it is only saved when needed.

from persistent import Persistent

class Root(Persistent):

    greeting = 'Hello'

For convenience we write a property for accessing the root object, creating one if needed. This is achieved by extending the “environment” - a thread-safe object created for every request.

from werkzeug.utils import cached_property
from ramverk import fullstack

class Environment(fullstack.Environment):

    @cached_property
    def db(self):
        return self.persistent.setdefault('greeter', Root())

To expose the object over HTTP we route some endpoints that decide what to do with requests matching certain paths or methods. The endpoints receive attributes from the environment as keyword arguments, but only the attributes they ask for:

from ramverk.routing import get, post

@get('/')
def greet_visitor(render, db):
    return render('index.html', greeting=db.greeting)

@post('/')
def set_greeting(db, request, redirect):
    db.greeting = request.form['greeting']
    return redirect(':greet_visitor')

Fairly straight-forward. The redirect() function takes the name of an endpoint. Endpoints are named by the fully qualified dotted name of the endpoint function or class but functions like redirect also accept relative endpoint names that are resolved in the context of the request. In this case, the request endpoint is greeter:set_greeting (assuming we put the application in greeter.py) which means that :greet_visitor is resolved to greeter:greet_visitor.

But wait a minute: what sort of sorcery is this that these functions become exposed on the web server just by being decorated as endpoints? Actually, there is no magic - these decorators are “declarative” more than they are “imperative” - to use these endpoints we must first register them with an application. We can automate this procedure by scanning modules and packages, looking for decorated objects defined in the top-level namespace. Our application also needs to be told to use our custom environment class so the endpoints can use our db property:

class Greeter(fullstack.Application):

    environment = Environment

    def configure(self):
        self.scan()

The default behavior of scan() is to look in the module the application is defined in, so in this simple case we don’t need to pass it any arguments.

One thing left to do before we can rock this application! Can you guess what? We need to write the index.html template that we’re rendering in our greet_visitor() endpoint. We’ll use Genshi for its raw power and parse templates with Compact XML because no one likes to write XML by hand.

<html
    <body
        ?indent restart

<h1
    "$greeting, World!

<form
    @action=${path(':set_greeting')}
    @method=POST

    <input
        @name=greeting
        @placeholder=Enter a greeting
        @type=text

That wasn’t so hard was it? Compact XML is a dialect of XML that, like Python and YAML, bases the syntactical structure on the indentation of each line meaning we don’t need to write closing tags and don’t need to quote attributes even if they contain spaces. The “indent restart” just means to continue at the first column while keeping the nesting level, so we can nest deeply while keeping our sanity intact. The path() function takes an endpoint name just like the redirect() function we used earlier, and returns an absolute path on the web server that routes to the specified endpoint. This way we can change the routes or mount the application at a subdirectory on the webserver without changing our template, but in this simple scenario the path function will simply return /.

To run the application in development mode we can use Paver. Ramverk includes a few helpful tasks that we can import in our pavement.py and configure to use the Greeter application:

from paver.easy import options
from ramverk.paver import *

options.app = 'greeter:Greeter'
$ paver serve
---> ramverk.paver.serve
    INFO: werkzeug:  * Running on http://localhost:8008/
    INFO: werkzeug:  * Restarting with reloader: stat() polling
---> ramverk.paver.serve

As a bonus we also got a paver shell task that imports everything in our application module to a bpython console and creates an instance of our application as app bound to a fake request which means we get access to an environment and our persistent objects:

>>> app
<greeter.Greeter object at ...>
>>> app.local
<greeter.Environment object at ...>
>>> app.local.db
   DEBUG: Greeter: connecting ZODB
<greeter.Root object at ...>
>>> app.local.db.greeting
'Hello'  # or whatever we set it to in the web interface

Framework Design

In my experience with Flask I’ve noticed a few things:

Sooner or later you will want to use the application factory pattern.

Flask applications are singletons, built imperatively. This quickly becomes an issue when you want to build the application conditionally based on some arguments. The problem is easily solved by wrapping the creation of the application in a function but what we end up with is effectively a sort of custom object model to get the effects of class instances. Ramverk applications use the language construct that exists to solve this very problem: class-based OOP. Classes also know their name and containing module so there’s no need for passing __name__ to anything.

At some point you’ll need to subclass the Flask application class.

Because Ramverk uses the class construct, we’re prepared for this up front and don’t need to refactor the application later. It isn’t difficult to do this refactoring with Flask but the premise of it seems to scare people: they think of the Flask class as some opaque thing you shouldn’t mess with.

There’s always a use case for hooking parts of the framework.

Flask solves this by emitting blinker signals and providing decorators with which to register callback functions for different situations, such as “before request” or “handle this http error”. The former case covers only cases where a signal has specifically been emitted and the latter case requires Flask to know how to deal with multiple callbacks and how they interact with each other. Both of these cases are really, again, reimplementations of OOP constructs: signals correspond to method overloading and callback handling to the method resolution order. Ramverk is designed for cooperative multiple inheritance; as an effect of this you can, and in fact, are expected to hook any part of the framework and you are in control of how your hooks interact. In Ramverk, “everything is a mixin” and “everything is a cooperative method”. Flask is actually well designed for this kind of usage as well, but it isn’t the primary way you use the framework.

This is an immensely powerful approach but demands a certain level of expertise from the programmer. I’m writing Ramverk for my own personal use and expect that any other potential (however unlikely) users are capable programmers who aren’t afraid to dig into the guts of the source code.

It has been said that extending through subclassing a framework’s classes is a bad idea but I think this is primarily relevant when the classes are not designed for it from the start. It could also be said that cooperative overrides suffer from issues similar to those of Rails’ alias_method_chain but again I would say the issue there is (or was) the lack of a clearly defined public API and the hooking of private internals.

To paraphrase an old saying,

He who does not understand OOP is bound to reinvent it.

I’m sure Armin understands OOP very well, much better than me even, and this of course is meant to be tongue-in-cheek. The point is that if you don’t use classes where they make sense, you’ll end up implementing similar behavior yourself. It seems sensible to me to instead use the constructs the language provides for it.

Coupling routing with views eventually leads to pain.

Another thing that I’ve noticed is that at least before 0.7, routing in Flask is substandard to Werkzeug. This is in part because routes are directly associated with views so without going around it we can only use “rules” and not “rule factories”; in part because views are expected to read all values that matched in the rule. The result of this is that it’s inconvenient to add a “submount” for say a locale code.

A solution to this problem is to keep Werkzeug’s separation of rules/endpoints and “view” functions, and not require of view functions that they read all the rule variables. This lets us use rule factories like Submount easily.


Frameworks also face the problem of making view functions friendly to unit testing. Flask requires a fake request context be pushed to a stack and uses blinker signals for rendered templates. Some think this makes the functions a little too complicated and Pyramid instead passes the request to the views and lets views return a mapping to be used as the context for a “renderer”, i.e. a template.

My approach in Ramverk is to let views be explicit about what they need from an application and pass application attributes based on the signature of the view function. Attributes include the request object and a render method for templating. In unit tests, views can instead be passed fake request objects and a dumbed-down render function that, say, just return its arguments as-is. Given the sample application above we might do:

from werkzeug.wrappers import Request
from werkzeug.test     import create_environ

def render(template_name, **context):
    return template_name, context

def test():
    request = Request(create_environ())
    template, context = index(request=request,
                              render=render,
                              db=app.db,
                              redirect=app.redirect)

    assert context['greeting'] == 'Hello'

As you can see it would similarly be trivial to fake the db or for example hook redirections.

Full Stack Configuration

Components

Application
Implements the application logic and configuration. Usually one instance per process.
Environment
Each request creates an Environment instance wrapping the application and the WSGI environment. These objects are stacked on a context-local stack so that they can be retreived safely from threads and greenlets. This object is the place for application request logic.
Request
This object wraps only the WSGI environment and expose a more high-level interface to it. It is similar in many ways to the Environment object but usually not application-bound and usually not implementing any logic, only representing the incoming HTTP request as data. The request object lives on the environment object.
Response
High-level interface for composing the application response to an HTTP request. Not usually created automatically by the framework. Instances are callable as WSGI applications which is the only interface the framework expects, meaning any WSGI application can be used as a response.
Template Context
Namespace that templates render in, in addition to the keyword arguments passed to the renderer. Instances are passed the environment object.
class ramverk.fullstack.Application(**settings)[source]

Bases: ramverk.logbook.LogbookLoggerMixin, ramverk.zodb.ZODBStorageMixin, ramverk.genshi.GenshiMixin, ramverk.rendering.JSONMixin, ramverk.scss.SCSSMixin, ramverk.routing.URLMapMixin, ramverk.venusian.VenusianMixin, ramverk.wsgi.SharedDataMixin, ramverk.application.BaseApplication

Full-stack application.

Inheritance diagram of Application

settings.secret_key
Default :SecretKey based on the name setting.
settings.storage
Default :File storage based on the name setting.
log_handler[source]

Use colors and alignment in the log during development, and log only warnings and above in production.

environment

Full-stack environment object.

Default :Environment
request

Full-stack request object.

Default :Request
response

Full-stack response object.

Default :Response
template_context

Full-stack template context.

Default :TemplateContext
class ramverk.fullstack.Environment(application, environ)

Bases: ramverk.logbook.LogbookHandlerMixin, ramverk.compiling.EnvironmentCompilerMixin, ramverk.session.SessionMixin, ramverk.rendering.RenderingEnvironmentMixin, ramverk.transaction.TransactionMixin, ramverk.routing.URLMapAdapterMixin, ramverk.zodb.ZODBConnectionMixin, ramverk.environment.BaseEnvironment

Full-stack environment object.

Inheritance diagram of Environment

class ramverk.fullstack.Request(environ, populate_request=True, shallow=False)

Bases: werkzeug.wrappers.AcceptMixin, werkzeug.wrappers.AuthorizationMixin, werkzeug.wrappers.CommonRequestDescriptorsMixin, werkzeug.wrappers.ETagRequestMixin, werkzeug.contrib.wrappers.JSONRequestMixin, werkzeug.wrappers.UserAgentMixin, werkzeug.wrappers.BaseRequest

Full-stack request object.

Inheritance diagram of Request

class ramverk.fullstack.Response(response=None, status=None, headers=None, mimetype=None, content_type=None, direct_passthrough=False)

Bases: werkzeug.wrappers.CommonResponseDescriptorsMixin, ramverk.wrappers.DeferredResponseInitMixin, werkzeug.wrappers.ETagResponseMixin, werkzeug.wrappers.ResponseStreamMixin, werkzeug.wrappers.WWWAuthenticateMixin, werkzeug.wrappers.BaseResponse

Full-stack response object.

Inheritance diagram of Response

default_mimetype

Fall back on text/html if no mimetype was set.

class ramverk.fullstack.TemplateContext(environment)

Bases: ramverk.routing.URLHelpersMixin, ramverk.rendering.BaseTemplateContext

Full-stack template context.

Inheritance diagram of TemplateContext

Minimal Configuration

class ramverk.application.BaseApplication(**settings)[source]

Bases: ramverk.utils.Configurable

Base for applications.

Parameters:settings – Keyword arguments can be passed to the constructor and are added to settings.
settings[source]

Environmental configuration in a Bunch.

settings.debug

Enable development niceties that shouldn’t be enabled in production. May be read by mixins and have no particular effect in itself.

Default :False
settings.name

Name of the application, may be used for log channels and database defaults and such.

Default :The name of the class.
module[source]

Dotted name of the module or package the application belongs to.

Used by mixins to locate templates and endpoints and such. Defaults to the module the application class was defined in, which is usually what you want.

log[source]

Log channel for this application.

environment

Environment class.

Default :BaseEnvironment
request[source]

Request class.

Default :BaseRequest
response

Response class.

Default :BaseResponse
stack

Stack of environments, by default a globally shared stack.

contextbound(environ)

Context manager stacking the WSGI environ wrapped in environment.

__call__(environ, start_response)[source]

WSGI interface to this application.

class ramverk.environment.BaseEnvironment(application, environ)[source]

Bases: ramverk.utils.Configurable

Environment base class.

application

Application that received the request of this environment.

environ

WSGI environment.

request[source]

A request wrapping this environ.

response

Alias of response.

Context-Local Environment Stack

A context-local object is a container that associates data with the identity of the thread or greenlet of the calling context. The implication of this is that global state can be imitated in a thread- and greenlet-safe manner. Even so, it is usually not a good idea to rely on context-local objects as it can complicate code and make it less explicit, but in certain cases like internationalization it can be impractical to avoid them.

ramverk.local.stack

A LocalStack that applications share by default.

ramverk.local.get_current(stack=None)

Fetch the current object local to the caller context, i.e. the tip of the stack and normally an instance of some BaseEnvironment subclass.

Raises an UnboundContextError if called in a context not bound to a request environment.

ramverk.local.current

Proxy to the current context-local. Operations like accessing attributes are forwarded to the current object and return proper, non-proxy results but be aware that if you pass something the proxy itself, then that’s what the receiver will get: a proxy, forwarding to whatever is the current object of their context and which might change at any time. To get the actual current object of your context, use get_current().

Forwarding operations raise UnboundContextError if there is no environment to forward to.

class ramverk.local.Proxy(local, name=None)

A LocalProxy that prepends the proxy type to the repr() to avoid confusion. Proxies do not fully behave like the object they proxy so it can be helpful to know if you’re dealing with one.

exception ramverk.local.UnboundContextError

Raised if get_current() or current is used when the stack is empty.

Dispatching Requests by URL

Mixins

class ramverk.routing.URLMapMixin[source]

Application mixin for dispatching to endpoints by matching the requested URL against a map of rules.

url_rule_class

The class used for URL rules.

Default :Rule
url_map[source]

An empty Map that you can add rules to or replace with your own map of rules.

update_endpoint_values(environment, endpoint, values)

This method is called when a URL for endpoint is being built using values to fill in the placeholder variables of the URL rule. By overriding this you can modify the values mapping in-place to set defaults so you don’t have to specify them manually every time. This is particularly useful in combination with is_endpoint_expecting() for example if you have a placeholder for a language code in the rule.

dispatch_to_endpoint(environment, endpoint, **kwargs)

Implements the logic for dispatching from an environment to an endpoint. Applications can override this to customize how endpoints are called.

Parameters:
  • environment (ramverk.environment.BaseEnvironment) – Environment object wrapping the request to dispatch from.
  • endpoint – A function, method or class that should produce a response for the request environment.

For functions and methods, the default implementation will inspect the call signature and map keyword arguments to attributes on the environment. For classes it creates an instance, passing along the environment, and then calls the instance.

To simplify unit testing kwargs should be included as overrides when dispatching to an endpoint with keyword arguments, i.e. the case with functions and methods in the default implementation.

class ramverk.routing.URLMapAdapterMixin

Environment mixin that binds the application url_map for the request in a MapAdapter and dispatches to the matching endpoint if there was one.

url_map_adapter

A MapAdapter for the url_map bound to the request environment.

url_rule

The url_rule_class instance that matched this request.

endpoint

The endpoint name for the matched url_rule.

segments

The converted values that matched the placeholders in the url_rule as a Bunch.

absolute_endpoint(endpoint)

Expand the relative endpoint description to a fully qualified endpoint name. Given an application in myapp and a current endpoint of myapp.frontend:index, :about resolves to myapp.frontend:about and .backend:index to myapp.backend:index.

url(endpoint=None, **values)

Build a URL for the route that matches endpoint (expanded with absolute_endpoint()) and values (updated with update_endpoint_values()). If the endpoint is None, the current URL is returned with the values used as overrides.

path(endpoint=None, **values)

Like url() but as an absolute path.

redirect(endpoint=None, **values)

Create a response that redirects to the route for endpoint with values.

class ramverk.routing.URLHelpersMixin

Template context mixin with helpers for URL building.

url

Alias of url().

path

Alias of path().

Decorators

class ramverk.venusian.VenusianMixin[source]

Application mixin that uses Venusian to provide support for building modular applications with simple decorators and plain Python modules.

The decorators attach callbacks but do not otherwise alter the decorated function/class in any way. Applications can later scan for these callbacks in modules and packages and call them with a reference to itself and any optional parameters, thereby allowing the callbacks to register the decorated function/class with the application in the manner they see fit.

scan(package=None, categories=('ramverk', ), **parameters)[source]

Scan a module or (recursively) a package and configure the application using the callbacks attached to top-level objects.

If package is a string it is treated as a dotted name that gets imported, and if it is None the application module is scanned. The parameters set attributes on the scanner that is passed to the callbacks and can be used to configure the behavior of decorators for individual scans. In addition the application attribute is set to the application running the scan.

ramverk.venusian.decorator(callback)

A decorator for turning a Venusian callback into a decorator that attaches the callback.

For reference consider the canonical example from the Venusian documentation:

def jsonify(wrapped):
    def callback(scanner, name, ob):
        def jsonified(request):
            result = wrapped(request)
            return json.dumps(result)
        scanner.registry.add(name, jsonified)
    venusian.attach(wrapped, callback)
    return wrapped

Using the decorator() decorator we can simplify it a little:

@decorator
def jsonify(scanner, name, ob):
    def jsonified(request):
        result = ob(request)
        return json.dumps(result)
    scanner.registry.add(name, jsonified)
ramverk.venusian.configurator(target)

Generic decorator for configuring the scanning application. The decorated function is called with the scan parameters listed in the signature (which can include the implicit application).

Example:

pkg/conf.py

@configurator
def configure(application, submount):
    application.scan('pkg.frontend', submount=submount)
    application.scan('pkg.pages', submount=submount + '/pages')

app.py

class App(Application):

    def configure(self):
        self.scan('pkg.conf', submount='/pkg')
ramverk.routing.router(target)[source]

Decorator for adding URL rules to an application by calling the router (passing the preferred url_rule_class) which should return an iterable of rules. Typically a router is a generator or a function that return a list.

These scan parameters are recognized:

Parameters:
  • submount (str) – The rules are wrapped in a Submount rule factory created with this string, effectively prepending the string to each rule.
  • subdomain (str) – Like submount but for the Subdomain factory.
  • rulefactory – Wrap the rules in an arbitrary rule factory. Should be a callable that accept the list of rules as a single argument, or a tuple that is used to create a partial().

The rules are also wrapped in an EndpointPrefix factory for the name of the module the router is located in plus a colon.

Here’s a non-trivial example:

@router
def urls(rule):
    yield rule('/show', endpoint='message')

def localized_message(response):
    return response('Howdy')

class App(Application):

    def configure(self):
        self.scan(submount='/<locale>',
                  rulefactory=(EndpointPrefix, 'localized_'))

The scan in this example adds a rule that looks something like this:

Submount('/<locale>', [
    EndpointPrefix(__name__ + ':', [
        EndpointPrefix('localized_', [
            Rule('/show', endpoint='message')
        ])
    ])
])

In more simple terms it is essentially this rule:

Rule('/<locale>/show', endpoint='name.of.module:localized_message')
ramverk.routing.route(string, defaults=None, subdomain=None, methods=None)

Decorator for adding a single URL rule using the arguments to the decorator and inferring the endpoint name from the decorated function/class.

The recognized scan parameters are the same as that of router() as is the use of the module name to prefix endpoints. Variants for the different HTTP methods are also available:

ramverk.routing.connect(string, defaults=None, subdomain=None)

Like @route(methods=['CONNECT']).

ramverk.routing.delete(string, defaults=None, subdomain=None)

Like @route(methods=['DELETE']).

ramverk.routing.get(string, defaults=None, subdomain=None)

Like @route(methods=['GET']).

ramverk.routing.head(string, defaults=None, subdomain=None)

Like @route(methods=['HEAD']).

ramverk.routing.options(string, defaults=None, subdomain=None)

Like @route(methods=['OPTIONS']).

ramverk.routing.patch(string, defaults=None, subdomain=None)

Like @route(methods=['PATCH']).

ramverk.routing.post(string, defaults=None, subdomain=None)

Like @route(methods=['POST']).

ramverk.routing.put(string, defaults=None, subdomain=None)

Like @route(methods=['PUT']).

ramverk.routing.trace(string, defaults=None, subdomain=None)

Like @route(methods=['TRACE']).

Example:

@get('/')
def index(response):
    return response('Howdy')

Endpoint Classes

class ramverk.routing.AbstractEndpoint(environment)

Bases: ramverk.utils.Configurable

Optional base for endpoint classes that must implement __call__().

classmethod __rule_options__()

The route() family of decorators look for this attribute and call it if available, using the returned dict as the base for the keyword arguments that get passed to the URL rule being created.

environment

The environment object for the current request.

__call__()

Abstract method that must be implemented to return a response.

class ramverk.routing.MethodDispatch(environment)

Bases: ramverk.routing.AbstractEndpoint

Base for endpoint classes that dispatches the request to the instance method whose name is the HTTP request method in lower case.

If no corresponding method exists a MethodNotAllowed is raised with a list of valid methods. If routed with the route() decorator the resulting rule will by default only match HTTP methods implemented in the class.

The methods are called with dispatch_to_endpoint() meaning they behave like endpoint functions by default.

connect()

Respond to a CONNECT request.

delete()

Respond to a DELETE request.

get()

Respond to a GET request.

head()

Respond to a HEAD request.

options()

Respond to an OPTIONS request.

patch()

Respond to a PATCH request.

post()

Respond to a POST request.

put()

Respond to a PUT request.

trace()

Respond to a TRACE request.

Example:

@route('/')
class Index(MethodDispatch):

    renderer = 'index.html'

    def configure(self):
        self.greeting = self.environment.db.greeting

    def get(self, render):
        return render(self.renderer, greeting=self.greeting)

Rendering Content

class ramverk.wrappers.DeferredResponseInitMixin

Add a method for reinitializing response instances.

using(response=None, status=None, headers=None, mimetype=None, content_type=None, direct_passthrough=None)

Convenience method that works like __init__() for responses that have already been created. Particularly useful as a complement to the rendering machinery, for example return render('json', error='authentication required').using(status=401).

class ramverk.rendering.RenderingMixinBase

Base class for renderer mixins.

renderers

Mapping of renderer names to rendering callables. A name is either a plain string like 'json' or starts with a dot to represent a file extension, e.g. '.html'.

class ramverk.rendering.RenderingEnvironmentMixin
render(renderer_name, **context)

Look up a renderer and call it with the name and context. The name is cut at the first dot such that 'index.html' gets the '.html' renderer.

class ramverk.rendering.JSONMixin

Bases: ramverk.rendering.RenderingMixinBase

Add a 'json' renderer to an application.

_JSONMixin__default(obj)

Called to return a serializable representation (that is, using types that map cleanly to JSON equivalents) of obj, or raise TypeError (the default).

Templating with Genshi

class ramverk.genshi.GenshiMixin[source]

Bases: ramverk.rendering.TemplatingMixinBase

Add Genshi templating to an application.

These renderers are configured by default:

Renderer Serializer Doctype Mimetype Dialect
'.html' HTML HTML 5 text/html CompactHTMLTemplate
'.xhtml' XML XHTML 1.1 application/xhtml+xml CompactTemplate
'.atom' XML   application/atom+xml CompactTemplate
'.svg' XML SVG image/svg+xml CompactTemplate
'.xml' XML   application/xml CompactTemplate
'.txt' Text   text/plain NewTextTemplate

Notes

  • None of the preconfigured renderers serialize lazily by default.
  • You probably don’t want to use the XHTML renderer.
  • See the documentation for XML templates for both HTMLTemplate and CompactTemplate.
template_loaders

Adds a package() loader for the application module/templates directory.

genshi_loader

The template_loaders.genshi loaders wrapped in a TemplateLoader.

configure_genshi_template(template)

Called when template is first loaded; override to do Babel and Flatland installation and such.

filter_genshi_stream(environment, template, stream)

Fallback for GenshiRenderer.filter(), returning the stream unaltered by default.

class ramverk.genshi.GenshiRenderer(app, serializer=None, doctype=None, mimetype=None, dialect=<class 'ramverk.genshi.CompactTemplate'>, lazy=False)

Genshi renderer with fixed configuration.

serializer

The method to use when serializing the stream.

input xml xhtml html text
<hr></hr> <hr/> <hr /> <hr> empty
<a>b</a> same same same b

See get_serializer() for more information.

doctype

Set a doctype for rendered documents.

Can be a string or a tuple, see DocType for more information.

mimetype

Set a mimetype on the returned response object.

dialect

Template class if not CompactTemplate.

lazy

Serialize lazily, can misbehave with databases.

filter(environment, template, stream)

Called to filter the stream for template, delegating to filter_genshi_stream() by default.

Example:

def configure(self):
    self.renderers['.svg'] = GenshiRenderer(self, 'xml', 'svg', 'image/svg+xml')
class ramverk.genshi.CompactTemplate(source, filepath=None, filename=None, loader=None, encoding=None, lookup='strict', allow_exec=True)

A MarkupTemplate parsing with Compact XML using preconfigured namespace prefixes.

Less talkative than XML while still as predictable and consistent, but less standard and familiar than either XML or HTML. Subclass to override the option attributes.

namespaces

Mapping of namespace prefixes to namespace URIs to be included in templates, by default including py and xi.

pretty_print

Whether the rendered markup should be pretty-printed with whitespace.

class ramverk.genshi.CompactHTMLTemplate(source, filepath=None, filename=None, loader=None, encoding=None, lookup='strict', allow_exec=True)

Like CompactTemplate with the extra namespace prefixes i18n (for Babel) and form (for Flatland) meant for use with the HTML serializer which will strip unused prefixes from the output.

class ramverk.genshi.HTMLTemplate(source, filepath=None, filename=None, loader=None, encoding=None, lookup='strict', allow_exec=True)

A MarkupTemplate parsing with HTMLParser.

This allows for less verbose HTML templates by allowing unclosed tags and unquoted attributes and not requiring all namespaces be declarated explicitly, but is less predictable and generic than the more strict XML dialect.

filter_html_stream(stream)

Apply filters to the HTML stream; this happens earlier than the usual markup stream which has to be well-formed XML. The default injects the namespace prefixes py, xi, i18n (for Babel) and form (for Flatland). The HTML serializer will later strip unused prefixes from the output.

class ramverk.rendering.TemplatingMixinBase

Bases: ramverk.rendering.RenderingMixinBase

Base class for templating mixins.

template_context

Template context class.

template_loaders

A Bunch of template engines mapping to lists of loaders.

class ramverk.rendering.BaseTemplateContext(environment)

Base class for template contexts.

environment

The context-bound environment.

application

Alias of application.

request

Alias of request.

Compiling Static Resources On-Demand

class ramverk.compiling.CompilerMixinBase[source]

Base class for compiler mixins.

A “compiler” is like a middleground between static files and renderers. Unlike renderers there is no “context”, everything needed to compile the source should be in the source. Unlike static files, these sources need preprocessing before their rendered state can be sent to clients. This is primarily a convenience for development where these sources may change at any time but for production deployments these files should usually be precompiled up front and served as static files.

Compilation sources are stored in package/compiled and are compiled on-demand for GET requests to /compiled/ at the application root. The file extension is mapped to a compiler in compilers which compiles a corresponding file (usually with a different file extension) into a response. A 'compiled' endpoint is set up so you can build URLs e.g. path('compiled', name='main.css')'/compiled/main.css' which in turn sends a compiled version of compiled/main.scss if the mixin below is used.

compilers[source]

Mapping of output file extensions to compilers.

class ramverk.compiling.EnvironmentCompilerMixin

Environment mixin dispatching to compilers.

Styling with SCSS

class ramverk.scss.SCSSMixin[source]

Bases: ramverk.compiling.CompilerMixinBase

Add an SCSS compiler to an application.

Compiles Sassy stylesheets in source.scss files into compiled.css responses.

Tracking the Session of a User

class ramverk.session.SessionMixin[source]

Environment mixin adding a signed session cookie.

settings.secret_key

A secret key to sign the session cookie with.

session[source]

A SecureJSONCookie signed with the configured secret_key.

class ramverk.session.SecureJSONCookie(data=None, secret_key=None, new=True)[source]

A SecureCookie serialized as JSON which is safer than pickle.

class ramverk.session.SecretKey(filename, bytes=256)[source]

Lazily read filename for a secret key, writing bytes random bytes to it if it doesn’t exist.

Persisting Objects with ZODB

class ramverk.zodb.ZODBStorageMixin

Application mixin adding a connection pool for a ZODB storage for use with ZODBConnectionMixin.

settings.storage

Must be set to a callable returning a ZODB storage object.

class ramverk.zodb.ZODBConnectionMixin

Bases: ramverk.transaction.TransactionalMixinBase

Environment mixin connecting the configured ZODB storage on-demand.

persistent

The root PersistentMapping of the storage.

class ramverk.transaction.TransactionMixin[source]

Environment mixin binding the request to a transaction.

Important

Should be mixed in before anything that relies on transactions, such as ZODBConnectionMixin.

class ramverk.transaction.TransactionalMixinBase

Base class for transactional environment mixins.

transaction_manager

The transaction manager to use for this request.

transaction

The current transaction.

Logging with Logbook

class ramverk.logbook.LogbookLoggerMixin

Application mixin dispatching log records with Logbook.

log

A Logbook logging channel for this application.

log_handler

The Logbook handler used by the LogbookHandlerMixin. The default is the Logbook default, which is an StderrHandler.

class ramverk.logbook.LogbookHandlerMixin

Environment mixin binding the request to the application’s log_handler.

Important

Should be mixed in at the top of the inheritance chain of the environment so that all log records during requests pass through log_handler.

Task Management with Paver

options.app

Set this to your application class or an application instance, optionally as a dotted name.

options.settings

Settings to use when creating application instances. Values are converted with literal_eval() where possible, as Paver doesn’t process options coming from the command-line in any way and simply stores them as strings. This conversion means you can override settings directly on the command-line with literal Python types such as booleans and numbers, for example:

$ paver settings.debug=False serve
options.settings.debug
Default :True

Example pavement.py:

from paver.easy    import options
from ramverk.paver import *

options.app = 'my.own:Application'
ramverk.paver.serve()[source]

Run a development server.

options.serve.hostname

Host to listen on.

Default :localhost
options.serve.port

Port number to listen on.

Default :8008
ramverk.paver.shell()[source]

Enter a [b]python shell set up for the app.

This will create an app bound to a fake request and add it to the shell locals as app. If installed, bpython is used, otherwise a normal Python console.

options.shell.namespace

Dotted name of a module to use as the namespace inside the shell.

Default :The module of the app.
options.shell.fake_request

Path to fake a request to before entering the shell.

Default :/
ramverk.paver.routes()

List the application’s URL rules.

$ paver routes
---> ramverk.paver.routes

  /static/<path:name>
    -> static

  /compiled/<path:name>
    -> compiled

  [HEAD|GET] /
    -> greeter:greet_visitor

  [POST] /
    -> greeter:set_greeting

WSGI Middlewares

class ramverk.wsgi.SharedDataMixin

Serve static files for an application.

__create__()

Configures a build-only endpoint called static if the application has a url_map.

shared_data

Mapping of public paths to files/directories or tuples of (module, directory). The default serves up the static directory under the application module on /static. See SharedDataMiddleware for more information.

ramverk.wsgi.mixin(middleware)

Mix in a simple middleware.

Example:

from werkzeug._internal import _easteregg

class App(mixin(_easteregg), BaseApplication):
    pass
ramverk.wsgi.middleware(mixin)

Decorate a class as a middleware mixin. A special pipeline method is passed the WSGI application to wrap, and the pipeline is transformed into a cooperative __call__() override.

Example:

from werkzeug._internal import _easteregg

@middleware
class EasterEggMixin(object):

    def pipeline(self, app):
        return _easteregg(app)

class App(EasterEggMixin, BaseApplication):
    pass

Common Utilities

class ramverk.utils.Configurable

Base for a common pattern for hooking instance creation cleanly.

This class exists primarily to avoid repeating documentation for the same pattern, and is mostly abstract in itself.

__create__()

Should be called by the “configurable” class after __init__() to allow cooperative mixins and other base classes to configure the instance without needing to mess with __init__() directly.

configure()

Called after __create__() to allow the instance to configure itself without bothering with super().

ramverk.utils.super(type=Omitted, instance=Omitted)

Variant of super() that mimics the behavior in Python 3 with no arguments.

class ramverk.utils.Bunch[source]

Bases: dict

Attribute-accessible dict.

class ramverk.utils.Alias(path, doc=None)

Property that delegates to a distant attribute.

Parameters:
  • path – Path to the delegated attribute from “self”, as a dotted string or a list of strings.
  • doc – ReST markup for a link to the delegated attribute.

Example:

class DelegatingObject(object):

    message = 'Howdy'

    yell = Alias('message.upper', ':meth:`~str.upper`')
ramverk.utils.has(**properties)

Class decorator sugar for adding simple cached properties. Keyword arguments name the properties and the values are factory callables for constructing new values.

This is useful for setting attribute defaults for mutable types or deferred values. Example:

@has(comments=list, timestamp=datetime.utcnow)
class Post(object):
    author = None

Note that the values aren’t initialized until asked for - in the example above the timestamp isn’t necessarily that of the post creation. A solution for that case is to inherit from EagerCachedProperties.

class ramverk.utils.EagerCachedProperties

Visit all cached properties during instance allocation, forcing them to fix their value.

class ramverk.utils.ReprAttributes

Add an informative repr() to an object, listing attributes and their values.

class ramverk.utils.InitFromArgs(*args, **kwargs)

Bases: ramverk.utils.Configurable

Add a convenience __init__() based on __args__ that accepts both positional and keyword arguments, setting unspecified args to none.

ramverk.utils.args(*names)

Class decorator sugar for setting __args__.

Example:

@args('question', 'answer')
class QA(InitFromArgs):
    pass
>>> vars(QA())
{'answer': None, 'question': None}
>>> vars(QA('What is the ultimate answer?'))
{'answer': None, 'question': 'What is the ultimate answer?'}
>>> vars(QA('What is the ultimate answer?', 42))
{'answer': 42, 'question': 'What is the ultimate answer?'}
>>> vars(QA(answer=42, question='What is the ultimate answer?'))
{'answer': 42, 'question': 'What is the ultimate answer?'}
>>> vars(QA(answer=42))
{'answer': 42, 'question': None}

Importing Lazily

With apipkg installed you can import lazily from Ramverk, both in terms of lazy-loading modules on-demand and in terms of human laziness because you don’t need to type out the full imports or for that matter remember them. Simply import the ramverk.any module, or from it:

from ramverk import any  # only apipkg loaded so far
any.Bunch                # ramverk.utils loaded on attribute access

# importing from different modules in one go
from ramverk.any import Application, route

# force everything to load
from ramverk.any import *

Glossary

Technologies

Ramverk
Swedish for framework and a nod to Werkzeug which in turn is German for utility.
Werkzeug
Werkzeug is a swiss army knife for WSGI, similar in scope to Paste and WebOb. Ramverk uses Werkzeug’s request/response wrappers and its routing module for URL dispatch, among other things.
Genshi
Genshi is primarily an engine for XML templating, useful for generating XML-like content for the web, such as HTML or Atom feeds. It is an optional mixin in Ramverk and included with the full-stack.
Compact XML
Compact XML is a language that compiles to XML, using indentation for nesting similar to Python.
Flatland
Flatland is a toolkit for processing and validating flat datastructures against schemata defined in declarative Python code and is useful for sanitizing user input in HTML forms among other things.
SCSS
SCSS or “Sassy CSS” is a superset of CSS that adds rule nesting, mixins, constants and other such things making stylesheets more maintainable.
ZODB
ZODB is a scalable pickle, adding transactions and support for multi-process setups.
Venusian
Venusian is a library for implementing decorators that are activated by scanning. This allows decorators to be reusable and decoupled from applications without the use of a global registry.
Babel
Babel is a toolkit for internationalizing Python applications and includes a system for extracting translation messages and a repository of locale data.
Logbook
Logbook is a modern logging system for Python built with the demands of complex web deployments in mind.
Paver
Paver is a tool for scripting tasks with Python and is useful for managing a project in development.

Terminology

cooperative mixin
Mixin that calls super() in overridden methods to cooperate with other mixins and base classes.
dotted name
String representation of the import path to an object, in the form package.module:member.attribute or any variation thereof. These can be dereferenced with import_string() and are used for endpoints in Ramverk.
endpoint
In Ramverk, a function or class that responds to a request matching a URL rule. In Werkzeug, an arbitrary object otherwise the same as an endpoint name.
endpoint name
Identifier associated with URL rules, in Ramverk usually a dotted name for the endpoint function or class.
mixin
Class meant to be mixed in with at least one base class in an inheritance chain, and not used alone or as a base itself.
relative endpoint
A partial endpoint name, expanded to its full form using the endpoint of the current request and the module name of the application.
router
Callable returning an iterable of URL rules; usually a generator function.