Issue91

Title Pyre: binding components to facilities using .cfg files
Priority feature Status chatting
Superseder Nosy List anonymous, baagaard, leif
Assigned To leif Topics Pyre

Created on 2007-03-02.00:20:22 by anonymous, last changed 2007-03-22.21:03:38 by leif.

Files
File name Uploaded Type Edit
001.tar.gz leif, 2007-03-02.00:40:01 application/x-gzip
hello.tar.gz leif, 2007-03-02.10:12:40 application/x-gzip
unnamed leif, 2007-03-02.00:40:01 text/html
Messages
msg297 (view) Author: leif Date: 2007-03-02.10:29:33
BTW, in "pyre.odb.hello", 'hello' is the name of the component containing the 
facility being bound.  For top-level facilities, it is the name of the 
application; e.g., "pyre.odb.pylithapp".  It will also include the vault name 
if given, e.g., "pyre.odb.journal.devices".

(Something tells me this isn't quite right, or quite enough.  The vault stuff 
always confuses me.  My head hurts...)
msg296 (view) Author: leif Date: 2007-03-02.10:12:40
Added support for "odb" info in egg metadata:

r6160

I've attached an egg-ized version of the Pythia 'hello' example to illustrate 
how to use this feature.  Simply add special 'entry_points' in the arguments to 
setup() in setup.py:

    entry_points = {
        "console_scripts": [ "hello = hello.components:main" ],

        "pyre.odb.hello": [
            "bob = hello.greeters:english",
            "pierre = hello.greeters:french",
        ],

    },

To run the example:

$ ./egg/bin/hello --greeter=bob
Good morning Michael Aivazis!
$ ./egg/bin/hello --greeter=pierre
Bonjour Michael Aivazis!
$ 

This illustrates multiple factories in the same Python module.  Also note that 
the family name and the factory name do not have to be the same, so one can 
easily create aliases to the same factory:

           "bob = hello.greeters:english",
           "sam = hello.greeters:english",
