From 53e083f9539559f23cc4605c4739201207d0de5d Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 19 Oct 2021 19:09:39 -0700 Subject: [PATCH 1/2] bpo-33277: Hack to proxy module attributes through their spec --- Lib/importlib/_bootstrap.py | 57 +++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 889f08f8aeec1f1..73ff9a450379fb3 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -490,7 +490,62 @@ def _spec_from_module(module, loader=None, origin=None): return spec +ProxyModule = None + +def _setup_proxy(): + global ProxyModule + class ProxyModule(type(sys)): + def __repr__(self): + return f'Verbose {self.__name__}' + + def __setattr__(self, name, value): + if (spec := object.__getattribute__(self, '__spec__')) is None: + object.__setattr__(self, name, value) + return + match name: + case '__name__': + spec.name = value + case '__package__': + # This isn't right because module.__package__ and + # module.__spec__.parent have subtly different sematics. + # The latter is read-only while the former is writable. + # I'm not sure if that's intended or how to resolve it. + # Maybe we should relax the constraint on .parent? + spec.__dict__['parent'] = value + case '__loader__': + spec.loader = value + case '__file__': + spec.origin = value + case '__path__': + spec.submodule_search_location = value + case '__cached__': + spec.cached = value + case _: + object.__setattr__(self, name, value) + + def __getattribute__(self, name): + if (spec := object.__getattribute__(self, '__spec__')) is None: + return object.__getattribute__(self, name) + match name: + case '__name__': + return spec.name + case '__package__': + return spec.parent + case '__loader__': + return spec.loader + case '__file__': + return spec.origin + case '__path__': + return spec.submodule_search_location + case '__cached__': + return spec.cached + case _: + return object.__getattribute__(self, name) + + def _init_module_attrs(spec, module, *, override=False): + if ProxyModule is None: + _setup_proxy() # The passed-in module may be not support attribute assignment, # in which case we simply don't set the attributes. # __name__ @@ -560,6 +615,8 @@ def _init_module_attrs(spec, module, *, override=False): module.__cached__ = spec.cached except AttributeError: pass + if module.__class__ is type(sys): + module.__class__ = ProxyModule return module From 031fbd2c24aa5b4a9714144d0f40226d657896f2 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 19 Oct 2021 19:14:42 -0700 Subject: [PATCH 2/2] We don't need the __repr__ --- Lib/importlib/_bootstrap.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 73ff9a450379fb3..dc8c6dfda1458d0 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -495,9 +495,6 @@ def _spec_from_module(module, loader=None, origin=None): def _setup_proxy(): global ProxyModule class ProxyModule(type(sys)): - def __repr__(self): - return f'Verbose {self.__name__}' - def __setattr__(self, name, value): if (spec := object.__getattribute__(self, '__spec__')) is None: object.__setattr__(self, name, value)