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

2005-07-24 Thread Ian Bicking
Phillip J. Eby wrote:
>> It goes in the .egg-info directory.  This way elsewhere you can say:
>>
>>application = SomeApplication[feature1]
> 
> 
> I like this a lot, although for a different purpose than the format 
> Chris and I were talking about.  

Yes, this proposal really just simplifies a part of that application 
deployment configuration, it doesn't replace it.  Though it might make 
other standardization less important.

> I see this fitting into that format as 
> maybe:
> 
>[feature1 from SomeApplication]
># configuration here
> 
> 
>> And it's quite unambiguous.  Note that there is *no* "configuration" in
>> the egg-info file, because you can't put any configuration related to a
>> deployment in an .egg-info directory, because it's not specific to any
>> deployment.  Obviously we still need a way to get configuration in
>> there, but lets say that's a different matter.
> 
> 
> Easily fixed via what I've been thinking of as the "deployment 
> descriptor"; I would call your proposal here the "import map".  
> Basically, an import map describes a mapping from some sort of feature 
> name to qualified names in the code.

Yes, it really just gives you a shorthand for the factory configuration 
variable.

> I have an extension that I would make, though.  Instead of using 
> sections for features, I would use name/value pairs inside of sections 
> named for the kind of import map.  E.g.:
> 
> [wsgi.app_factories]
> feature1 = somemodule:somefunction
> feature2 = another.module:SomeClass
> ...
> 
> [mime.parsers]
> application/atom+xml = something:atom_parser
> ...

I assume mime.parsers is just a theoretical example of another kind of 
service a package can provide?  But yes, this seems very reasonable, and 
even allows for loosely versioned specs (e.g., wsgi.app_factories02, 
which returns factories with a different interface; or maybe something 
like foo.configuration_schema, an optional entry point that returns the 
configuration schema for an application described elsewhere).

This kind of addresses the issue where the module structure of a package 
becomes an often unintentional part of its external interface.  It feels 
a little crude in that respect... but maybe not.  Is it worse to do:

   from package.module import name

or:

   name = require('Package').load_entry_point('service_type', 'name')

OK, well clearly the second is worse ;)  But if that turned into a 
single function call:

   name = load_service('Package', 'service_type', 'name')

It's not that bad.  Maybe even:

   name = services['Package:service_type:name']

Though service_type feels extraneous to me.  I see the benefit of being 
explicit about what the factory provides, but I don't see the benefit of 
separating namespaces; the name should be unambiguous.  Well... unless 
you used the same name to group related services, like the configuration 
schema and the application factory itself.  So maybe I retract that 
criticism.

> In addition to specifying the entry point, each entry in the import map 
> could optionally list the "extras" that are required if that entry point 
> is used.
> It could also issue a 'require()' for the corresponding feature if it 
> has any additional requirements listed in the extras_require dictionary.

I figured each entry point would just map to a feature, so the 
extra_require dictionary would already have entries.

> So, I'm thinking that this would be implemented with an entry_points.txt 
> file in .egg-info, but supplied in setup.py like this:
> 
> setup(
> ...
> entry_points = {
> "wsgi.app_factories": dict(
> feature1 = "somemodule:somefunction",
> feature2 = "another.module:SomeClass [extra1,extra2]",
> ),
> "mime.parsers": {
> "application/atom+xml": "something:atom_parser 
> [feedparser]"
> }
> },
> extras_require = dict(
> feedparser = [...],
> extra1 = [...],
> extra2 = [...],
> )
> )

I think I'd rather just put the canonical version in .egg-info instead 
of as an argument to setup(); this is one place where using Python 
expressions isn't a shining example of clarity.  But I guess this is 
fine too; for clarity I'll probably start writing my setup.py files with 
variable assignments, then a setup() call that just refers to those 
variables.

>> Open issues?  Yep, there's a bunch.  This requires the rest of the
>> configuration to be done quite lazily.
> 
> 
> Not sure I follow you; the deployment descriptor could contain all the 
> configuration; see the Web-SIG post I made just previous to this one.

Well, when I proposed that the factory be called with zero arguments, 
that wouldn't allow any configuration to be passed in.

>>   I don't think
>> this is useful without the other pieces (both in front of this
>> configuration file and behind it) but maybe we can think abo

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

2005-07-24 Thread Phillip J. Eby
At 02:12 PM 7/24/2005 -0500, Ian Bicking wrote:
>This kind of addresses the issue where the module structure of a package 
>becomes an often unintentional part of its external interface.  It feels a 
>little crude in that respect... but maybe not.  Is it worse to do:
>
>   from package.module import name
>
>or:
>
>   name = require('Package').load_entry_point('service_type', 'name')
>
>OK, well clearly the second is worse ;)  But if that turned into a single 
>function call:
>
>   name = load_service('Package', 'service_type', 'name')
>
>It's not that bad.  Maybe even:
>
>   name = services['Package:service_type:name']

The actual API I have implemented in my CVS working copy is:

the_object = load_entry_point('Project', 'group', 'name')

which seems pretty clean to me.  You can also use 
dist.load_entry_point('group','name') if you already have a distribution 
object for some reason.  (For example, if you use an activation listener to 
get callbacks when distributions are activated on sys.path.)

To introspect an entry point or check for its existence, you can use:

entry_point = get_entry_info('Project', 'group', 'name')

which returns either None or an EntryPoint object with various 
attributes.  To list the entry points of a group, or to list the groups, 
you can use:

# dictionary of group names to entry map for each kind
group_names = get_entry_map('Project')

# dictionary of entry names to corresponding EntryPoint object
entry_names = get_entry_map('Project', 'group')

These are useful for dynamic entry points.


>Though service_type feels extraneous to me.  I see the benefit of being 
>explicit about what the factory provides, but I don't see the benefit of 
>separating namespaces; the name should be unambiguous.

You're making the assumption that the package author defines the entry 
point names, but that's not the case for application plugins; the 
application will define entry point names and group names for the 
application's use, and some applications will need multiple groups.  Groups 
might be keyed statically (i.e. a known set of entry point names) or 
dynamically (the keys are used to put things in a table, e.g. a file 
extension handler table).


>>In addition to specifying the entry point, each entry in the import map 
>>could optionally list the "extras" that are required if that entry point 
>>is used.
>>It could also issue a 'require()' for the corresponding feature if it has 
>>any additional requirements listed in the extras_require dictionary.
>
>I figured each entry point would just map to a feature, so the 
>extra_require dictionary would already have entries.

The problem with that is that asking for a feature that's not in 
extras_require is an InvalidOption error, so this would force you to define 
entries in extras_require even if you have no extras involved.  It would 
also make for redundancies when entry points share an extra.  I also don't 
expect extras to be used as frequently as entry points.


