diff options
-rw-r--r-- | docs/news.txt | 5 | ||||
-rw-r--r-- | docs/paste-deploy.txt | 51 | ||||
-rw-r--r-- | paste/deploy/interfaces.py | 28 | ||||
-rw-r--r-- | paste/deploy/loadwsgi.py | 66 | ||||
-rw-r--r-- | tests/conftest.py | 4 | ||||
-rw-r--r-- | tests/fake_packages/FakeApp.egg/FakeApp.egg-info/entry_points.txt | 14 | ||||
-rw-r--r-- | tests/fake_packages/FakeApp.egg/setup.py | 8 | ||||
-rw-r--r-- | tests/sample_configs/test_config_included.ini | 2 | ||||
-rw-r--r-- | tests/sample_configs/test_filter.ini | 3 | ||||
-rw-r--r-- | tests/test_filter.py | 6 |
10 files changed, 118 insertions, 69 deletions
diff --git a/docs/news.txt b/docs/news.txt index d8eecd7..8ea663c 100644 --- a/docs/news.txt +++ b/docs/news.txt @@ -4,6 +4,11 @@ Paste Deployment News Version 0.2 (svn trunk) ----------------------- +* Added a ``filter-with`` setting to applications. + +* Removed the ``1`` from all the protocol names (e.g., + ``paste.app_factory1`` is not ``paste.app_factory``). + * Added ``filter-app:`` and ``pipeline:`` sections. `Docs <paste-deploy.html#filter-composition>`__. diff --git a/docs/paste-deploy.txt b/docs/paste-deploy.txt index 5e64dd0..e7852cb 100644 --- a/docs/paste-deploy.txt +++ b/docs/paste-deploy.txt @@ -132,10 +132,10 @@ The other way to define an application is to point exactly to some Python code:: [app:myapp] - paste.app_factory1 = myapp.modulename:app_factory + paste.app_factory = myapp.modulename:app_factory You must give an explicit *protocol* (in this case -``paste.app_factory1``), and the value is something to import. In +``paste.app_factory``), and the value is something to import. In this case the module ``myapp.modulename`` is loaded, and the ``app_factory`` object retrieved from it. @@ -227,9 +227,21 @@ of objects. Filter Composition ~~~~~~~~~~~~~~~~~~ -Two special section types exist to apply filters more easily to your -applications: ``[filter-app:...]`` and ``[pipeline:...]``. Both of these -sections define applications, and so can be used wherever an +There are several ways to apply filters to applications. It mostly +depends on how many filters, and in what order you want to apply them. + +The first way is to use the ``filter-with`` setting, like:: + + [app:main] + use = egg:MyEgg + filter-with = printdebug + + [filter:printdebug] + use = egg:Paste#printdebug + +Also, two special section types exist to apply filters to your +applications: ``[filter-app:...]`` and ``[pipeline:...]``. Both of +these sections define applications, and so can be used wherever an application is needed. ``filter-app`` defines a filter (just like you would in a @@ -244,6 +256,9 @@ ended by an application, like:: [pipeline:main] pipeline = filter1 egg:FilterEgg#filter2 filter3 app + [filter:filter1] + ... + ``egg:`` URIs ------------- @@ -286,7 +301,7 @@ argument to ``setup()`` like:: name='MyApp', ... entry_points={ - 'paste.app_factory1': [ + 'paste.app_factory': [ 'main=myapp.mymodule:app_factory', 'ob2=myapp.mymodule:ob_factory'], }, @@ -309,12 +324,12 @@ This lets you point to factories (that obey the specific protocols we mentioned). But that's not much use unless you can create factories for your applications. -There's a few protocols: ``paste.app_factory1``, -``paste.composit_factory1``, ``paste.filter_factory1``, and lastly -``paste.server_factory1``. Each of these expects a callable (like a +There's a few protocols: ``paste.app_factory``, +``paste.composit_factory``, ``paste.filter_factory``, and lastly +``paste.server_factory``. Each of these expects a callable (like a function, method, or class). -``paste.app_factory1`` +``paste.app_factory`` ~~~~~~~~~~~~~~~~~~~~~~ The application is the most common. You define one like:: @@ -325,7 +340,7 @@ The application is the most common. You define one like:: The ``global_config`` is a dictionary, and local configuration is passed as keyword arguments. The function returns a WSGI application. -``paste.composit_factory1`` +``paste.composit_factory`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Composits are just slightly more complex:: @@ -364,7 +379,7 @@ Then we use it like:: [app:myapp] use = egg:MyApp -``paste.filter_factory1`` +``paste.filter_factory`` ~~~~~~~~~~~~~~~~~~~~~~~~~ Filter factories are just like app factories (same signature), except @@ -394,10 +409,10 @@ variable is set, creating a really simple authentication filter:: '403 Forbidden', [('Content-type', 'text/html')]) return ['You are forbidden to view this resource'] -``paste.filter_app_factory1`` +``paste.filter_app_factory`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This is very similar to ``paste.filter_factory1``, except that it also +This is very similar to ``paste.filter_factory``, except that it also takes a ``wsgi_app`` argument, and returns a WSGI application. So if you changed the above example to:: @@ -409,7 +424,7 @@ Then ``AuthFilter`` would serve as a filter_app_factory (``req_usernames`` is a required local configuration key in this case). -``paste.server_factory1`` +``paste.server_factory`` ~~~~~~~~~~~~~~~~~~~~~~~~~ This takes the same signature as applications and filters, but returns @@ -429,10 +444,10 @@ An example might look like:: An implementation of ``Server`` is left to the user. -``paste.server_runner1`` +``paste.server_runner`` ~~~~~~~~~~~~~~~~~~~~~~~~ -Like ``paste.server_factory1``, except ``wsgi_app`` is passed as the +Like ``paste.server_factory``, except ``wsgi_app`` is passed as the first argument, and the server should run immediately. Outstanding Issues @@ -444,7 +459,7 @@ Outstanding Issues * Should there be a "default" protocol for each type of object? Since there's currently only one protocol, it seems like it makes sense (in the future there could be multiple). Except that - ``paste.app_factory1`` and ``paste.composit_factory1`` overlap + ``paste.app_factory`` and ``paste.composit_factory`` overlap considerably. * ConfigParser's INI parsing is kind of annoying. I'd like it both diff --git a/paste/deploy/interfaces.py b/paste/deploy/interfaces.py index 5f649cb..63e37b2 100644 --- a/paste/deploy/interfaces.py +++ b/paste/deploy/interfaces.py @@ -35,10 +35,10 @@ def loadserver(uri, name=None, relative_to=None, global_conf=None): ## Factories ############################################################ -class IPasteAppFactory1: +class IPasteAppFactory: """ - This is the spec for the ``paste.app_factory1`` + This is the spec for the ``paste.app_factory`` protocol/entry_point. """ @@ -51,28 +51,28 @@ class IPasteAppFactory1: capture these values). """ -class IPasteCompositFactory1: +class IPasteCompositFactory: """ - This is the spec for the ``paste.composit_factory1`` + This is the spec for the ``paste.composit_factory`` protocol/entry_point. - This also produces WSGI applications, like ``paste.app_factory1``, + This also produces WSGI applications, like ``paste.app_factory``, but is given more access to the context in which it is loaded. """ def __call__(loader, global_conf, **local_conf): """ - Like IPasteAppFactory1 this returns a WSGI application + Like IPasteAppFactory this returns a WSGI application (IWSGIApp). The ``loader`` value conforms to the ``ILoader`` interface, and can be used to load (contextually) more applications. """ -class IPasteFilterFactory1: +class IPasteFilterFactory: """ - This is the spec for the ``paste.filter_factory1`` + This is the spec for the ``paste.filter_factory`` protocol/entry_point. """ @@ -81,10 +81,10 @@ class IPasteFilterFactory1: Returns a IFilter object. """ -class IPasteFilterAppFactory1: +class IPasteFilterAppFactory: """ - This is the spec for the ``paste.filter_app_factory1`` + This is the spec for the ``paste.filter_app_factory`` protocol/entry_point. """ @@ -96,10 +96,10 @@ class IPasteFilterAppFactory1: objects that implement the IFilter interface. """ -class IPasteServerFactory1: +class IPasteServerFactory: """ - This is the spec for the ``paste.server_factory1`` + This is the spec for the ``paste.server_factory`` protocol/entry_point. """ @@ -108,10 +108,10 @@ class IPasteServerFactory1: Returns a IServer object. """ -class IPasteServerRunner1: +class IPasteServerRunner: """ - This is the spec for the ``paste.server_runner1`` + This is the spec for the ``paste.server_runner`` protocol/entry_point. """ diff --git a/paste/deploy/loadwsgi.py b/paste/deploy/loadwsgi.py index 9a970b9..9e892d7 100644 --- a/paste/deploy/loadwsgi.py +++ b/paste/deploy/loadwsgi.py @@ -57,14 +57,14 @@ class _ObjectType(object): APP = _ObjectType( 'application', - ['paste.app_factory1', 'paste.composit_factory1'], + ['paste.app_factory', 'paste.composit_factory'], [['app', 'application'], 'composit', 'pipeline', 'filter-app']) def APP_invoke(context): - if context.protocol == 'paste.composit_factory1': + if context.protocol == 'paste.composit_factory': return context.object(context.loader, context.global_conf, **context.local_conf) - elif context.protocol == 'paste.app_factory1': + elif context.protocol == 'paste.app_factory': return context.object(context.global_conf, **context.local_conf) else: assert 0, "Protocol %r unknown" % context.protocol @@ -73,13 +73,13 @@ APP.invoke = APP_invoke FILTER = _ObjectType( 'filter', - [['paste.filter_factory1', 'paste.filter_app_factory1']], + [['paste.filter_factory', 'paste.filter_app_factory']], ['filter']) def FILTER_invoke(context): - if context.protocol == 'paste.filter_factory1': + if context.protocol == 'paste.filter_factory': return context.object(context.global_conf, **context.local_conf) - elif context.protocol == 'paste.filter_app_factory1': + elif context.protocol == 'paste.filter_app_factory': def filter_wrapper(wsgi_app): # This should be an object, so it has a nicer __repr__ return context.object(wsgi_app, context.global_conf, @@ -92,13 +92,13 @@ FILTER.invoke = FILTER_invoke SERVER = _ObjectType( 'server', - [['paste.server_factory1', 'paste.server_runner1']], + [['paste.server_factory', 'paste.server_runner']], ['server']) def SERVER_invoke(context): - if context.protocol == 'paste.server_factory1': + if context.protocol == 'paste.server_factory': return context.object(context.global_conf, **context.local_conf) - elif context.protocol == 'paste.server_runner1': + elif context.protocol == 'paste.server_runner': def server_wrapper(wsgi_app): # This should be an object, so it has a nicer __repr__ return context.object(wsgi_app, context.global_conf, @@ -137,6 +137,16 @@ def FILTER_APP_invoke(context): FILTER_APP.invoke = FILTER_APP_invoke +FILTER_WITH = _ObjectType( + 'filtered_app', [], []) + +def FILTER_WITH_invoke(context): + filter = context.filter_context.create() + app = APP_invoke(context) + return filter(app) + +FILTER_WITH.invoke = FILTER_WITH_invoke + ############################################################ ## Loaders ############################################################ @@ -285,25 +295,37 @@ class ConfigLoader(_Loader): # @@: It's a global option (?), so skip it continue local_conf[option] = self.parser.get(section, option) + if object_type is APP and 'filter-with' in local_conf: + filter_with = local_conf.pop('filter-with') + else: + filter_with = None if section.startswith('filter-app:'): - return self._filter_app_context( + context = self._filter_app_context( object_type, section, name=name, global_conf=global_conf, local_conf=local_conf, global_additions=global_additions) - if section.startswith('pipeline:'): - return self._pipeline_app_context( + elif section.startswith('pipeline:'): + context = self._pipeline_app_context( object_type, section, name=name, global_conf=global_conf, local_conf=local_conf, global_additions=global_additions) - if 'use' in local_conf: - return self._context_from_use( - object_type, local_conf, global_conf, global_additions) + elif 'use' in local_conf: + context = self._context_from_use( + object_type, local_conf, global_conf, global_additions, + section) else: - return self._context_from_explicit( - object_type, local_conf, global_conf, global_additions) + context = self._context_from_explicit( + object_type, local_conf, global_conf, global_additions, + section) + if filter_with is not None: + filter_context = self.filter_context( + name=filter_with, global_conf=global_conf) + context.object_type = FILTER_WITH + context.filter_context = filter_context + return context def _context_from_use(self, object_type, local_conf, global_conf, - global_additions): + global_additions, section): use = local_conf.pop('use') context = self.get_context( object_type, name=use, global_conf=global_conf) @@ -314,7 +336,7 @@ class ConfigLoader(_Loader): return context def _context_from_explicit(self, object_type, local_conf, global_conf, - global_addition): + global_addition, section): possible = [] for protocol_options in object_type.egg_protocols: for protocol in protocol_options: @@ -349,10 +371,12 @@ class ConfigLoader(_Loader): APP, next_name, global_conf) if 'use' in local_conf: context.filter_context = self._context_from_use( - FILTER, local_conf, global_conf, global_additions) + FILTER, local_conf, global_conf, global_additions, + section) else: context.filter_context = self._context_from_explicit( - FILTER, local_conf, global_conf, global_additions) + FILTER, local_conf, global_conf, global_additions, + section) return context def _pipeline_app_context(self, object_type, section, name, diff --git a/tests/conftest.py b/tests/conftest.py index 0cc7d9b..d48c2d6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -14,7 +14,3 @@ import pkg_resources sys.path.insert(0, base) #pkg_resources.require('Paste-Deploy') -# This is where applications we test go; these applications -# are only used for testing, they aren't "real". -sys.path.append(os.path.join(here, 'fake_packages')) - diff --git a/tests/fake_packages/FakeApp.egg/FakeApp.egg-info/entry_points.txt b/tests/fake_packages/FakeApp.egg/FakeApp.egg-info/entry_points.txt index 8ecaf21..9bfc986 100644 --- a/tests/fake_packages/FakeApp.egg/FakeApp.egg-info/entry_points.txt +++ b/tests/fake_packages/FakeApp.egg/FakeApp.egg-info/entry_points.txt @@ -1,22 +1,22 @@ -[paste.app_factory1] +[paste.app_factory] basic_app=fakeapp.apps:make_basic_app other=fakeapp.apps:make_basic_app2 configed=fakeapp.configapps:SimpleApp.make_app -[paste.filter_factory1] +[paste.composit_factory] - caps=fakeapp.apps:make_cap_filter + remote_addr=fakeapp.apps:make_remote_addr -[paste.composit_factory1] +[paste.filter_app_factory] - remote_addr=fakeapp.apps:make_remote_addr + caps2=fakeapp.apps:CapFilter -[paste.filter_app_factory1] +[paste.filter_factory] - caps2=fakeapp.apps:CapFilter + caps=fakeapp.apps:make_cap_filter diff --git a/tests/fake_packages/FakeApp.egg/setup.py b/tests/fake_packages/FakeApp.egg/setup.py index febdf9f..854483e 100644 --- a/tests/fake_packages/FakeApp.egg/setup.py +++ b/tests/fake_packages/FakeApp.egg/setup.py @@ -5,18 +5,18 @@ setup( version="1.0", packages=find_packages(), entry_points={ - 'paste.app_factory1': """ + 'paste.app_factory': """ basic_app=fakeapp.apps:make_basic_app other=fakeapp.apps:make_basic_app2 configed=fakeapp.configapps:SimpleApp.make_app """, - 'paste.composit_factory1': """ + 'paste.composit_factory': """ remote_addr=fakeapp.apps:make_remote_addr """, - 'paste.filter_factory1': """ + 'paste.filter_factory': """ caps=fakeapp.apps:make_cap_filter """, - 'paste.filter_app_factory1': """ + 'paste.filter_app_factory': """ caps2=fakeapp.apps:CapFilter """, }, diff --git a/tests/sample_configs/test_config_included.ini b/tests/sample_configs/test_config_included.ini index aa24a61..cc0da7a 100644 --- a/tests/sample_configs/test_config_included.ini +++ b/tests/sample_configs/test_config_included.ini @@ -4,7 +4,7 @@ def3 = c [app:main] # Equivalent to the egg reference, but just for kicks... -paste.app_factory1 = fakeapp.configapps:SimpleApp.make_app +paste.app_factory = fakeapp.configapps:SimpleApp.make_app set glob = orig bob = your uncle another = BAR diff --git a/tests/sample_configs/test_filter.ini b/tests/sample_configs/test_filter.ini index f948b97..bfad8dc 100644 --- a/tests/sample_configs/test_filter.ini +++ b/tests/sample_configs/test_filter.ini @@ -17,3 +17,6 @@ use = egg:FakeApp#caps2 method_to_call = lower next = normal +[app:inv] +use = egg:FakeApp#basic_app +filter-with = egg:FakeApp#caps diff --git a/tests/test_filter.py b/tests/test_filter.py index 5740223..6829ada 100644 --- a/tests/test_filter.py +++ b/tests/test_filter.py @@ -31,3 +31,9 @@ def test_pipeline2(): assert isinstance(app, fakeapp.apps.CapFilter) assert app.app is fakeapp.apps.basic_app assert app.method_to_call == 'upper' + +def test_filter_app_inverted(): + app = loadapp('config:sample_configs/test_filter.ini#inv', + relative_to=here) + assert isinstance(app, fakeapp.apps.CapFilter) + assert app.app is fakeapp.apps.basic_app |