TextPress

open source content publishing system


TextPress Architecture

If you want to write a plugin you should better read that carefully. It will give you a better understanding of the TextPress internals. In the following document we will use some terms we should cover here first so that there is no misunderstanding. If we speak of the WSGI application we don't mean the TextPress class in the application module, but an instance of that. However, the term "TextPress instance" usually refers to the instance folder and the WSGI application for that particular folder.

Application Setup

this covers the textpress.application module

One thing you will notice is that TextPress doesn't manage WSGI applications on its own, although it knows perfectly well about all currently running instances. The reason for this is that the process of setting up a TextPress instance must never ever occur during a regular request. There is one exception which is the reloading procedure which we will cover later.

TextPress has one requirement to work correctly. And that is that there is only one active instance per thread. It's perfectly fine to have two of them in the same thread, but only one of them is allowed to execute code at the same time. Additionally the application setup process is not thread safe, so there is a global setup lock that is acquired during setup. This is one of the few reasons why you cannot create WSGI applications by directly instantiating TextPress but using the make_textpress() factory function.

Once an application is set up you can not use it until it's bound to the thread. This can happen by calling app.bind_to_thread(). That sort of binding automatically happens before each request, but additionally for the request object. To get the current application and request object for this thread you can call the get_application() and get_request() functions.

Here a small example, assuming the textpress instance data is stored in 'instance':

>>> from textpress import make_textpress
>>> app = make_textpress("instance")
>>> from textpress import api
>>> api.get_application()
>>> app.bind_to_thread()
>>> api.get_application()
<textpress.application.TextPress object at 0xb7d2294c>
>>> app is api.get_application()
True

During the setup an application is automatically bound to the current thread so that plugins can normally use database, configuration etc. in their setup phase. Only in that setup phase plugins can register new functionality safely. Although there are some ways to do some part of the registration after the setup too, this is not supported due to possible optimizations.

Plugins can use the app object to register new events, configuration variables and much more on the application. Have a look at already existing plugins, especially the documented "Eric The Fish" plugin.

Request Handling

The application object really is the WSGI application which inner middlewares applied. The normal dispatching method is _dispatch_request and during application setup it's bound to self.dispatch_request and probably wrapped by some middlewares. This inner middleware approach allows use to safely unbind application and request for the current thread at the end and add middlewares from inside plugins.

Event Passing

It's possible to send out events to the current application using he emit_event function in the application module. Plugins can add themselves as listeners to those events during their setup phase and get their functions called. It's important to know that all events will get exactly the same data, although the return value (EventResult) is per default lazily evaluated. That means it's not possible to chain handlers and send one the return value of the other one. This is a intended behavior because there is no guarantee in which order the listeners are called.