Hello,
it's looking just great! How do you think, can this behavior be extracted into some pattern for imperative configuration? I would like to have this in my toolchain in addition to zope.configuration. -- Andrey Popp On Thursday, December 9, 2010 at 11:19 AM, Chris McDonough wrote: > Hi all, > > I just merged a branch I've been working on for some time to the Pyramid > master branch on Github. The branch was named "twophase", and its goal > was to provide imperative configuration extensibility and two-phase > configuration much like that currently offered by ZCML. > > As a result, the ``pyramid.configuration`` module has been deprecated > and exists now only to support backwards compatibility. In its place a > new ``pyramid.config`` module exists with fundamentally the same API. > Paster template authors beware. > > What does this change get us? Imperative extensibility was the primary > goal (the ability to write "extensible" applications by including > configuration from other non-local sources). This was always a feature > offered to us by ZCML, but now folks don't have to use ZCML to get it. > > Here's an example of using imperative configuration to include > configuration from a non-local source. If the ``configure`` function > below lives in a module named ``myapp.myconfig``: > > # myapp.myconfig module > > def my_view(request): > from pyramid.response import Response > return Response('OK') > > def configure(config): > config.add_view(my_view) > > You might cause it be included within your Pyramid application like so: > > from pyramid.config import Configurator > > def main(global_config, **settings): > config = Configurator() > config.include('myapp.myconfig.configure') > return config.make_wsgi_app() > > When application extensibility of this kind is used, often configuration > conflicts are an issue. When you reuse some configuration from another > module, it's more likely that the configuration in that module will > conflict with configuration statements you execute "locally" or other > configuration statements executed as the result of including some other > configuration. For this reason, *configuration conflict detection* is > now a feature by default. > > Included configuration statements will be overridden by local > configuration statements if an included callable causes a configuration > conflict by registering something with the same configuration > parameters. So for instance, in the below, the local "add_view" call > will "win" even though both views are registered for the same thing: > > from pyramid.config import Configurator > > def someview(request): > from pyramid.response import Response > return Response('OK') > > def main(global_config, **settings): > config = Configurator() > config.add_view(someview) > config.include('myapp.myconfig.configure') > return config.make_wsgi_app() > > However, if two *included* configuration callables register the same > configuration parameters, a ConfigurationConflictError will now occur. > For example: > > # myapp.myconfig module > > def my_view(request): > from pyramid.response import Response > return Response('OK') > > def configure(config): > config.add_view(my_view) > > # myapp.myconfig2 module > > def my_view(request): > from pyramid.response import Response > return Response('OK') > > def configure(config): > config.add_view(my_view) > > # your application's __init__ > > from pyramid.config import Configurator > > def main(global_config, **settings): > config = Configurator() > config.include('myapp.myconfig.configure') > config.include('myapp.myconfig2.configure') > return config.make_wsgi_app() > > When ``make_wsgi_app`` is called, the configuration will be "committed", > and a conflict will be detected, because both myapp.myconfig.configure > and myapp.myconfig2.configure registered a view with the same > configuration parameters. To avoid a conflict, and make > "myapp.myconfig2.configure" "win", you can call "commit" between the > calls to "include": > > # your application's __init__ > > from pyramid.config import Configurator > > def main(global_config, **settings): > config = Configurator() > config.include('myapp.myconfig.configure') > config.commit() > config.include('myapp.myconfig2.configure') > return config.make_wsgi_app() > > Calling "commit" executes all pending configuration and clears the > configuration stack. It is safe to call commit() at any time. > "make_wsgi_app" calls commit on your behalf usually, but you can issue > commits at any time. > > A conflict error will now occur if you register two configuration > statements for the same set of arguments locally too: > > from pyramid.config import Configurator > > def someview(request): > from pyramid.response import Response > return Response('OK') > > def someotherview(request): > from pyramid.response import Response > return Response('OK') > > def main(global_config, **settings): > config = Configurator() > config.add_view(someview) > config.add_view(someotherview) > return config.make_wsgi_app() > > Because both calls to "add_view" above are registered for the same set > of circumstances, a ConfigurationConflictError will occur when > "make_wsgi_app" is run. To avoid this, use commit between those calls: > > from pyramid.config import Configurator > > def someview(request): > from pyramid.response import Response > return Response('OK') > > def someotherview(request): > from pyramid.response import Response > return Response('OK') > > def main(global_config, **settings): > config = Configurator() > config.add_view(someview) > config.commit() > config.add_view(someotherview) > return config.make_wsgi_app() > > By default, the pyramid.config.Configurator behaves like this, deferring > configuration actions until "commit" or "make_wsgi_app" is called. If > you want the older behavior back (no conflicts, every configuration > method executes immediately, later configuration statements override > earlier ones), you can use an "autocommitting" configurator: > > from pyramid.config import Configurator > > def someview(request): > from pyramid.response import Response > return Response('OK') > > def someotherview(request): > from pyramid.response import Response > return Response('OK') > > def main(global_config, **settings): > config = Configurator(autocommit=True) > config.add_view(someview) > config.add_view(someotherview) > return config.make_wsgi_app() > > The "someotherview" registered above will "win" in this circumstance. > > I've used "add_view" in all the examples above, but all methods of the > configurator make use of this feature (add_route, add_handler, > set_session_factory, etc). > > If you can imagine a future where folks factor reusable configuration > into these configuration callables, and those callables are included by > your application, you can imagine a future where applications can be > composed of other applications more cleanly than today. This isn't > exactly a "reusable application" story (reusable applications require > more "rails" than just this can provide), but it does allow > configuration to live cleanly outside of the main() function, and allows > for composability of multiple applications written by the same person or > organization fairly cleanly without ZCML. > > - C > > > -- > You received this message because you are subscribed to the Google Groups > "pylons-devel" group. > To post to this group, send email to pylons-de...@googlegroups.com. > To unsubscribe from this group, send email to > pylons-devel+unsubscr...@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/pylons-devel?hl=en. > > > > -- You received this message because you are subscribed to the Google Groups "pylons-devel" group. To post to this group, send email to pylons-de...@googlegroups.com. To unsubscribe from this group, send email to pylons-devel+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/pylons-devel?hl=en.