>>So, I'm thinking that this would be implemented with an entry_points.txt 
>>file in .egg-info, but supplied in setup.py like this:
>> setup(
>> ...
>> entry_points = {
>> "wsgi.app_factories": dict(
>> feature1 = "somemodule:somefunction",
>> feature2 = "another.module:SomeClass [extra1,extra2]",
>> ),
>> "mime.parsers": {
>> "application/atom+xml": "something:atom_parser [feedparser]"
>> }
>> },
>> extras_require = dict(
>> feedparser = [...],
>> extra1 = [...],
>> extra2 = [...],
>> )
>> )
>
>I think I'd rather just put the canonical version in .egg-info instead of 
>as an argument to setup(); this is one place where using Python 
>expressions isn't a shining example of clarity.  But I guess this is fine 
>too; for clarity I'll probably start writing my setup.py files with 
>variable assignments, then a setup() call that just refers to those variables.

The actual syntax I'm going to end up with is:

   entry_points = {
   "wsgi.app_factories": [
   "feature1 = somemodule:somefunction",
   "feature2 = another.module:SomeClass [extra1,extra2]",
   ]
   }

Which is still not great, but it's a bit simpler.  If you only have one 
entry point, you can use:

   entry_points = {
   "wsgi.app_factories": "feature = somemodule:somefunction",
   }

Or you can use a long string for each group:

   entry_points = {
   "wsgi.app_factories": """
   # define features for blah blah
   feature1 = somemodule:somefunction
   feature2 = another.module:SomeClass [extra1,extra2]
   """
   }

