Skip to content

Callable modules

 2 min A CPython experiment put a module body inside a function, made attributes share its globals, and used a call to execute it again.
Published

Mar 14, 2014

This experiment put a function in sys.modules. Importing the name returned that function. Calling it executed the module body in the namespace its attributes exposed.

The construction, after preparing a name, namespace, and compiled module body:

module = eval('lambda: None', m_dict)
module.__dict__ = module.__globals__
module.__name__ = m_name
module.__code__ = m_code
sys.modules[m_name] = module

The first line created a function whose globals were m_dict. The second made its attribute dictionary the same dictionary. A name assigned by the executing body could then be read as an attribute of the imported object.

Replacing __code__ supplied the body. It had been compiled in exec mode from module source, rather than obtained from an ordinary function definition. The function had no closure to reconcile with that code. This was a CPython object-model experiment, not a portable module-loader API.

There were two places for the name, too. The execution namespace contained __name__, while the function object had its own name descriptor. Setting the object’s name did not eliminate the need for the namespace entry used by the module body.

The demonstration imported the registered object first, then called it to perform the actual execution. Inside that body it imported its own call method, code, and dictionary, changed its namespace name, and invoked itself again. The second execution used the same dictionary. This was reexecution, not a clean reset: names that a later body did not replace could remain.

It ended by calling a function defined by the module body and raising an exception from that function. The traceback was part of the demonstration; the imported object still carried executable definitions with real globals.