[Python-ideas] __getattr__ bouncer for modules

Random832 random832 at fastmail.com
Sun Apr 17 00:06:30 EDT 2016


On Sat, Apr 16, 2016, at 23:42, Chris Angelico wrote:
> It's not as clean as actually supporting @property, but it could be
> done without the "bootstrap problem" of trying to have a module
> contain the class that it's to be an instance of. All you have to do
> is define __getattr__ as a regular top-level function, and it'll get
> called. You can then dispatch to property functions if you wish (eg
> """return globals()['_property_'+name]()"""), or just put all the code
> straight into __getattr__.

Or you could have ModuleType.__getattr(ibute?)__ search through some
object other than the module itself for descriptors.

from types import ModuleType, SimpleNamespace
import sys

# __magic__ could simply be a class, but just to prove it doesn't have
to be:

@property
def foo(self):
    return eval(input("get foo?"))

@foo.setter
def foo(self, value):
    print("set foo=" + repr(value))

def __call__(self, v1, v2):
    print("modcall" + repr((v1, v2)))

__magic__ = SimpleNamespace()
for name in 'foo __call__'.split():
    setattr(__magic__, name, globals()[name])
    del globals()[name]

class MagicModule(ModuleType):
    def __getattr__(self, name):
        descriptor = getattr(self.__magic__, name)
        return descriptor.__get__(self, None)

    def __setattr__(self, name, value):
        try:
            descriptor = getattr(self.__magic__, name)
        except AttributeError:
            return super().__setattr__(name, value)
        return descriptor.__set__(self, value)

    def __call__(self, *args, **kwargs):
        # Because why not?
        try:
            call = self.__magic__.__call__
        except AttributeError:
            raise TypeError("Could not access " + self.__name__ +
            "__magic__.__call__ method.")
        return call(self, *args, **kwargs)

sys.modules[__name__].__class__ = MagicModule


More information about the Python-ideas mailing list