Or even list everything in one giant string:

   entry_points = """
   [wsgi.app_factories]
   # define features for blah blah
   feature1 = somemodule:somefu

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

2005-07-24 Thread Phillip J. Eby
At 02:12 PM 7/24/2005 -0500, Ian Bicking wrote:
>Phillip J. Eby wrote:
>>However, after more thought, I think that the "next application" argument 
>>should be a keyword argument too, like 'wsgi_next' or some such.  This 
>>would allow a factory to have required arguments in its signature, e.g.:
>> def some_factory(required_arg_x, required_arg_y, optional_arg="foo", 
>> ):
>> ...
>>The problem with my original idea to have the "next app" be a positional 
>>argument is that it would prevent non-middleware applications from having 
>>any required arguments.
>
>I think it's fine to declare the next_app keyword argument as special, and 
>promise (by convention) to always pass it in with that name.

Actually, now that we have the "entry points" capability in pkg_resources 
(I just checked it in), we could simply have middleware components looked 
up in 'wsgi.middleware_factories' and applications looked up in 
'wsgi.application_factories'.  If a factory can be used for both, you can 
always list it in both places.

Entry points have 1001 uses...  I can imagine applications defining entry 
point groups for URL namespaces.  For example, Trac has URLs like 
/changesets and /roadmap, and these could be defined via a trac.navigation 
entry point group, e.g.:

 [trac.navigation]
 changesets = some.module:foo
 roadmap = other.module:bar

And then people could easily create plugin projects that add additional 
navigation components.  (Trac already has an internal extension point 
system to do things rather like this, but entry points are automatically 
discoverable without any prior knowledge of what modules to import.)

There are other frameworks out there (e.g. PyBlosxom), both web and 
non-web, that could really do nicely with having a standard way to do this 
kind of thing, rather than having to roll their own.

___
Web-SIG mailing list
Web-SIG@python.org
Web SIG: http://www.python.org/sigs/web-sig
Unsubscribe: 
http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com


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

2005-07-24 Thread Ian Bicking
Phillip J. Eby wrote:
> The actual syntax I'm going to end up with is:
> 
>   entry_points = {
>   "wsgi.app_factories": [
>   "feature1 = somemodule:somefunction",
>   "feature2 = another.module:SomeClass [extra1,extra2]",
>   ]
>   }

That seems weird to put the assignment inside a string, instead of:

entry_points = {
   'wsgi.app_factories': {
 'app': 'somemodule:somefunction',
   },
}

Also, is there any default name?  Like for a package that distributes 
only one application.  Or these just different spellings for the same thing?


-- 
Ian Bicking  /  [EMAIL PROTECTED]  / http://blog.ianbicking.org
___
Web-SIG mailing list
Web-SIG@python.org
Web SIG: http://www.python.org/sigs/web-sig
Unsubscribe: 
http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com


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

2005-07-24 Thread Chris McDonough
Sorry, I think I may have lost track of where we were going wrt the
deployment spec.  Specifically, I don't know how we got to using eggs
(which I'd really like to, BTW, they're awesome conceptually!) from
where we were in the discussion about configuring a WSGI pipeline.  What
is a "feature"?  What is an "import map"? "Entry point"?  Should I just
get more familiar with eggs to understand what's being discussed here or
did I miss a few posts?

On Sun, 2005-07-24 at 12:49 -0400, Phillip J. Eby wrote:
> [cc:ed to distutils-sig because much of the below is about a new egg 
> feature; follow-ups about the web stuff should stay on web-sig]
> 
> At 04:04 AM 7/24/2005 -0500, Ian Bicking wrote:
> >So maybe here's a deployment spec we can start with.  It looks like:
> >
> >[feature1]
> >someapplication.somemodule.some_function
> >
> >[feature2]
> >someapplication.somemodule.some_function2
> >
> >You can't get dumber than that!  There should also be a "no-feature"
> >section; maybe one without a section identifier, or some special section
> >identifier.
> >
> >It goes in the .egg-info directory.  This way elsewhere you can say:
> >
> >application = SomeApplication[feature1]
> 
> I like this a lot, although for a different purpose than the format Chris 
> and I were talking about.  I see this fitting into that format as maybe:
> 
> [feature1 from SomeApplication]
> # configuration here
> 
> 
> >And it's quite unambiguous.  Note that there is *no* "configuration" in
> >the egg-info file, because you can't put any configuration related to a
> >deployment in an .egg-info directory, because it's not specific to any
> >deployment.  Obviously we still need a way to get configuration in
> >there, but lets say that's a different matter.
> 
> Easily fixed via what I've been thinking of as the "deployment descriptor"; 
> I would call your proposal here the "import map".  Basically, an import map 
> describes a mapping from some sort of feature name to qualified names in 
> the code.
> 
> I have an extension that I would make, though.  Instead of using sections 
> for features, I would use name/value pairs inside of sections named for the 
> kind of import map.  E.g.:
> 
>  [wsgi.app_factories]
>  feature1 = somemodule:somefunction
>  feature2 = another.module:SomeClass
>  ...
> 
>  [mime.parsers]
>  application/atom+xml = something:atom_parser
>  ...
> 
> In other words, feature maps could be a generic mechanism offered by 
> setuptools, with a 'Distribution.load_entry_point(kind,name)' API to 
> retrieve the desired object.  That way, we don't end up reinventing this 
> idea for dozens of frameworks or pluggable applications that just need a 
> way to find a few simple entry points into the code.
> 
> In addition to specifying the entry point, each entry in the import map 
> could optionally list the "extras" that are required if that entry point is 
> used.
> It could also issue a 'require()' for the corresponding feature if it has 
> any additional requirements listed in the extras_require dictionary.
> 
> So, I'm thinking that this would be implemented with an entry_points.txt 
> file in .egg-info, but supplied in setup.py like this:
> 
>  setup(
>  ...
>  entry_points = {
>  "wsgi.app_factories": dict(
>  feature1 = "somemodule:somefunction",
>  feature2 = "another.module:SomeClass [extra1,extra2]",
>  ),
>  "mime.parsers": {
>  "application/atom+xml": "something:atom_parser [feedparser]"
>  }
>  },
>  extras_require = dict(
>  feedparser = [...],
>  extra1 = [...],
>  extra2 = [...],
>  )
>  )
> 
> Anyway, this would make the most common use case for eggs-as-plugins very 
> easy: an application or framework would simply define entry points, and 
> plugin projects would declare the ones they offer in their setup script.
> 
> I think this is a fantastic idea and I'm about to leap into implementing 
> it.  :)
> 
> 
> >This puts complex middleware construction into the function that is
> >referenced.  This function might be, in turn, an import from a
> >framework.  Or it might be some complex setup specific to the
> >application.  Whatever.
> >
> >The API would look like:
> >
> >wsgiapp = wsgiref.get_egg_application('SomeApplication[feature1]')
> >
> >Which ultimately resolves to:
> >
> >wsgiapp = some_function()
> >
> >get_egg_application could also take a pkg_resources.Distribution object.
> 
> Yeah, I'm thinking that this could be implemented as something like:
> 
>  import pkg_resources
> 
>  def get_wsgi_app(project_name, app_name, *args, **kw):
>  dist = pkg_resources.require(project_name)[0]
>  return dist.load_entry_point('wsgi.app_factories', 
> app_name)(*args,**kw)
> 
> with all the heavy lifting happening in the pkg_resources.Distribution 
> class, 

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

2005-07-24 Thread Ian Bicking
Chris McDonough wrote:
> Sorry, I think I may have lost track of where we were going wrt the
> deployment spec.  Specifically, I don't know how we got to using eggs
> (which I'd really like to, BTW, they're awesome conceptually!) from
> where we were in the discussion about configuring a WSGI pipeline.  What
> is a "feature"?  What is an "import map"? "Entry point"?  Should I just
> get more familiar with eggs to understand what's being discussed here or
> did I miss a few posts?

It wouldn't hurt to read up on eggs.  It's not obvious how they fit 
here, and it's taken me a while to figure it out.  But specifically:

* Eggs are packages.  Packages can have optional features.  Those 
features can have additional requirements (external packages) that the 
base package does not have.  Package specifications are spelled like 
"PackageName>=VERSION_NUMBER[FeatureName]"

* Import maps and entry points are new things we're discussing now. 
They are kind of the same thing; basically an entry point maps a logical 
specification (like a 'wsgi.app_factory' named 'foo') to a actual import 
statement.  That's the configuration file:

   [wsgi.app_factory]
   app = mymodule.wsgi:make_app

Which means to get an object "app" which fulfills the spec 
"wsgi.app_factory" you would do "from mymodule.wsgi import make_app"

Eggs have an PackageName.egg-info directory, where configuration files 
can go, and pkg_resources (which is part of setuptools, and associated 
with easy_install, and defines the require() function) can find and 
parse them.

-- 
Ian Bicking  /  [EMAIL PROTECTED]  / http://blog.ianbicking.org
___
Web-SIG mailing list
Web-SIG@python.org
Web SIG: http://www.python.org/sigs/web-sig
Unsubscribe: 
http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com


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

2005-07-24 Thread Phillip J. Eby
At 07:26 PM 7/24/2005 -0500, Ian Bicking wrote:
>Phillip J. Eby wrote:
>>The actual syntax I'm going to end up with is:
>>   entry_points = {
>>   "wsgi.app_factories": [
>>   "feature1 = somemodule:somefunction",
>>   "feature2 = another.module:SomeClass [extra1,extra2]",
>>   ]
>>   }
>
>That seems weird to put the assignment inside a string, instead of:
>
>entry_points = {
>   'wsgi.app_factories': {
> 'app': 'somemodule:somefunction',
>   },
>}

It turned out that EntryPoint objects really want to know their 'name' for 
ease of use in various APIs, and it also made it really easy to do stuff 
like "map(EntryPoint.parse, lines)" to get a list of entry points from a 
list of lines.


>Also, is there any default name?

Huh?


>   Like for a package that distributes only one application.  Or these 
> just different spellings for the same thing?

I don't understand you.  The most minimal way to specify a single entry 
point in setup() is with:

 entry_points = """
 [groupname.here]
 entryname = some.thing:here
 """


___
Web-SIG mailing list
Web-SIG@python.org
Web SIG: http://www.python.org/sigs/web-sig
Unsubscribe: 
http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com


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

2005-07-24 Thread Ian Bicking
Phillip J. Eby wrote:
>>   Like for a package that distributes only one application.  Or these 
>> just different spellings for the same thing?
> 
> 
> I don't understand you.  The most minimal way to specify a single entry 
> point in setup() is with:
> 
> entry_points = """
> [groupname.here]
> entryname = some.thing:here
> """

Basically, in the (I think common) case where a package only provides 
one entry point, do we have to choose an arbitrary entry name.  Like, a 
package that implements one web application; it seems like that 
application would have to be named.  Maybe that name could match the 
package name, or a fixed name we agree upon, but otherwise it adds 
another name to the mix.


-- 
Ian Bicking  /  [EMAIL PROTECTED]  / http://blog.ianbicking.org
___
Web-SIG mailing list
Web-SIG@python.org
Web SIG: http://www.python.org/sigs/web-sig
Unsubscribe: 
http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com


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

2005-07-24 Thread Phillip J. Eby
At 08:35 PM 7/24/2005 -0400, Chris McDonough wrote:
>Sorry, I think I may have lost track of where we were going wrt the
>deployment spec.  Specifically, I don't know how we got to using eggs
>(which I'd really like to, BTW, they're awesome conceptually!) from
>where we were in the discussion about configuring a WSGI pipeline.  What
>is a "feature"?  What is an "import map"? "Entry point"?  Should I just
>get more familiar with eggs to understand what's being discussed here or
>did I miss a few posts?

I suggest this post as the shortest architectural introduction to the whole 
egg thang:

 http://mail.python.org/pipermail/distutils-sig/2005-June/004652.html

It explains pretty much all of the terminology I'm currently using, except 
for the new terms invented today...

Entry points are a new concept, invented today by Ian and myself.  Ian 
proposed having a mapping file (which I dubbed an "import map") included in 
an egg's metadata, and then referring to named entries from a pipeline 
descriptor, so that you don't have to know or care about the exact name to 
import.  The application or middleware factory name would be looked up in 
the egg's import map in order to find the actual factory object.

I took Ian's proposal and did two things:

1) Generalized the idea to a concept of "entry points".  An entry point is 
a name that corresponds to an import specification, and an optional list of 
"extras" (see terminology link above) that the entry point may 
require.  Entry point names exist in a namespace called an "entry point 
group", and I implied that the WSGI deployment spec would define two such 
groups: wsgi.applications and wsgi.middleware, but a vast number of other 
possibilities for entry points and groups exist.  In fact, I went ahead and 
implemented them in setuptools today, and realized I could use them to 
register setup commands with setuptools, making it extensible by any 
project that registers entry points in a 'distutils.commands' group.

2) I then proposed that we extend our deployment descriptor (.wsgi file) 
syntax so that you can do things like:

 [foo from SomeProject]
 # configuration here

What this does is tell the WSGI deployment API to look up the "foo" entry 
point in either the wsgi.middleware or wsgi.applications entry point group 
for the named project, according to whether it's the last item in the .wsgi 
file.  It then invokes the factory as before, with the configuration values 
as keyword arguments.

This proposal is of course an *extension*; it should still be possible to 
use regular dotted names as section headings, if you haven't yet drunk the 
setuptools kool-aid.  But, it makes for interesting possibilities because 
we could now have a tool that reads a WSGI deployment descriptor and runs 
easy_install to find and download the right projects.  So, you could 
potentially just write up a descriptor that lists what you want and the 
server could install it, although I think I personally would want to run a 
tool explicitly; maybe I'll eventually add a --wsgi=FILENAME option to 
EasyInstall that would tell it to find out what to install from a WSGI 
deployment descriptor.

That would actually be pretty cool, when you realize it means that all you 
have to do to get an app deployed across a bunch of web servers is to copy 
the deployment descriptor and tell 'em to install stuff.  You can always 
create an NFS-mounted cache directory where you put pre-built eggs, and 
EasyInstall would just fetch and extract them in that case.

Whew.  Almost makes me wish I was back in my web apps shop, where this kind 
of thing would've been *really* useful to have.

___
Web-SIG mailing list
Web-SIG@python.org
Web SIG: http://www.python.org/sigs/web-sig
Unsubscribe: 
http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com


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

2005-07-24 Thread Phillip J. Eby
At 08:49 PM 7/24/2005 -0500, Ian Bicking wrote:
>Chris McDonough wrote:
>>Sorry, I think I may have lost track of where we were going wrt the
>>deployment spec.  Specifically, I don't know how we got to using eggs
>>(which I'd really like to, BTW, they're awesome conceptually!) from
>>where we were in the discussion about configuring a WSGI pipeline.  What
>>is a "feature"?  What is an "import map"? "Entry point"?  Should I just
>>get more familiar with eggs to understand what's being discussed here or
>>did I miss a few posts?
>
>It wouldn't hurt to read up on eggs.  It's not obvious how they fit here, 
>and it's taken me a while to figure it out.  But specifically:
>
>* Eggs are packages.  Packages can have optional features.

I've taken to using the term "project" to mean a collection of packages, 
scripts, data files, etc., wrapped with a setup script.  In order to avoid 
confusion with other kinds of "features" and "options", the official term 
for those things is now "extras".  An "extra" is some optional capability 
of a project that may incur additional requirements.


>   Those features can have additional requirements (external packages) 
> that the base package does not have.  Package specifications are spelled 
> like "PackageName>=VERSION_NUMBER[FeatureName]"

Actually, it's "ProjectName[extra,...]>=version", and you can list multiple 
version operators, like "FooBar>1.2,<2.1,==2.6,>3.0" to mean versions 
between 1.2 and 2.1 exclusive, and anything *after* 3.0, but 2.6 was okay 
too.  :)

I'm proposing that for WSGI entry points, we allow everything but the 
[extras_list] in a section heading, e.g.:

 [wiki from FooBarWiki>=2.0]

would mean what it looks like it does.  By the way, all this version 
parsing, dependency checking, PyPI-finding, auto-download and build from 
source or binary stuff already exists; it's not a hypothetical 
pie-in-the-sky proposal.


>* Import maps and entry points are new things we're discussing now. They 
>are kind of the same thing; basically an entry point maps a logical 
>specification (like a 'wsgi.app_factory' named 'foo') to a actual import 
>statement.  That's the configuration file:
>
>   [wsgi.app_factory]
>   app = mymodule.wsgi:make_app
>
>Which means to get an object "app" which fulfills the spec 
>"wsgi.app_factory" you would do "from mymodule.wsgi import make_app"
>
>Eggs have an PackageName.egg-info directory, where configuration files can 
>go, and pkg_resources (which is part of setuptools, and associated with 
>easy_install, and defines the require() function) can find and parse them.

Yes, and with the CVS HEAD version of setuptools you can now specify a 
project's entry point map in it setup script, and it will generate the 
entry point file in the project's .egg-info directory, and parse it at 
runtime when you request lookup of an entry point.  There's an API in 
pkg_resources that lets you do:

 factory = load_entry_point("ProjectName", "wsgi.app_factory", "app")

which will do the same as if you had said "from mymodule.wsgi import 
make_app as factory".

___
Web-SIG mailing list
Web-SIG@python.org
Web SIG: http://www.python.org/sigs/web-sig
Unsubscribe: 
http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com


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

2005-07-24 Thread Chris McDonough
Thanks...

I'm still confused about high level requirements so please try to be
patient with me as I try get back on track.

These are the requirements as I understand them:

1.  We want to be able to distribute WSGI applications and middleware
(presumably in a format supported by setuptools).

3.  We want to be able to configure a WSGI application in order
to create an application instance.

2.  We want a way to combine configured instances of those
applications into pipelines and start an "instance" of a pipeline.

Are these requirements the ones being discussed?  If so, which of the
config file formats we've been discussing matches which requirement?

Thanks,

- C

On Sun, 2005-07-24 at 22:24 -0400, Phillip J. Eby wrote:
> At 08:35 PM 7/24/2005 -0400, Chris McDonough wrote:
> >Sorry, I think I may have lost track of where we were going wrt the
> >deployment spec.  Specifically, I don't know how we got to using eggs
> >(which I'd really like to, BTW, they're awesome conceptually!) from
> >where we were in the discussion about configuring a WSGI pipeline.  What
> >is a "feature"?  What is an "import map"? "Entry point"?  Should I just
> >get more familiar with eggs to understand what's being discussed here or
> >did I miss a few posts?
> 
> I suggest this post as the shortest architectural introduction to the whole 
> egg thang:
> 
>  http://mail.python.org/pipermail/distutils-sig/2005-June/004652.html
> 
> It explains pretty much all of the terminology I'm currently using, except 
> for the new terms invented today...
> 
> Entry points are a new concept, invented today by Ian and myself.  Ian 
> proposed having a mapping file (which I dubbed an "import map") included in 
> an egg's metadata, and then referring to named entries from a pipeline 
> descriptor, so that you don't have to know or care about the exact name to 
> import.  The application or middleware factory name would be looked up in 
> the egg's import map in order to find the actual factory object.
> 
> I took Ian's proposal and did two things:
> 
> 1) Generalized the idea to a concept of "entry points".  An entry point is 
> a name that corresponds to an import specification, and an optional list of 
> "extras" (see terminology link above) that the entry point may 
> require.  Entry point names exist in a namespace called an "entry point 
> group", and I implied that the WSGI deployment spec would define two such 
> groups: wsgi.applications and wsgi.middleware, but a vast number of other 
> possibilities for entry points and groups exist.  In fact, I went ahead and 
> implemented them in setuptools today, and realized I could use them to 
> register setup commands with setuptools, making it extensible by any 
> project that registers entry points in a 'distutils.commands' group.
> 
> 2) I then proposed that we extend our deployment descriptor (.wsgi file) 
> syntax so that you can do things like:
> 
>  [foo from SomeProject]
>  # configuration here
> 
> What this does is tell the WSGI deployment API to look up the "foo" entry 
> point in either the wsgi.middleware or wsgi.applications entry point group 
> for the named project, according to whether it's the last item in the .wsgi 
> file.  It then invokes the factory as before, with the configuration values 
> as keyword arguments.
> 
> This proposal is of course an *extension*; it should still be possible to 
> use regular dotted names as section headings, if you haven't yet drunk the 
> setuptools kool-aid.  But, it makes for interesting possibilities because 
> we could now have a tool that reads a WSGI deployment descriptor and runs 
> easy_install to find and download the right projects.  So, you could 
> potentially just write up a descriptor that lists what you want and the 
> server could install it, although I think I personally would want to run a 
> tool explicitly; maybe I'll eventually add a --wsgi=FILENAME option to 
> EasyInstall that would tell it to find out what to install from a WSGI 
> deployment descriptor.
> 
> That would actually be pretty cool, when you realize it means that all you 
> have to do to get an app deployed across a bunch of web servers is to copy 
> the deployment descriptor and tell 'em to install stuff.  You can always 
> create an NFS-mounted cache directory where you put pre-built eggs, and 
> EasyInstall would just fetch and extract them in that case.
> 
> Whew.  Almost makes me wish I was back in my web apps shop, where this kind 
> of thing would've been *really* useful to have.
> 

___
Web-SIG mailing list
Web-SIG@python.org
Web SIG: http://www.python.org/sigs/web-sig
Unsubscribe: 
http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com


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

2005-07-24 Thread Chris McDonough
BTW, a simple example that includes proposed solutions for all of these
requirements would go a long way towards helping me (and maybe others)
understand how all the pieces fit together.  Maybe something like:

- Define two simple WSGI components:  a WSGI middleware and a WSGI
  application.

- Describe how to package each as an indpendent egg.

- Describe how to configure an instance of the application.

- Describe how to configure an instance of the middleware

- Describe how to string them together into a pipeline.

- C


On Mon, 2005-07-25 at 02:33 -0400, Chris McDonough wrote:
> Thanks...
> 
> I'm still confused about high level requirements so please try to be
> patient with me as I try get back on track.
> 
> These are the requirements as I understand them:
> 
> 1.  We want to be able to distribute WSGI applications and middleware
> (presumably in a format supported by setuptools).
> 
> 3.  We want to be able to configure a WSGI application in order
> to create an application instance.
> 
> 2.  We want a way to combine configured instances of those
> applications into pipelines and start an "instance" of a pipeline.
> 
> Are these requirements the ones being discussed?  If so, which of the
> config file formats we've been discussing matches which requirement?
> 
> Thanks,
> 
> - C
> 
> On Sun, 2005-07-24 at 22:24 -0400, Phillip J. Eby wrote:
> > At 08:35 PM 7/24/2005 -0400, Chris McDonough wrote:
> > >Sorry, I think I may have lost track of where we were going wrt the
> > >deployment spec.  Specifically, I don't know how we got to using eggs
> > >(which I'd really like to, BTW, they're awesome conceptually!) from
> > >where we were in the discussion about configuring a WSGI pipeline.  What
> > >is a "feature"?  What is an "import map"? "Entry point"?  Should I just
> > >get more familiar with eggs to understand what's being discussed here or
> > >did I miss a few posts?
> > 
> > I suggest this post as the shortest architectural introduction to the whole 
> > egg thang:
> > 
> >  http://mail.python.org/pipermail/distutils-sig/2005-June/004652.html
> > 
> > It explains pretty much all of the terminology I'm currently using, except 
> > for the new terms invented today...
> > 
> > Entry points are a new concept, invented today by Ian and myself.  Ian 
> > proposed having a mapping file (which I dubbed an "import map") included in 
> > an egg's metadata, and then referring to named entries from a pipeline 
> > descriptor, so that you don't have to know or care about the exact name to 
> > import.  The application or middleware factory name would be looked up in 
> > the egg's import map in order to find the actual factory object.
> > 
> > I took Ian's proposal and did two things:
> > 
> > 1) Generalized the idea to a concept of "entry points".  An entry point is 
> > a name that corresponds to an import specification, and an optional list of 
> > "extras" (see terminology link above) that the entry point may 
> > require.  Entry point names exist in a namespace called an "entry point 
> > group", and I implied that the WSGI deployment spec would define two such 
> > groups: wsgi.applications and wsgi.middleware, but a vast number of other 
> > possibilities for entry points and groups exist.  In fact, I went ahead and 
> > implemented them in setuptools today, and realized I could use them to 
> > register setup commands with setuptools, making it extensible by any 
> > project that registers entry points in a 'distutils.commands' group.
> > 
> > 2) I then proposed that we extend our deployment descriptor (.wsgi file) 
> > syntax so that you can do things like:
> > 
> >  [foo from SomeProject]
> >  # configuration here
> > 
> > What this does is tell the WSGI deployment API to look up the "foo" entry 
> > point in either the wsgi.middleware or wsgi.applications entry point group 
> > for the named project, according to whether it's the last item in the .wsgi 
> > file.  It then invokes the factory as before, with the configuration values 
> > as keyword arguments.
> > 
> > This proposal is of course an *extension*; it should still be possible to 
> > use regular dotted names as section headings, if you haven't yet drunk the 
> > setuptools kool-aid.  But, it makes for interesting possibilities because 
> > we could now have a tool that reads a WSGI deployment descriptor and runs 
> > easy_install to find and download the right projects.  So, you could 
> > potentially just write up a descriptor that lists what you want and the 
> > server could install it, although I think I personally would want to run a 
> > tool explicitly; maybe I'll eventually add a --wsgi=FILENAME option to 
> > EasyInstall that would tell it to find out what to install from a WSGI 
> > deployment descriptor.
> > 
> > That would actually be pretty cool, when you realize it means that all you 
> > have to do to get an app deployed across a bunch of web servers is to copy 
> > the deployment descriptor and te

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

2005-07-25 Thread Chris McDonough
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']

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()

An import map would ship inside of the egg-info dir:

[wsgi.app_factories]
helloworld = helloworld:HelloWorld
lowercaser = helloworld:Lowercaser

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]

... and run some "starter" script that parses that as a pipeline,
creates the two instances, wires them together, and we get a running
pipeline?

Am I on track?

OK, back to Battlestar Galactica ;-)



On Mon, 2005-07-25 at 02:40 -0400, Chris McDonough wrote:
> BTW, a simple example that includes proposed solutions for all of these
> requirements would go a long way towards helping me (and maybe others)
> understand how all the pieces fit together.  Maybe something like:
> 
> - Define two simple WSGI components:  a WSGI middleware and a WSGI
>   application.
> 
> - Describe how to package each as an indpendent egg.
> 
> - Describe how to configure an instance of the application.
> 
> - Describe how to configure an instance of the middleware
> 
> - Describe how to string them together into a pipeline.
> 
> - C
> 
> 
> On Mon, 2005-07-25 at 02:33 -0400, Chris McDonough wrote:
> > Thanks...
> > 
> > I'm still confused about high level requirements so please try to be
> > patient with me as I try get back on track.
> > 
> > These are the requirements as I understand them:
> > 
> > 1.  We want to be able to distribute WSGI applications and middleware
> > (presumably in a format supported by setuptools).
> > 
> > 3.  We want to be able to configure a WSGI application in order
> > to create an application instance.
> > 
> > 2.  We want a way to combine configured instances of those
> > applications into pipelines and start an "instance" of a pipeline.
> > 
> > Are these requirements the ones being discussed?  If so, which of the
> > config file formats we've been discussing matches which requirement?
> > 
> > Thanks,
> > 
> > - C
> > 
> > On Sun, 2005-07-24 at 22:24 -0400, Phillip J. Eby wrote:
> > > At 08:35 PM 7/24/2005 -0400, Chris McDonough wrote:
> > > >Sorry, I think I may have lost track of where we were going wrt the
> > > >deployment spec.  Specifically, I don't know how we got to using eggs
> > > >(which I'd really like to, BTW, they're awesome conceptually!) from
> > > >where we were in the discussion about configuring a WSGI pipeline.  What
> > > >is a "feature"?  What is an "import map"? "Entry point"?  Should I just
> > > >get more familiar with eggs to understand what's being discussed here or
> > > >did I miss a few posts?
> > > 
> > > I suggest this post as the shortest architectural introduction to the 
> > > whole 
> > > egg thang:
> > > 
> > >  http://mail.python.org/pipermail/distutils-sig/2005-June/004652.html
> > > 
> > > It explains pretty much all of the terminology I'm currently using, 
> > > except 
> > > for the new terms invented today...
> > > 
> > > Entry points are a new concept, invented today by Ian and myself.  Ian 
> > > proposed having a mapping file (which I dubbed an "import map") included 
> > > in 
> > > an egg's metadata, and then referring to named entries from a pipeline 
> > > descriptor, so that you don't have to know or care about the exact name 
> > > to 
> > > import.  The application or middleware factory name would be looked up in 
> > > the egg's import map in order to find the actual factory object.
> > > 
> > > I took Ian's proposal and did two things:
> > > 
> > > 1) Generalized the idea to a concept of "entry points".  An entry point 
> > > is 
> > > a name that corresponds to an import specification, and an optional list 
> > > of 
> > > "extras" (see terminology link above) that the entry point may 
> > > require.  Entry point names exist in a namespace called an "entry point 
> > > group", and I implied that the WSGI deployment spec would define two such 
> > > groups: wsgi.applications and wsgi.middleware, but a vast number of other 
> > > possibilities for entry points and groups exist.  In fact, I went ahead 
> > > and 
> > > implement

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

2005-07-25 Thread Phillip J. Eby
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.

___
Web-SIG mailing list
Web-SIG@python.org
Web SIG: http://www.python.org/sigs/web-sig
Unsubscribe: 
http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com


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

2005-07-25 Thread Chris McDonough
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 '.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

  



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(

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

2005-07-25 Thread Phillip J. Eby
At 12:59 PM 7/25/2005 -0400, Chris McDonough wrote:
>   In order to define a pipeline, we use a ".ini"-format configuration

Ultimately I think the spec will need a formal description of what that 
means exactly, including such issues as a PEP 263-style "encoding" 
specifier, and the precise format of values.  But I'm fine with adding all 
that myself, since I'm going to have to specify it well enough to create a 
parser anyway.

With respect to the format, I'm actually leaning towards either treating 
the settings as Python assignment statements (syntactically speaking) or 
restricting the values to being single-token Python literals (i.e., 
numbers, strings, or True/False/None, but not tuples, lists, or other 
expressions).

Interestingly enough, I think you could actually define the entire format 
in terms of standard Python-language tokens, it's just the higher-level 
syntax that differs from Python's.  Although actually using the "tokenize" 
module to scan it would mean that all lines' content would need to start 
exactly at the left margin, with no indentation.  Probably not a big deal, 
though.

The syntax would probably be something like:

   pipeline::= section*
   section ::= heading assignment*
   heading ::= '[' qname trailer ']' NEWLINE
   assignment  ::= NAME '=' value NEWLINE

   qname   ::= NAME ('.' NAME) *
   trailer ::= "from" requirement | "options"
   value   ::= NUMBER | STRING | "True" | "False" | "None"
   requirement ::= NAME versionlist?
   versionlist ::= versionspec (',' versionspec)*
   versionspec ::= relop STRING
   relop   ::= "<" | "<=" | "==" | "!=" | ">=" | ">"

The versions would have to be strings in order to avoid problems parsing 
e.g '2.1a4' as a number.  And if we were going to allow structures like 
tuples or lists or dictionaries, then we'd need to expand on 'value' a 
little bit, but not as much as if we allowed arbitrary expressions.


>   file conventionally named '.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 that matter, while doing development and testing, the interpreter could 
be something like "#!invoke peak launch wsgifile", to launch the app in a 
web browser from a localhost http server.  (Assuming I added a "wsgifile" 
command to PEAK, of course.)


>   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 """

