Problem
Unable to dynamically create classes with local scope within a function but works at the top level.
Question
How to create classes dynamically at the correct namespace scope.
I'm trying to dynamically create classes that are added to the callers namespace.
So that I'm able to do the following.
import creator
creator.make('SomeClass')
print "SomeClass :", SomeClass
This can be made to work when adding dynamic classes at the top level but when trying to do the same within a function, so that the created class has only local scope, it doesn't work.
I saw that there is hack which works when placed inside the function but that's going to disappear.
The following code shows the problem obviously there would be more to creator.py than that shown.
# creator.py
import inspect
def make(classname):
t = type(classname, (object,), { })
frame = inspect.currentframe()
print "WILL make() - callers locals :", frame.f_back.f_locals.keys()
frame.f_back.f_locals[classname] = t
print "DID make() - callers locals :", frame.f_back.f_locals.keys()
return t
# example.py
import creator
print "WILL __main__ - locals :", locals().keys()
creator.make('SomeClass')
print "DID __main__ - locals :", locals().keys()
print "SomeClass :", SomeClass
print "-" * 80
def closed():
# This hack helps
# https://stackoverflow.com/a/1549221/1481060
# exec ''
class ClosedClass: pass
print "WILL closed() - locals :", locals().keys()
creator.make('AnotherClass')
print "DID closed() - locals :", locals().keys()
# NOT EXPECTED
try: print AnotherClass
except NameError, ex: print "OUCH:", ex
closed()
Output:
WILL __main__ - locals : ['creator', '__builtins__', '__file__', '__package__', '__name__', '__doc__']
WILL make() - callers locals : ['creator', '__builtins__', '__file__', '__package__', '__name__', '__doc__']
DID make() - callers locals : ['creator', '__builtins__', 'SomeClass', '__file__', '__package__', '__name__', '__doc__']
DID __main__ - locals : ['creator', '__builtins__', 'SomeClass', '__file__', '__package__', '__name__', '__doc__']
SomeClass : <class 'creator.SomeClass'>
--------------------------------------------------------------------------------
WILL closed() - locals : ['ClosedClass']
WILL make() - callers locals : ['ClosedClass']
DID make() - callers locals : ['ClosedClass', 'AnotherClass']
DID closed() - locals : ['ClosedClass', 'AnotherClass']
OUCH: global name 'AnotherClass' is not defined
Of course I don't want OUCH:...
Looking at locals() within closed one would imagine it should work but as mentioned in the hack the Python compiler is optimising the locals and missing the introduction of a new dynamic one.
If I uncomment the exec '' line then it works.
I could create theses classes with global scope but I'm trying to keep my namespaces clean.
I could of course do AnotherClass = creator.make('AnotherClass') but I'm trying to keep it DRY.
Is there a way of making it work without the exec '' hack ?
Or is it a case of making them global?