Callable modules?

Bengt Richter bokr at oz.net
Wed Jul 24 19:44:20 EDT 2002


On Tue, 23 Jul 2002 10:47:10 +0200, holger krekel <pyth at devel.trillke.net> wrote:

>Paul Rubin wrote:
>> martin at v.loewis.de (Martin v. Loewis) writes:
>> > You can put a callable object in sys.modules. Module objects
>> > themselves are not callable.
>> 
>> Is this documented somewhere?  I don't see it in the obvious places.
>> 
>> > > If there's not a way to do this already, maybe it's a reasonable
>> > > addition.
>> > 
>> > Maybe not. Why do you need this?
>> 
>> Just to not have to say "foo.foo()" or "from foo import foo".
>> 
>> If the main purpose of the module is to provide one function, I think
>> it's cleaner to be able to import the module and call the function
>> without special tricks.
Or just use your own trick, e.g., given

----< PR.py >------
#PR.py for Paul Rubin
def PR(): return 'Hi Paul'
-------------------

Define one-line trick:
 >>> def PRimport(name, locs=locals()): locs[name] = getattr(__import__(name),name)
 ...

And use it thus
 >>> PRimport('PR')
 >>> PR
 <function PR at 0x007D8FD0>
 >>> PR()
 'Hi Paul'

>
>Moreover, I often thought about integrating class and module types.
>I am not primarly concerned about making modules callable, although 
>i might appreciate it.
>
>My use case is 
>
>    from somemodule.someclass import filter1
>
>where 'filter1' would reference an unbound class-method.
>I need this for implementing a class which offers filter
>functions which can be applied either as
>
>        obj.filter1(args)
>or
>        filter1(obj, args)
>
>Of course, you can express the latter via
>
>        someclass.filter1(obj, args)
>
>but i want to have filter1 (and 41 other filter methods) 
>potentially directly in the namespace.  I *know* that i can write
>
>    filter1=someclass.filter1
>    filter2=someclass.filter2
>    ...
>
>but it's redundant and you have to repeat this scheme whereever
>you use 'somemodule'.  That's ugly where in fact i just want to say
>
>    from somemodule.filters import filter1,filter2, filter37
>
>and someclass could inherit from 'filters', thus providing both 
>invocation methods.  I know that i can *hack* this to work by
>
>    - doing new.module('somemodule.filters') in somemodule 
>      and adding the modules functions at runtime to someclass.
>
>    - making up a package (with filters beeing in a different file)
>      and adding the modules functions at runtime to someclass
>
>    - some variant of this schema
>
>but actually, doing the above 'from ....someclass import filter1,...' 
>seems much more expressive and doesn't involve any run-time hacks.
>
>I hope i made my point clear enough.  Basically i'd like to 
>generalize the namespace-injection methods 'import' and 'from'.
>This might involves extending the Module-type to work
>more like a "usual" class with __getattr__ and such. Probabally
>it only involves extending the import machinery.
>
>Unfortunately, i haven't found time to investigate the matter 
>but maybe someone can point out that it's the path to 
>all evil (read: wrong concept), anyway :-) 
>
Is this what you want to do (just for grins I allowed a simple pattern
to specify filter names). I

(Warning: Not tested beyond what you see below ;-)