Note that you can also just list the parameter names you take, or no 
parameter names at all.  I don't want to imply that you *have* to use kw, 
because it's fairly easy to envision simple middleware components that only 
take two or three parameters, or maybe even just one (e.g., their config 
file name).


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

You probably meant "application" or "terminal application" here.  (Or 
whatever term we end up with for an application that isn't middleware.


>   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.

Or raise SyntaxError for a malformed descriptor file, or ImportError if an 
application import failed or an entry point couldn't be found, or 
DistributionNotFound if a needed egg couldn't be found, or VersionConflict 
if it needs a conflicting version.

Or really it could raise anything if one of the factories failed, come to 
think of it.

___
Web-SIG mailing list
Web-SIG@python.org
Web SIG: http://www.python.org/sigs/web-sig
Unsubscribe: 
http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com


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

2005-07-25 Thread Phillip J. Eby
At 01:35 PM 7/25/2005 -0400, Phillip J. Eby wrote:
>heading ::= '[' qname trailer ']' NEWLINE

Oops. That should've been "trailer?", since the trailer is optional.

___
Web-SIG mailing list
Web-SIG@python.org
Web SIG: http://www.python.org/sigs/web-sig
Unsubscribe: 
http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com


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

2005-07-25 Thread Ian Bicking
Phillip J. Eby wrote:
> At 12:59 PM 7/25/2005 -0400, Chris McDonough wrote:
> 
>>  In order to define a pipeline, we use a ".ini"-format configuration
> 
> 
> Ultimately I think the spec will need a formal description of what that 
> means exactly, including such issues as a PEP 263-style "encoding" 
> specifier, and the precise format of values.  But I'm fine with adding all 
> that myself, since I'm going to have to specify it well enough to create a 
> parser anyway.

Incidentally I have a generic ini parser here:

   http://svn.w4py.org/home/ianb/wsgikit_old_config/iniparser.py

I suspect I'm doing the character decoding improperly (line-by-line 
instead of opening the file with the given character encoding), but 
otherwise it's been sufficiently generic and workable, and should allow 
for doing more extensive parsing of things like section headers.

