views:

74

answers:

3

I want to use a function from another module as a decorator, but I need it to manipulate the current module's global namespace.

For example, I want to be able to go from this:

class SomeClass:
    pass

root = SomeClass

to this:

from othermodule import decorator

@decorator
class Someclass:
    pass

Any ideas?

A: 

That already works:

from othermodule import decorator

@decorator
class Someclass:
    pass

Just put in othermodule.py:

def decorator(cls):
    #.... do something with cls
    return cls
nosklo
Yeah I know it works in general, but it's the fact that I want a decorator from the imported module to define a variable in the original module's global scope that causes trouble.
Doug W
A: 

This is a bit hacky, but try this in othermodule.py:

import sys
def decorator(cls):
    mod = __import__(cls.__module__)
    mod.root = cls
Duncan
Yeah that worked, thanks. Actually I ended up using mod = sys.modules[cls.__module__] because the __import__ call only returned the top level module (cls.__module__) was 4 levels deep - ie mod1.mod2.mod3.mod4
Doug W
Incidentally, `mod = __import__(cls.__module__, {}, {}, ('*', ))` would return the mod4 module, not the outermost mod1 package
Marius Gedminas
This is a direct answer (although error-prone for packages and such), but very scary code. Also, it's sort of silly to use `setattr` with string literals. `setattr(foo, 'bar', baz)` is spelled `foo.bar = baz`.
Mike Graham
Yes, the setattr was excessive. I've removed it. The code isn't all that scary: __module__ is a documented attribute of classes. Whether the OP should actually be doing it at all is of course another question.
Duncan
+3  A: 

Having a decorator modify the global namespace if any module, let alone another module, is bad and never necessary. Code that mutates far-away globals is difficult to read and maintain. You should definitely consider modifying your design to avoid mutable global state and especially implicit assignment.

Mike Graham
+1 for best practices.
jathanism