msg295 (view) Author: baagaard Date: 2007-03-02.01:31:29
Based on what you have said, the cleanest implementation seems to involve:
(1) Each component should be defined in its own file and include a factory
function named appropriately.
(2) Use the family argument in pyre.inventory.facility() to name the factory
method which should be used to bind the component.
(3) Change the binding of components to facilities using only the filename for
the component. Because the factory methods are in the Component files, this can
be done from the command line or cfg files (with Leif's patch in the CIG Pythia).
msg294 (view) Author: leif Date: 2007-03-02.01:15:01
In CIG-Pyre, Pyre Bug LCS001 has been fixed. This means one can write a 
full dotted module name when binding a facility. Using the 001 example:

$ ./foo.py
bar is holmer
zoo is Holmer
$ ./foo.py --bar=Bart.Skateboard
bar is skateboard
zoo is skateboard
$

However, I didn't stop there. :-)

In CIG Pyre, one can have an arbitrary number of factories inside a 
single module. You access them by specifying another dotted name, 
separated from the module name by a colon:

    --bar=Bart.Skateboard:xxx.yyy.zzz

Pyre still automatically appends the "family" (facility) name, so the 
above example really means the following:

    from Bart.Skateboard import xxx
    factory = xxx.yy.zzz.simpsons
    factory()

If you want to suppress the addition of the family name, append an extra 
dot:

    --bar=Bart.Skateboard:myfactory.

This means:

    from Bart.Skateboard import myfactory
    myfactory()

This works for class names as well. So, to answer the original question 
in the context of a .cfg file:

    # use class X in file Y for facility Z
    Z = Y.X.

Or, using the 001 example:

$ cat myscript.cfg

[myscript]
bar = Bart.Skateboard:Skateboard.

$

~~~~

Having said all this, I really consider the above a hack for advanced 
users. In practice, the Pyre application writer wants to associate 
simple names with various components. Littering the filesystem with 
little .odb files seem ridiculous for this small task... a simple 
dictionary would suffice. My current plan is to add support for "egg 
metadata" containing the name->component mapping. Pyre will 
automatically read the metadata at startup. This means the application 
author adds a few lines to "setup.py" describing the name mapping, and 
when the package is installed somewhere on sys.path, it will "Just Work".
msg293 (view) Author: leif Date: 2007-03-02.00:40:01
This is closely related to Pyre Bug LCS001, the first Pyre bug I 
reported to Michael Aivazis back in December of 2005. I've included my 
original e-mail below -- and attached the test case  001.tar.gz -- for 
reference.

Summary: in CACR-Pyre, "facility=xxx" works, but "facility=yyy.xxx" doesn't.

-------- Original Message --------
Subject: 	Pyre Bug LCS001: "facility=package.xxx" does not work
Date: 	Wed, 07 Dec 2005 11:56:59 -0800
From: 	Leif Strand <leif@geodynamics.org>
To: 	Michael Aivazis <aivazis@caltech.edu>

Description
--------------------

If one has a facility:

bar = facility("bar", family="simpsons", factory=Holmer)

one can write

./foo.py --bar=Lisa

if there is a module "Lisa.py" which implements 'simpsons':

def simpsons():
    return Lisa()

Additionally, one can write

./foo.py --bar=Marge

if there is a package called 'Bart' which likewise implements 'simpsons' 
in its __init__.py.

However, nested packages/modules do not work: one can not incorporate 
dots into the component reference. E.g.,

./foo.py --bar=Bart.Skateboard
./foo.py --bar=Bart.Slingshot

does not work even if there is a module Bart/Skateboard.py or a module 
Bart/Slingshot/__init__.py which implements 'simpsons'. Pyre mistakenly 
looks for the factory in the top-level 'Bart' package:

[leif@convection 001]$ ./test2.sh
 >> /home/leif/opt/pythia-0.8/lib/python2.3/site-packages/pyre/inventory/Facility.py:131:_import
 >> pyre.inventory(error)
 -- no factory for facility 'bar' in '/home/leif/bugs/pyre/001/Bart/__init__.py'
zoo is Holmer
 >> /home/leif/opt/pythia-0.8/lib/python2.3/site-packages/pyre/inventory/Facility.py:131:_import
 >> pyre.inventory(error)
 -- no factory for facility 'bar' in '/home/leif/bugs/pyre/001/Bart/__init__.pyc'
zoo is Holmer
[leif@convection 001]$

Test Case
--------------------

001.tar.gz [Attached]

Proposed Fix
--------------------
Alter the first few lines of pyre.inventory.Facility._import() so that 
it reads as follows:

    def _import(self, name):
        try:
            module = __import__(name, {}, {})
            components = name.split('.')
            for comp in components[1:]:
                module = getattr(module, comp)
        except ImportError:
            #...

This code is snippet is taken directly from the Python documentation 
(section 2.1, "Built-in Functions" 
<http://www.python.org/doc/2.3.5/lib/built-in-funcs.html>). Run 
'patch.sh' in the enclosed test case to see it in action.

See also section 1.7.4 of the Python programming FAQ 
<http://www.python.org/doc/faq/programming.html#import-x-y-z-returns-module-x-how-do-i-get-z>.

---Leif
msg292 (view) Author: baagaard Date: 2007-03-02.00:30:10
I created this issue but forgot to login.
msg291 (view) Author: anonymous Date: 2007-03-02.00:20:21
If I want to change the component bound to a Pyre facility, am I limited to 
using an odb file with cfg files. In other words, is there a nifty way to just 
say "use class X in file Y for facility Z"?

I find it a pain to create an odb file when all I want to do is change the 
component bound to a facility. I realize that it is possible to bind a 
component to a facility if you put the appropriate factory method in the class 
definition file (essentially using the the class definition file as the odb 
file), but, in general, components don't know the names of the facilities they 
can be bound to (they know the types but not the names).
History
Date User Action Args
2007-03-22 21:03:38leifsettopic: + Pyre
nosy: anonymous, leif, baagaard
2007-03-02 10:29:33leifsetnosy: anonymous, leif, baagaard
messages: + msg297
2007-03-02 10:12:42leifsetfiles: + hello.tar.gz
nosy: anonymous, leif, baagaard
messages: + msg296
2007-03-02 01:31:29baagaardsetnosy: anonymous, leif, baagaard
messages: + msg295
2007-03-02 01:15:01leifsetnosy: anonymous, leif, baagaard
messages: + msg294
2007-03-02 00:40:01leifsetfiles: + unnamed, 001.tar.gz
nosy: anonymous, leif, baagaard
messages: + msg293
2007-03-02 00:30:10baagaardsetnosy: + baagaard
messages: + msg292
2007-03-02 00:20:22anonymouscreate