Patchwork [4,of,4,RFC] demandimport: stop overriding __getattribute__()

login
register
mail settings
Submitter Yuya Nishihara
Date May 24, 2017, 3:38 p.m.
Message ID <307c520a7be0f3899806.1495640317@mimosa>
Download mbox | patch
Permalink /patch/20883/
State Accepted
Headers show

Comments

Yuya Nishihara - May 24, 2017, 3:38 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1493305282 -32400
#      Fri Apr 28 00:01:22 2017 +0900
# Node ID 307c520a7be0f3899806cc6995d0789819fa7242
# Parent  c96f6277a21b5a37f4cc8e4f37b89f79aa77e423
demandimport: stop overriding __getattribute__()

Proxy __dict__ and __doc__ explicitly instead.

I'm not sure which is less evil, but this seems slightly simpler than hooking
all attribute accesses.
Augie Fackler - May 24, 2017, 8:28 p.m.
On Thu, May 25, 2017 at 12:38:37AM +0900, Yuya Nishihara wrote:
> # HG changeset patch
> # User Yuya Nishihara <yuya@tcha.org>
> # Date 1493305282 -32400
> #      Fri Apr 28 00:01:22 2017 +0900
> # Node ID 307c520a7be0f3899806cc6995d0789819fa7242
> # Parent  c96f6277a21b5a37f4cc8e4f37b89f79aa77e423
> demandimport: stop overriding __getattribute__()

queued, thanks

>
> Proxy __dict__ and __doc__ explicitly instead.
>
> I'm not sure which is less evil, but this seems slightly simpler than hooking
> all attribute accesses.

I agree this is a wee bit saner.

Patch

diff --git a/hgdemandimport/demandimportpy2.py b/hgdemandimport/demandimportpy2.py
--- a/hgdemandimport/demandimportpy2.py
+++ b/hgdemandimport/demandimportpy2.py
@@ -153,9 +153,7 @@  class _demandmod(object):
     def __call__(self, *args, **kwargs):
         raise TypeError("%s object is not callable" % repr(self))
 
-    def __getattribute__(self, attr):
-        if attr in ('_data', '_extend', '_load', '_module', '_addref'):
-            return object.__getattribute__(self, attr)
+    def __getattr__(self, attr):
         self._load()
         return getattr(self._module, attr)
 
@@ -163,6 +161,16 @@  class _demandmod(object):
         self._load()
         setattr(self._module, attr, val)
 
+    @property
+    def __dict__(self):
+        self._load()
+        return self._module.__dict__
+
+    @property
+    def __doc__(self):
+        self._load()
+        return self._module.__doc__
+
 _pypy = '__pypy__' in sys.builtin_module_names
 
 def _demandimport(name, globals=None, locals=None, fromlist=None, level=level):
diff --git a/tests/test-demandimport.py b/tests/test-demandimport.py
--- a/tests/test-demandimport.py
+++ b/tests/test-demandimport.py
@@ -69,6 +69,17 @@  print("re =", f(re))
 print("re.stderr =", f(re.stderr))
 print("re =", f(re))
 
+# Test access to special attributes through demandmod proxy
+from mercurial import pvec as pvecproxy
+print("pvecproxy =", f(pvecproxy))
+print("pvecproxy.__doc__ = %r"
+      % (' '.join(pvecproxy.__doc__.split()[:3]) + ' ...'))
+print("pvecproxy.__name__ = %r" % pvecproxy.__name__)
+# __name__ must be accessible via __dict__ so the relative imports can be
+# resolved
+print("pvecproxy.__dict__['__name__'] = %r" % pvecproxy.__dict__['__name__'])
+print("pvecproxy =", f(pvecproxy))
+
 import contextlib
 print("contextlib =", f(contextlib))
 try:
diff --git a/tests/test-demandimport.py.out b/tests/test-demandimport.py.out
--- a/tests/test-demandimport.py.out
+++ b/tests/test-demandimport.py.out
@@ -18,6 +18,11 @@  remod = <module 're' from '?'>
 re = <unloaded module 'sys'>
 re.stderr = <open file '<whatever>', mode 'w' at 0x?>
 re = <proxied module 'sys'>
+pvecproxy = <unloaded module 'pvec'>
+pvecproxy.__doc__ = 'A "pvec" is ...'
+pvecproxy.__name__ = 'mercurial.pvec'
+pvecproxy.__dict__['__name__'] = 'mercurial.pvec'
+pvecproxy = <proxied module 'pvec'>
 contextlib = <unloaded module 'contextlib'>
 contextlib.unknownattr = ImportError: cannot import name unknownattr
 __import__('contextlib', ..., ['unknownattr']) = <module 'contextlib' from '?'>