you can do this, given the files noted below
(ForHolger is a class in forHolger.py):

 >>> from importForHolger import importForHolger as impH
 >>> dir()
 ['__builtins__', '__doc__', '__name__', 'impH']
 >>> impH('forHolger.ForHolger',globals(), locals(),['*Holg*'])
 >>> dir()
 ['ForHolger', '__builtins__', '__doc__', '__name__', 'filterHolger1', 'filterHolger2', 'im
 pH', 'methodForHolger']
 >>> fh=ForHolger()
 >>> fh.filterHolger1()
 'filtered by Holger1: KREKEL'
 >>> fh.filterHolger2()
 'filtered by Holger2: krekel'
 >>> filterHolger1(fh)
 'filtered by Holger1: KREKEL'
 >>> filterHolger2(fh)
 'filtered by Holger2: krekel'
 >>> methodForHolger(fh,'an arg','another')
 methodForHolger called with:
 self=<forHolger.ForHolger instance at 0x007D0FE0>
 args=('an arg', 'another')
 >>> fh.methodForHolger('an arg','another')
 methodForHolger called with:
 self=<forHolger.ForHolger instance at 0x007D0FE0>
 args=('an arg', 'another')
 >>> fh.filter1()
 'KREKEL'
 >>> fh.filter2()
 'krekel'
 >>> fh.someOtherMethod()
 'OM: Krekel'
 >>> impH('math',globals(), locals(),['pi','c*','*n'])
 >>> for k,v in locals().items(): print '%15s = %s' % (k,`v`)
 ...
            asin = <built-in function asin>
             cos = <built-in function cos>
   filterHolger1 = <function filterHolger1 at 0x007D02E0>
   filterHolger2 = <function filterHolger2 at 0x007D02A0>
    __builtins__ = <module '__builtin__' (built-in)>
       ForHolger = <class forHolger.ForHolger at 0x007D0150>
            ceil = <built-in function ceil>
            atan = <built-in function atan>
            cosh = <built-in function cosh>
 methodForHolger = <function methodForHolger at 0x007D03A0>
             tan = <built-in function tan>
              fh = <forHolger.ForHolger instance at 0x007D0FE0>
        __name__ = '__main__'
            impH = <function importForHolger at 0x007CE1C0>
              pi = 3.1415926535897931
             sin = <built-in function sin>
         __doc__ = None
            math = <module 'math' (built-in)>

--< forHolger.py >-------------
#forHolger.py

class ForHolger:
    def __init__(self, v='Krekel'): self.v = v
    def methodForHolger(self, *args):
         print 'methodForHolger called with:\nself=%s\nargs=%s'%(`self`,`args`)
    def filter1(self): return self.v.upper()
    def filter2(self): return self.v.lower()
    def filterHolger1(self): return 'filtered by Holger1: %s' % self.filter1()
    def filterHolger2(self): return 'filtered by Holger2: %s' % self.filter2()
    def someOtherMethod(self): return 'OM: %s' % self.v

-------------------------------


--< importForHolger.py >-------
# importForHolger.py

def importForHolger(name, globs, locs, thinglist=None):
    """Walks dotted name and "imports" attributes matching thinglist patterns"""
    names = name.split('.')
    entity = __import__(names[0], globs, locs)
    for ename in names[1:]:
        entity = getattr(entity, ename)    # walk to last entity
    locs[names[-1]] = entity        # assume we want the entity as well as selected things
    for thingpatt in thinglist:
        # grab things in entity matching thingslist
        # allow for thingpatt or *thingpatt or thing*patt or thingpatt* or *thingpatt*
        tp = thingpatt.split('*')
        if len(tp)==1:
            matchtest = lambda x: x==tp[0]
        elif len(tp)==3:  # *thingpatt*
            matchtest = lambda x: x.find(tp[1])!=-1
        elif tp[0]:
            if tp[1]:
                matchtest = lambda x: x.startswith(tp[0]) and x.endswith(tp[1])
            else:
                matchtest = lambda x: x.startswith(tp[0])
        else:
             matchtest = lambda x: x.endswith(tp[1])
        for k,v in entity.__dict__.items():
            if matchtest(k): locs[k]=v
-------------------------------


It seems to work inside a function too ( 2.2 on windows):

 >>> def foo():
 ...     from importForHolger import importForHolger as impH
 ...     impH('math',globals(), locals(),['pi','c*','*n'])
 ...     return locals()
 ...
 >>> d=foo()
 >>> for k,v in d.items(): print '%15s = %s' % (k,`v`)
 ...
            asin = <built-in function asin>
             cos = <built-in function cos>
            atan = <built-in function atan>
             sin = <built-in function sin>
            impH = <function importForHolger at 0x007D82C0>
              pi = 3.1415926535897931
            cosh = <built-in function cosh>
             tan = <built-in function tan>
            math = <module 'math' (built-in)>
            ceil = <built-in function ceil>

That doesn't do what I wanted to do, though. I wanted to make a module with
an attribute the works like a real property. So while we're on the subject (? ;-)
of "integrating class and module types" ...

given

----< testmod.py >-----------
#testmod.py
import time

def get_p(self): return self._p
def set_p(self, v): self._p=v
p = property(get_p,set_p)
_p = 'class variable default for p property'

asctime = time.asctime
def get_now(self): return self.asctime()
now = property(get_now)
-----------------------------

and
----< myImport.py >----------
# myImport.py   
def myImport(name, globs=globals(), locs=locals(), *args, **kw):
    import sys
    class MyModule(object):
        execfile(name+'.py') 
    tmp = MyModule()
    tmp.__dict__.update(kw)
    if args: tmp.import_args = args
    tmp.__name__ = name
    sys.modules[name] = tmp
    locs[name] = tmp
    return tmp
-----------------------------

this seems possible:

 >>> execfile('myImport.py') # so locs get bound to interactive locs
 >>> myImport('testmod')
 <__main__.MyModule object at 0x007D00D0>
 >>> testmod.now
 'Wed Jul 24 16:50:12 2002'
 >>> testmod.p
 'class variable default for p property'
 >>> testmod.p='instance value'
 >>> testmod.p
 'instance value'
 >>> del testmod._p
 >>> testmod.p
 'class variable default for p property'

Only tested as far as you see above ;-)

Regards,
Bengt Richter



More information about the Python-list mailing list