-- 
Ian Bicking  /  [EMAIL PROTECTED]  /  http://blog.ianbicking.org
___
Web-SIG mailing list
Web-SIG@python.org
Web SIG: http://www.python.org/sigs/web-sig
Unsubscribe: 
http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com


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

2005-07-25 Thread James Gardner
Hi All,

I'm a bit late coming to all this and didn't really see the benefits of 
the new format over what we already do so I set out to contrast new and 
old to demonstrate why it wasn't *that* useful. I've since changed my 
mind and think it is great but here is the contrasting I did anyway. I'd 
be pleased to hear all the glaring errors :-)

Here is a new example: we want to have an application that returns a 
GZip encoded "hello world" string after it has been made lowercase by 
case changer middleware taking a parameter newCase. The GZip middleware 
is an optional feature of the modules in wsgiFilters.egg and the 
CaseChanger middleware and HelloWorld application are in the helloworld.egg.

The classes look like this:

class HelloWorld:
def __call__(self, environ, start_response):
start_response('200 OK', [('Content-type','text/plain')])
return ['Hello World']

class CaseChanger:
def __init__(self, app, newCase):
self.app = app
self.newCase = newCase

def __call__(self, environ, start_response):
for chunk in self.app(environ, start_response):
if self.newCase == 'lower':
yield chunk.lower()
else: 
yield chunk

