When writing Python code, I often find myself missing a feature from
Node.js: the ability to directly require a module and have
it become callable. In Node.js, you can export a function from a module
and call it immediately:
const messUpThings = require('./mess-up-things');
messUpThings(); // Works!I know some developers aren’t fond of this pattern, but there are legitimate use cases for it.
Consider a scenario where you need to write a helper function with a
self-descriptive name, like mess_up_things. The function
name itself tells you what it does. You have a few options:
Create a util or helper module: Put it in a
util.py or helper.py module. But many
developers dislike these generic names, especially Go developers who
prefer more descriptive package names.
Create a dedicated file: Put it in
mess_up_things.py. But then you need to write verbose
import statements like:
from mess_up_things import mess_up_thingsThis repetition feels unnecessary. Wouldn’t it be nice if you could just do:
import mess_up_things
mess_up_things()If you’re familiar with Python’s magic methods, you might know that
any object with a __call__ method becomes callable. Since
modules are instances of types.ModuleType, and they support
magic methods like __getattr__, couldn’t we just define a
__call__ method directly on a module?
Unfortunately, Python doesn’t support this out of the box. There was
actually PEP 713 that
proposed adding module-level __call__ support, but it was
rejected.
However, if you’re familiar with Python’s runtime, you’ll find there’s a trick to achieve this behavior:
import sys
class MyModule(sys.modules[__name__].__class__):
def __call__(self):
print("Messing up things...")
sys.modules[__name__].__class__ = MyModuleThis works by dynamically replacing the module’s
__class__ at runtime. But who wants to copy-paste this
boilerplate every time?
To avoid repeating this boilerplate, I’ve encapsulated this simple mechanism into a package called Cadule (short for Callable [Mo]dule Less).
pip install caduleCreate a file called mess_up_things.py:
import cadule
@cadule
def __call__():
print("Messing up things...")Then in your Python REPL or another script:
>>> import mess_up_things
>>> mess_up_things()
Messing up things...
>>> callable(mess_up_things)
TrueThat’s it! The entire mess_up_things module is now a
callable object. When you call it, it executes the decorated
__call__ function.
You can also pass arguments and return values:
import cadule
@cadule
def __call__(target):
return f"Messing up {target}!">>> import mess_up_things
>>> mess_up_things("the database")
'Messing up the database!'Cadule is particularly useful for:
Github: https://github.com/aisk/cadule