tags:

views:

110

answers:

3

What I want to do is something like this:

template.py

def dummy_func():
    print(VAR)
# more functions like this to follow

fabfile.py
# this gets called by fabric (fabfile.org)
# safe to think of it as ant build.xml

import template
template.VAR = 'some_val'
from template import *

Namely I have a template module other modules should 'extend' contributing the required variables. Can this be done in a functional manner (as opposed to object inheritance)?

EDIT: Added a bit more code.

+3  A: 

I'm not sure what you mean by "a functional manner" -- do you mean, as in functional programming? That's not going to happen (since you're intrinsically trying to modify an object, which is the reverse of FP). Or do you mean something like "a way that works"?

For the latter interpretation, the big problem is the import * part -- among the many problems that suggest not using that, you're going to be running smack into one: it performs a snapshot of whatever module-level names are bound at the time (or just those listed in __all__ in the module, if that's defined) -- future changes to the bindings of names will never be reflected in modules that previously did the import *.

Why do you believe you need to merge the template_module's namespace into that of the importing module? If you just did a regular import template_module as tm, then simply referring to all the relevant names as tm.this, tm.that will work just fine (including picking up all changes to the bindings up to the instant of use -- in other words, it uses the "late binding" approach that you appear to require here).

Alex Martelli
@Alex Martelli thanks for the details. I basically want a build script to 'extend' a template providing all the necessary details. Duplicating 'target' calls would be a little bit messy, but seems like it's the way to go.
alex
A: 

If you change a property of a module in one place, it will be the same in other places too. Proof:

Create a file '/tmp/test1.py':

imoprt os
os.path = '' # set os.path (module) to a mere string
os.zzz = 'zzz'

Then

cd /tmp && python

>>> dir(test)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'os', 'z']
>>> test.os
<module 'os' from '/usr/lib/python2.6/os.pyc'>
>>> test.os.path
''
>>> import os
>>> os.path
''
>>> os.zzz
'zzz'

Now os.path is an empty string even in the main application, and zzz is everywhere too.

culebrón
This won't work with immutable objects such as numbers, tuples or strings when using from X import Y though: os.path="foo"; from os import path; os.path="bar"; path --> 'foo'
AKX
Yes, this way path becomes a reference to os.path, and if you change it, you change the **local** "path". It's not that os.path became immutable.
culebrón
A: 

Turns out this has a fabric-centric solution.
So you have an abstract some__fab__template.py and a concrete fabfile.py that should 'extend' the template contributing some required variables (e.g. project name).
I've implemented it utilizing fab's env dictionary.
In template file you reference env.VAR and in the 'concrete' fabfile.py you do this:

from fabric.api import *
env.VAR = 'some value'
import some__fab__template

def dist():
    some__fab__template.dist()
alex