Class GZip:
def __init__(self, app):
self.app = app

def __call__(self, environ, start_response):
# Do clever things with headers here (omitted)
for chunk in self.app(environ, start_response):
yeild gzip(chunk)

The way we would write our application at the moment is as follows:

from pkg_resources import require

require('helloworld >= 0.2')
from helloworld import Helloworld

require('wsgiFilters[GZip] == 1.4.3')
from wsgiFilters import GZip

pipeline =  GZip(
app = CaseChanger(
app = HelloWorld(),
newCase = 'lowercase',
)
)

With pipeline itself somehow being executed as a WSGI application.

The new way is like this (correct me if I'm wrong)

The modules have egg_info files like this respectively defining the 
"entry points":

wsgiFilters.egg:

[wsgi.middleware]
gzipper = GZip:GZip

helloworld.egg:

[wsgi.middleware]
cs = helloworld:CaseChanger

[wsgi.app]
myApp = helloworld:HelloWorld


We would then write an "import map" (below) based on the "deployment 
descriptors" in the .eggs used to describe the "entry points" into the 
eggs. The order the "pipeline" would be built is the same as in the 
Python example eg middleware first then application.

[gzipper from wsgiFilters[GZip] == 1.4.3]
[cs from helloworld  >= 0.2 ]
newCase = 'lower'
[myApp from helloworld >= 0.2]


It is loaded using an as yet unwritten modules which uses a factory 
returning a middleware pipeline equivalent to what would be produced in 
the Python example (is this very last bit correct?)

Doing things this new way has the following advantages:
* We have specified explicitly in the setup.py of the eggs that the 
middleware and applications we are importing are actually middleware and 
an application
* It is simpler for a non-technical user.
* There are lots of other applications for the ideas being discussed

It has the following disadvantages:
* We are limited as to what we can use as variable names. Existing 
middleware would need customising to only accept basic parameters.
* We require all WSGI coders to use the egg format.
* Users can't customise the middleware in the configuration file (eg by 
creating a derived class etc and you lose flexibility).
* If we use a Python file we can directly import and manipulate the 
pipeline (I guess you can do this anyway once your factory has returned 
the pipeline)

