2009/1/14 Mark Ramm <[email protected]>: > My ultimate plan is to take this document and explode it out to discussion > of what happens at each WSGI middleware layer, and how to overide the setup > of each of these things, so that people have a clear picture of what's going > on. > > Hopefully this snipit helps. Also, if you have good ideas on how we can > best visually represent all of this, I'd be very interested.
Hi Mark, I have a draft of an article about TG2 and Pylons (attached) which, I think, could be helpful. It briefly explains the WSGI stack and request handling. It started as a collection of random notes that I wrote for myself when I was trying to come up with tests for #1999. I realized that I needed to setup some infrastructure (the stack, configuration etc.) for the tests, but I was lacking detailed understanding of how everything was supposed to work. In the article I used tables which you might find interesting, plus, included several useful references. -- Timur Izhbulatov -- www.timka.org --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "TurboGears Trunk" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/turbogears-trunk?hl=en -~----------~----~----~----~------~----~------~--~---Title: Application Configuration Modules
Comparison of TurboGears 2 and Pylons
TurboGears 2 is a full stack WSGI web framework built from best-of-breed components on top of Pylons. TurboGears adds a relatively thin integration layer which creates a comfortable environment for developers already familiar with TurboGears 1.x.
When I started learning TurboGears, I only had basic understanding of WSGI and was not familiar with Pylons. Thus I was a bit confused by some extra indirection introduced by the framework for no obvious reasons. Apparently, this stems from the fact that TurboGears is still Pylons, but with some remarkable differences, and that is why the understanding of how TurboGears works is impossible without getting familiar with Pylons.
Here I will try to summarize the key points of TurboGears' operation in WSGI sense with the emphasis on where it differs from Pylons.
NOTE: Make sure you read the Pylons' Internal Architecture chapter of The Pylons Book [pylons-arch]. This is an invaluable source of knowledge!
Overview
The application modules responsible for configuration in Pylons [pylons-conf] and TurboGears [tg-conf] project are presented in the following table:
| Module | Function | Pylons | TG | Comments |
|---|---|---|---|---|
config/middleware.py |
make_app |
Y | Y | WSGI middleware stack. Calls load_environment |
config/environment.py |
load_environment |
Y | Y | Application runtime environment (PylonsConfig etc.). Called from make_app |
config/routing.py |
make_map |
Y | N | Pylons URL mapping. Called from
load_environment. TurboGears uses object dispatch. |
config/app_cfg.py |
N/A | N | Y | TurboGears application configuration. Creates and configures an
AppConfig instance. The instance's setup_tg_wsgi_app
method is later called from config/middleware.py to create the
make_base_app application factory function which is called from
the make_app function. |
Pylons is built upon Paste WSGI meta-framework. Every Pylons (and thus
TurboGears) application defines a paste.app_factory entry point
[pylons-arch] in its setup.py that is used to create an application instance.
Usually, this is the make_app function from
config/middleware.py.
In turn, make_app calls the load_environment function
internally to do necessary initialization before building the application WSGI
stack.
In Pylons the configuration modules are generated from template when a new
project is created using the paster create command. The functions
in these modules do everything necessary to setup the application, and
developers are supposed to directly edit the corresponding function's body, if
customization is needed.
In TurboGears configuration modules are also generated from project template, but, unlike Pylons, the generated files don't directly define the necessary functions. Instead, they call special framework factory methods which, in turn, return dynamically created function objects. Thus application developers don't have to change their code every time they update TurboGears.
The config/app_cfg.py module is special. There is no any specific
function required to be defined there. The purpose of this module is to create
an instance of the tg.configuration.AppConfig class and set its
attributes as necessary.
That is, basic customization can be achieved by simply changing the AppConfig instance's attributes. More advanced cases may require subclassing AppConfig and overriding some of its methods.
WSGI Middleware Stack
The default stack in Pylons and TurboGears looks like this (outermost is the topmost):
| Component | Pylons | TG | In tg.configuration.AppConfig class |
Comments |
|---|---|---|---|---|
| Registry Manager | Y | Y | setup_tg_wsgi_app() | Always added. [paste-registry] |
| Status Code Redirect | Y | Y | add_error_middleware() |
[pylons-wsgi] [pylons-full-stack] |
| Error Handler | Y | Y | add_error_middleware() |
[pylons-wsgi] [pylons-full-stack] |
| DB Session Remover | N | Y | use_sqlalchemy, add_dbsession_remover_middleware(), |
TODO |
| Transaction Manager | N | Y | use_transaction_manager, add_tm_middleware() |
TODO |
| Auth | N | Y | auth_backend ('sqlalchemy' is supported OOTB),
add_auth_middleware(), setup_sa_auth_backend() |
TODO |
| ToscaWidgets | N | Y | use_toscawidgets, add_tosca_middleware() |
TODO |
| Cache | Y | Y | add_core_middleware() |
TODO |
| Session | Y | Y | add_core_middleware() |
TODO |
| Routes | Y | Y | add_core_middleware(), setup_routes() |
TODO |
| Application | Y | Y | N/A | Pylons or TurboGears WSGI application |
Pylons default WSGI stack is described in [pylons-wsgi]. Pylons middleware components are
described in [pylons-arch]. The make_app
function in config/middleware.py builds the stack by simply
wrapping more and more middleware components around the application object. The
application developer can add custom middleware components by directly inserting
the appropriate lines of code into the make_app function.
In TurboGears the tg.configuration.AppConfig class responsible for
building the WSGI stack has a number of add_*_middleware methods,
which can be overridden in subclasses to change how the corresponding middleware
component is added.
Application Runtime Environment
First, this should not be confused with WSGI environment. The
config/environment.py module basically initializes the necessary
parts of the application which are not middleware:
PylonsConfiginstance (pylons.configuration.config)- templating
- model
AppGlobalsinstancehelpersmodule
This is done in a way similar to that of middleware. The
load_environment function is responsible for initializing the
PylonsConfig object using the data from the deployment .ini file and defaults.
In TurboGears, again, the load_environment function is created in
run-time by the AppConfig.make_load_environment() method which is
called inside the config/environment.py.
Request Handling
Key points:
-
TurboGears application is a Pylons application
(
isinstance(tg.wsgiapp.TGApp, pylons.wsgiapp.PylonsApp) == True) - Pylons application is a WSGI application
- WSGI application is a callable object which accepts two arguments
pylons.wsgiapp.PylonsApp class is responsible for:
- registering context, request, and globals request specific objects in Registry Manager [pylons-arch]
- dispatching request to controller
__call__(environ, start_response)
self.setup_app_env(environ, start_response)
"""Setup and register all the Pylons objects with the registry
After creating all the global objects for use in the request,
``PylonsApp.register_globals`` is called to register them
in the environment.
"""
self.register_globals(environ)
controller = self.resolve(environ, start_response)
"""Uses dispatching information found in ``environ['wsgiorg.routing_args']``
to retrieve a controller name and return the controller instance from the
appropriate controller module.
"""
return self.find_controller(controller)
"""Locates a controller by attempting to import it then grab
the SomeController instance from the imported module.
"""
response = self.dispatch(controller, environ, start_response)
"""Dispatches to a controller, will instantiate the controller
if necessary.
"""
return controller(environ, start_response)
return response
The tg.wsgiapp.TGApp class is the same as PylonsApp
except that it overrides the find_controller method because TG
doesn't check the Pylons' __controller__ special variable [pylons-ctrl].
References
- [paste-registry] http://pythonpaste.org/modules/registry.html
- [pylons-arch] http://pylonsbook.com/alpha1/pylons_wsgi_architecture
- [pylons-conf] http://docs.pylonshq.com/configuration.html
- [pylons-ctrl] http://pylonshq.com/project/pylonshq/changeset/0a1419d9cdd8
- [pylons-full-stack] http://docs.pylonshq.com/configuration.html#what-is-full-stack
- [pylons-wsgi] http://docs.pylonshq.com/concepts.html#wsgi-middleware
- [tg-conf] http://turbogears.org/2.0/docs/main/Config.html
