[Web-SIG] Entry points and import maps (was Re: Scarecrow deployment config

Chris McDonough chrism at plope.com
Mon Jul 25 18:59:22 CEST 2005


Great.  Given that, I've created the beginnings of a more formal
specification:

WSGI Deployment Specification
-----------------------------

  I use the term "WSGI component" in here as shorthand to indicate all
  types of WSGI implementations (application, middleware).

  The primary deployment concern is to create a way to specify the
  configuration of an instance of a WSGI component within a
  declarative configuration file.  A secondary deployment concern is
  to create a way to "wire up" components together into a specific
  deployable "pipeline".

Pipeline Descriptors
--------------------

  Pipeline descriptors are file representations of a particular WSGI
  "pipeline".  They include enough information to configure,
  instantiate, and wire together WSGI apps and middleware components
  into one pipeline for use by a WSGI server.  Installation of the
  software which composes those components is handled separately.

  In order to define a pipeline, we use a ".ini"-format configuration
  file conventionally named '<something>.wsgi'.  This file may
  optionally be marked as executable and associated with a simple UNIX
  interpreter via a leading hash-bang line to allow servers which
  employ stdin and stdout streams (ala CGI) to run the pipeline
  directly without any intermediation.  For example, a deployment
  descriptor named 'myapplication.wsgi' might be composed of the
  following text::

    #!/usr/bin/runwsgi

    [mypackage.mymodule.factory1]
    quux = arbitraryvalue
    eekx = arbitraryvalue

    [mypackage.mymodule.factory2]
    foo = arbitraryvalue
    bar = arbitraryvalue

  Section names are Python-dotted-path names (or setuptools "entry
  point names" described in a later section) which represent
  factories.  Key-value pairs within a given section are used as
  keyword arguments to the factory that can be used as configuration
  for the component being instantiated.

  All sections in the deployment descriptor describe 'middleware'
  except for the last section, which must describe an application.

  Factories which construct middleware must return something which is
  a WSGI "callable" by implementing the following API::

     def factory(next_app, [**kw]):
         """ next_app is the next application in the WSGI pipeline,
         **kw is optional, and accepts the key-value pairs
         that are used in the section as a dictionary, used
         for configuration """

  Factories which construct middleware must return something which is
  a WSGI "callable" by implementing the following API::

     def factory([**kw]):
         """" **kw is optional, and accepts the key-value pairs
          that are used in the section as a dictionary, used
          for configuration """

  A deployment descriptor can also be parsed from within Python.  An
  importable configurator which resides in 'wsgiref' exposes a
  function that accepts a single argument, "configure"::

    >>> from wsgiref.runwsgi import parse_deployment
    >>> appchain = parse_deployment('myapplication.wsgi')

  'appchain' will be an object representing the fully configured
  "pipeline".  'parse_deployment' is guaranteed to return something
  that implements the WSGI "callable" API described in PEP 333.

Entry Points

  <description of setuptools entry points goes here>



On Mon, 2005-07-25 at 10:39 -0400, Phillip J. Eby wrote:
> At 03:02 AM 7/25/2005 -0400, Chris McDonough wrote:
> >Actually, let me give this a shot.
> >
> >We package up an egg called helloworld.egg.  It happens to contain
> >something that can be used as a WSGI component.  Let's say it's a WSGI
> >application that always returns 'Hello World'.  And let's say it also
> >contains middleware that lowercases anything that passes through before
> >it's returned.
> >
> >The implementations of these components could be as follows:
> >
> >class HelloWorld:
> >     def __init__(self, app, **kw):
> >         pass # nothing to configure
> >
> >     def __call__(self, environ, start_response):
> >         start_response('200 OK', [])
> >         return ['Hello World']
> 
> I'm thinking that an application like this wouldn't take an 'app' 
> constuctor parameter, and if it takes no configuration parameters it 
> doesn't need **kw, but good so far.
> 
> 
> >class Lowercaser:
> >     def __init__(self, app, **kw):
> >         self.app = app
> >         # nothing else to configure
> >
> >     def __call__(self, environ, start_response):
> >         for chunk in self.app(environ, start_response):
> >             yield chunk.lower()
> 
> Again, no need for **kw if it doesn't take any configuration, but okay.
> 
> 
> >An import map would ship inside of the egg-info dir:
> >
> >[wsgi.app_factories]
> >helloworld = helloworld:HelloWorld
> >lowercaser = helloworld:Lowercaser
> 
> I'm thinking it would be more like:
> 
>      [wsgi.middleware]
>      lowercaser = helloworld:Lowercaser
> 
>      [wsgi.apps]
>      helloworld = helloworld:HelloWorld
> 
> and you'd specify it in the setup script as something like this:
> 
>      setup(
>          #...
>          entry_points = {
>              'wsgi.apps': ['helloworld = helloworld:HelloWorld']
>              'wsgi.middleware': ['lowercaser = helloworld:Lowercaser']
>          }
>      )
> 
> (And the CVS version of setuptools already supports this.)
> 
> 
> 
> >So we install the egg and this does nothing except allow it to be used
> >from within Python.
> >
> >But when we create a "deployment descriptor" like so in a text editor:
> >
> >[helloworld from helloworld]
> >
> >[lowercaser from helloworld]
> 
> Opposite order, though; the lowercaser comes first because it's the 
> middleware; the application would always come last, because they're listed 
> in the order in which they receive data, just like a pipes-and-filters 
> command line.
> 
> 
> >... and run some "starter" script that parses that as a pipeline,
> 
> ... possibly using a #! line if you're using CGI or FastCGI with Apache or 
> some other non-Python webserver.
> 
> 
> >creates the two instances, wires them together, and we get a running
> >pipeline?
> >
> >Am I on track?
> 
> Definitely.
> 



More information about the Web-SIG mailing list