Both methods are the same in that
* We have specified the order of the pipeline and the middleware and 
applications involved
* Auto-downloading and installation of middleware and applications based 
on version requirements is possible (thanks to PJE's eggs)
* We have specified which versions of modules we require.
* Both could call a script such as wsgi_CGI.py wsgi_mod_python.py etc to 
execute the WSGI pipeline so both method's files could be distributed as 
a single file and would auto download their own dependencies.

Other ideas:

Is it really necessary to be able to give an entry point a name? If not 
because we know what we want to import anyway, we can combine the 
deployment descriptor into the import map:

[GZip:GZip from wsgiFilters[GZip] == 1.4.3]


We can then simplify the deployment descriptor like this:

[wsgi.middleware]
GZip:GZip

And then remove the colons and give a fully qualified Python-style path:

[GZip.GZip from wsgiFilters[GZip] == 1.4.3]

and

[wsgi.middleware]
GZip.GZip

Is this not better? Why do you need to assign names to entry points?

Although writing a middleware chain is dead easy for a Python 
programmer, it isn't for the end user and if you compare the end user 
files from this example I know which one I'd rather explain to someone. 
So although this dep

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

2005-07-25 Thread Phillip J. Eby
At 10:54 PM 7/25/2005 +0100, James Gardner wrote:
>The new way is like this (correct me if I'm wrong)
>
>The modules have egg_info files like this respectively defining the
>"entry points":
>
>wsgiFilters.egg:
>
>[wsgi.middleware]
>gzipper = GZip:GZip

Almost; this one should be:

[wsgi.middleware]
gzipper = GZip:GZip [GZip]

So that using gzipper doesn't require specifying "extras" in the pipeline 
descriptor.  See below.


>helloworld.egg:
>
>[wsgi.middleware]
>cs = helloworld:CaseChanger
>
>[wsgi.app]
>myApp = helloworld:HelloWorld
>
>
>We would then write an "import map" (below) based on the "deployment
>descriptors" in the .eggs used to describe the "entry points" into the
>eggs.

Actually, the new thing you write is the deployment descriptor or pipeline 
descriptor.  The "import map" is the thing you put in the eggs' setup.py to 
list the entry points offered by the eggs.



>The order the "pipeline" would be built is the same as in the
>Python example eg middleware first then application.
>
>[gzipper from wsgiFilters[GZip] == 1.4.3]
>[cs from helloworld  >= 0.2 ]
>newCase = 'lower'
>[myApp from helloworld >= 0.2]

You wouldn't need the [GZip] part if it were declared with the entry point, 
as I showed above.


>It is loaded using an as yet unwritten modules which uses a factory
>returning a middleware pipeline equivalent to what would be produced in
>the Python example (is this very last bit correct?)

Yes.  The order in the file is the order in which the items are invoked by 
the controlling server.


>Doing things this new way has the following advantages:
>* We have specified explicitly in the setup.py of the eggs that the
>middleware and applications we are importing are actually middleware and
>an application
>* It is simpler for a non-technical user.
>* There are lots of other applications for the ideas being discussed
>
>It has the following disadvantages:
>* We are limited as to what we can use as variable names. Existing
>middleware would need customising to only accept basic parameters.

This depends a lot on the details of the .ini-like format, which are still 
up in the air.


>* We require all WSGI coders to use the egg format.

Not so; you can use [GZip.GZip] as a section header in order to do just a 
plain ol' import.


>* Users can't customise the middleware in the configuration file (eg by
>creating a derived class etc and you lose flexibility).

No, but all they have to do is create a Python file and refer to it, and 
they are thereby encouraged to separate code from configuration.  :)


