[Web-SIG] PasteDeploy 0.1

Phillip J. Eby pje at telecommunity.com
Tue Aug 23 16:42:30 CEST 2005

At 11:30 PM 8/22/2005 -0500, Ian Bicking wrote:
>Phillip J. Eby wrote:
>>At 09:03 PM 8/22/2005 -0500, Ian Bicking wrote:
>>>However, in paste.deploy there does remain real global configuration, so 
>>>you wouldn't have to manually copy in values from the globals.
>>>While admittedly it makes the interface slightly less elegant from the 
>>>Python side, I think it's an important feature.
>>That's easily emulated if you need it; just create a configuration 
>>service or services that can be acquired via the parent_component 
>>links.  Actually, the format I propose allows numerous other ways to 
>>emulate that feature on varying scales, but doesn't force all factories 
>>to understand any one specific configuration protocol.
>It's important to me, and it's not intuitive to me what you envision. So I 
>feel a need to services in action, replacing global configuration.

Using a config service in a factory to get a default argument value:

     def some_app_factory(parent_component, **args):
         config = parent_component.get_service("global_config")
         args.setdefault('someparam', config['someparam'])

Registering a config service (old syntax):

     [globalconfigservice from SomeEgg]
     someparam = "foo"

     [...next component in the stack...]

The config service would respond to 'get_service("global_config")' by 
returning self.

The idea is that when you chain non-wrapper components in a pipeline, each 
one gets the previous component as its "parent component", so you can 
"acquire" services from your parents.  Components nearer to you (i.e. more 
local) can override more global service definitions.

>>I'm thinking that the loader gets passed some arguments to determine what 
>>entry point group to use.  This format, by the way, only requires one 
>>group for all the "normal" entry points, because the "wrapper" keyword 
>>distinguishes between the two factory signatures -- which are the only 
>>signatures you get.
>In paste.deploy the syntax (filter:, etc) and the group are redundant. So 
>if you accidentally treat an application like a filter it'll be caught 
>before you call the object with the wrong parameters.

Well, that could certainly be done with this approach too.

>>>>* "wrapper" means that the factory will be called with two positional 
>>>>arguments; non-wrappers are called with one argument.  Named wrappers 
>>>>can be passed a positional argument if used in an another factory 
>>>>argument expression - this will be the object they should wrap.
>>>This part is unclear to me.
>>See the urlmap in the example, where "/blog" = auth(blog()).  'auth' is a 
>>"wrapper", so it can be called with something to wrap (e.g. 'blog()').
>But the wrapper there is called with one argument, and the app with zero; 
>but you say the wrapper has two and the app one...?

Named sections have a default parent_component argument, so that you don't 
have to explicitly pass them in.

># or you could do the whole thing like:
>filebrowser = pipeline:
>     auth wrapper from Paste:
>         require_role = "admin"
>         admin_email = config.admin_email
>     filebrowser from FileBrowser:
>         document_root = static.document_root
># maybe you could specialize/clone like:
>auth2 = auth:
>     require_role = "editor"

Interesting.  If we used "in" to include other files then you could refer 
to e.g.:

     foo = main in "some.ini":
         # override params

Also, I was thinking that in this syntax, you want to be able to leave off 
the trailing ':' for simple definitions, so that this would be a complete 
definition, without needing a body:

     foo = main in "some.ini"

Finally, I think we could drop the "pipeline" keyword and simply use a ':' 
to define a name, which then gives us a way to stack components inside a 
definition, e.g.:

         login wrapper from Paste:
             # blah
         urlmap from Paste:
             "/":     static
             "/blog": main in "blog.ini"
                  auth wrapper from Paste:
                      require_role = "admin"
                  filebrowser from FileBrowser:
                      document_root = static.document_root

The idea here is that if you want to pass a component or components to a 
factory, you have to use ':' syntax, with either a one-line component 
specifier on the same line, or a multi-component stack as an indented suite.

An interesting question is whether you should be able to refer to nested 
definitions as factory prototypes (ala your auth2/auth) or whether only 
top-level names should be usable.  For example in this:

     foo: bar from baz

         foo: snickety from lemon
         scuzz: foo
             thingy: foo

Does "scuzz: foo" refer to the inner foo or the outer foo?  What about 
"thingy: foo"?

I'm inclined to say that both refer to the spam: foo rather than the 
outermost "foo".  i.e., more or less the same rules as Python scopes.

One minor problem with this syntax overall, though, is that it's a bit 
context-dependent.  Whether "foo:" means "define foo" or "create a foo" is 
just a matter of alternating layers.  It would be better if the syntax were 
less ambiguous, e.g.:

     main :=
         login wrapper from Paste:
             # blah
         urlmap from Paste:
             "/"     := static
             "/blog" := main in "blog.ini"
             "/cms"  :=
                  auth wrapper from Paste:
                      require_role = "admin"
                  filebrowser from FileBrowser:
                      document_root = static.document_root

But that doesn't actually seem to help visually, and makes it harder to 
write because you have to remember all the time whether you need ":" or 
":=".  Maybe this would be better:

     main is:
         login wrapper from Paste:
             # blah
         urlmap from Paste:
             match_mode = "longest"
             "/" is static
             "/blog" is main in "blog.ini"
             "/cms" is:
                  auth wrapper from Paste:
                      require_role = "admin"
                  filebrowser from FileBrowser:
                      document_root = static.document_root

What's nice about this is that now you can unambiguously create a top-level 
object in the simple case:

     zapp from Zope:
         cfg_file = "site.zcml"

Without needing to do:

     main is:
         zapp from Zope:
             cfg_file = "site.zcml"

Which is a pain, IMO.  Although I suppose we could allow:

     main is zapp from Zope:
         cfg_file = "site.zcml"

which isn't too bad.

>>>* "name from egg_spec" reads nice on one level, but is vague on another 
>>>level.  Even if "egg:egg_spec#name" doesn't read well, I think it is 
>>>nicely self-describing.
>>Um, wha???  The only difference between the two is that one of them has 
>>"egg:" in front of it, which seems a bit redundant to me.  That's 
>>probably because I assume that in the long run eggs will be so ubiquitous 
>>that it really will be redundant to explicitly refer to them as such.  :)
>In paste.deploy config files are full peers to Eggs, and can be used 
>anywhere that an egg: reference can be used.  I think that's a neat 
>feature.  I don't want to tack on referencing other config files, like a 
>special loader factory or textual inclusion hacks or anything like that.
>Config files describe applications.  Egg entry points describe 
>applications.  They should be peers.

Okay.  The "in" syntax I gave above allows that, although I could also go 
for only using "from", as long as config URLs are quoted strings.  I also 
think the strings should be relative or absolute URLs, rather than 
filenames.  (So that '/' has the same meaning on all platforms.)  That will 
be something of a pain for Windows users who may need to include drive 
letters, but oh well.  We can always treat the letters A-Z as a special 
"file:" protocol to fix that.  :)

>>Conversely, if I assume that some further description is required, I 
>>would want to say "pypi:" or "project:" or something else of that sort, 
>>because "egg" isn't the essential nature of the thing; the name is a 
>>*project* name, while eggs are an implementation detail.
>egg: is an access method, just like http: or whatever.  It doesn't say 
>what the URI describes, just how to find it.

Ah, but that's just it.  The project name is a URN, not a URL, precisely 
because it *doesn't* describe how to locate the resource, it just names the 
resource and tells the system to go find it.

More information about the Web-SIG mailing list