>* If we use a Python file we can directly import and manipulate the
>pipeline (I guess you can do this anyway once your factory has returned
>the pipeline)

Yep.


>Both methods are the same in that
>* We have specified the order of the pipeline and the middleware and
>applications involved
>* Auto-downloading and installation of middleware and applications based
>on version requirements is possible (thanks to PJE's eggs)

One difference here: the .ini format is parseable to determine what eggs 
are needed without executing arbitrary code.


>Other ideas:
>
>Is it really necessary to be able to give an entry point a name?

Yes.  Entry points are a generic setuptools mechanism now, and they have 
names.  However, this doesn't mean they all have to be exported by an egg's 
import map.


>If not
>because we know what we want to import anyway, we can combine the
>deployment descriptor into the import map:
>
>[GZip:GZip from wsgiFilters[GZip] == 1.4.3]

We could perhaps still allow that format; the format is still being 
discussed.  However, this would just be a function of the .wsgi file, and 
doesn't affect the concept of "entry points".  It's just naming a factory 
directly instead of accessing it via an entry point.


>We can then simplify the deployment descriptor like this:
>
>[wsgi.middleware]
>GZip:GZip

If you don't care about the entry point, you can just not declare one.  But 
you can't opt out of naming them.


>Why do you need to assign names to entry points?

Because other things that use entry points need names.  For example, 
setuptools searches a "distutils.commands" entry point group to find 
commands that extend the normal setup commands.  It certainly doesn't know 
in advance what commands the eggs are going to provide.

The question you should be asking is, "Why do we have to use entry points 
to specify factories?", and the answer is, "we don't".  :)


>Although writing a middleware chain is dead easy for a Python
>programmer, it isn't for the end user and if you compare the end user
>files from this example I know which one I'd rather explain to someone.

Yep.  Ian gets the credit for further simplifying my "sequence of 
[factory.name] sections" proposal by coming up with the idea of having 
named entry points declared in an egg.  I then took the entry points idea 
to its logical conclusion, even refactoring setuptools to use them for its 
own extensibility.


>So althoug

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

2005-07-25 Thread Phillip J. Eby
[cc:'d to distutils-sig because this is mostly about cool uses for the new 
EntryPoint facility of setuptools/pkg_resources]

At 12:01 AM 7/26/2005 +0100, James Gardner wrote:
>Hi Phillip,
>
>>There's one other advantage: this format will hopefully become as 
>>successful as WSGI itself in adoption by servers and applications.
>>Hopefully within a year or so, *the* normal way to deploy a Python web 
>>app will be using a .wsgi file.
>>
>>Beyond that, we can hopefully begin to see "Python" rather than 
>>"framework X" as being what people write their web apps with.
>
>Well that would be absolutely wonderful but also looking fairly likely 
>which is great news. I've got to say a massive thank you for the eggs 
>format and easy install as well.. Python was really crying out for it and 
>it will be phenomenally useful. I've split all my code up as a result 
>because there is no need to worry about people having to install lots of 
>packages if it is all done automatically.
>
>One thought: I'd ideally like to be able to backup a WSGI deployment to 
>allow it to be easily redeployed on another server with a different 
>configuration or just restored in the event of data loss. This would 
>probably just involve making a zip file of all data files (including an 
>SQL dump) and then redistributing it with the .wsgi file. Have you had any 
>thoughts on how that could be achieved or is that something you wouldn't 
>want the .wsgi file to be used for? Whatever software installed the 
>dependencies of the .wsgi file would need to be aware of the data file and 
>what to do with it, perhaps simply by calling an install handler 
>somewhere? Umm, all getting a bit complicated but I just wondered if you 
>had had any thoughts of that nature?

Well, you could define another set of entry point groups, like 
"wsgi.middleware.backup_handlers", which would contain entry points with 
the same names as in middleware, that would get called with the same 
configuration arguments as the application factories, but would then do 
some kind of backing up.  Similarly, you could have an entry point group 
for restoration functions.  These would have to defined by the same egg as 
the one with the factory, of course, although perhaps we could make the 
entry point names be the entry point targets instead of using the original 
entry point names.  That additional level of indirection would let one egg 
define backup and restore services for another's factories.  Perhaps the 
backup functions would return the name of a directory tree to back up, and 
the restore functions would receive some kind of zipfile or archive.

Obviously that's a totally unbaked idea that would need a fair amount of 
thought, but there's nothing stopping anybody from fleshing it out as a 
tool and publishing a spec for the entry points it uses.


>Oh sorry, another quick question: Is there any work underway auto-document 
>eggs using some of the docutils code if an appropriate specifier is made 
>in the egg_info file saying the egg is in restructured text or similar? 
>Would that be something you would be willing to include as a feature of 
>easy_install or is it a bit too messy? I'd love to be able to distribute a 
>.wsgi file and have all the documentation for the downloaded modules auto 
>created. If only some of the modules supported it it would still be quite 
>handy.

I'm having a little trouble envisioning what you mean exactly.  All that's 
really coming across is the idea that "there's some way to generate 
documentation from eggs".  I'd certainly like to be able to see tools like 
epydoc or pydoc support generating documentation for an egg.  However, 
there's a fair amount of balkanization in how you specify inputs for Python 
documentation tools, not unlike the previous balkanization of web servers 
and web apps/frameworks.  Maybe somebody will come up with a similar lingua 
franca for documentation tools.

With respect to adding more cool features to setup(), I plan to add a 
couple of entry point groups to setuptools that would support what you have 
in mind, though.  There's already a distutils.commands group that allows 
you to register setup commands, but I also plan to add egg_info.writers and 
distutils.setup_args.  The setup_args entry points would have the name of a 
parameter you'd like setup() to have, and would be a function that would 
get called on the Distribution object during its finalize_options() (so you 
can validate the argument).  The egg_info.writers group will define entry 
points for writing metadata files as part of the egg_info command.

Last, but not least, I'll add a 'setup_requires' argument to setup() that 
will specify eggs that need to be present for the setup script to run.

With these three things in place, tools like the build_utils or py2exe and 
py2app won't have to monkeypatch the distutils in order to install 
themselves; they can instead just define entry points for setup() args and 
the new commands they add.  And