# HG changeset patch # User Gregory Szorc # Date 1549068449 28800 # Node ID 3e89736b98ce6bb2b9e29215a5e4c84350862eb6 # Parent dffd6a301570c80a99d2275129c4bb79a2d8cc8d py3: conditionalize test-demandimport.py for Python 3 The Python 3 lazy importer uses the LazyLoader that is part of importlib. On Python 3 and later, LazyLoader is implemented using a custom module type that defines a __getattribute__ which triggers module loading. Furthermore, there are additional differences as well. For example, it appears that Python 3 will return an existing sys.modules entry instead of constructing a new module object. This commit adds additional test coverage for lazy importing behavior to cover the differences between Python 2 and 3. This reveals that the test and some lazy import functionality is kinda busted. For example, the test assumes "contextlib" will be lazy. But in reality an import before it has already imported contextlib! There's definitely room to improve the behavior of the demand importer code, both for Python 2 and 3. But at least the test passes on Python 3 now. Differential Revision: https://phab.mercurial-scm.org/D5796 diff -r dffd6a301570 -r 3e89736b98ce tests/test-demandimport.py --- a/tests/test-demandimport.py Fri Feb 01 12:09:05 2019 -0800 +++ b/tests/test-demandimport.py Fri Feb 01 16:47:29 2019 -0800 @@ -6,6 +6,10 @@ import os import subprocess import sys +import types + +# Don't import pycompat because it has too many side-effects. +ispy3 = sys.version_info[0] >= 3 # Only run if demandimport is allowed if subprocess.call(['python', '%s/hghave' % os.environ['TESTDIR'], @@ -16,6 +20,16 @@ if sys.flags.optimize: sys.exit(80) +if ispy3: + from importlib.util import _LazyModule + + try: + from importlib.util import _Module as moduletype + except ImportError: + moduletype = types.ModuleType +else: + moduletype = types.ModuleType + if os.name != 'nt': try: import distutils.msvc9compiler @@ -43,15 +57,26 @@ # We use assert instead of a unittest test case because having imports inside # functions changes behavior of the demand importer. -assert f(node) == "", f(node) +if ispy3: + assert not isinstance(node, _LazyModule) +else: + assert f(node) == "", f(node) # now enable it for real del os.environ['HGDEMANDIMPORT'] demandimport.enable() # Test access to special attributes through demandmod proxy +assert 'mercurial.error' not in sys.modules from mercurial import error as errorproxy -assert f(errorproxy) == "", f(errorproxy) + +if ispy3: + # unsure why this isn't lazy. + assert not isinstance(f, _LazyModule) + assert f(errorproxy) == "", f(errorproxy) +else: + assert f(errorproxy) == "", f(errorproxy) + doc = ' '.join(errorproxy.__doc__.split()[:3]) assert doc == 'Mercurial exceptions. This', doc assert errorproxy.__name__ == 'mercurial.error', errorproxy.__name__ @@ -61,50 +86,122 @@ name = errorproxy.__dict__['__name__'] assert name == 'mercurial.error', name -assert f(errorproxy) == "", f(errorproxy) +if ispy3: + assert not isinstance(errorproxy, _LazyModule) + assert f(errorproxy) == "", f(errorproxy) +else: + assert f(errorproxy) == "", f(errorproxy) import os -assert f(os) == "", f(os) +if ispy3: + assert not isinstance(os, _LazyModule) + assert f(os) == "", f(os) +else: + assert f(os) == "", f(os) + assert f(os.system) == '', f(os.system) assert f(os) == "", f(os) +assert 'mercurial.utils.procutil' not in sys.modules from mercurial.utils import procutil -assert f(procutil) == "", f(procutil) +if ispy3: + assert isinstance(procutil, _LazyModule) + assert f(procutil) == "", f( + procutil + ) +else: + assert f(procutil) == "", f(procutil) + assert f(procutil.system) == '', f(procutil.system) +assert procutil.__class__ == moduletype, procutil.__class__ assert f(procutil) == "", f( procutil ) assert f(procutil.system) == '', f(procutil.system) +assert 'mercurial.hgweb' not in sys.modules from mercurial import hgweb -assert f(hgweb) == "", f(hgweb) -assert f(hgweb.hgweb_mod) == "", f(hgweb.hgweb_mod) + +if ispy3: + assert not isinstance(hgweb, _LazyModule) + assert f(hgweb) == "", f(hgweb) + assert isinstance(hgweb.hgweb_mod, _LazyModule) + assert ( + f(hgweb.hgweb_mod) == "" + ), f(hgweb.hgweb_mod) +else: + assert f(hgweb) == "", f(hgweb) + assert f(hgweb.hgweb_mod) == "", f( + hgweb.hgweb_mod + ) + assert f(hgweb) == "", f(hgweb) import re as fred -assert f(fred) == "", f(fred) + +if ispy3: + assert not isinstance(fred, _LazyModule) + assert f(fred) == "" +else: + assert f(fred) == "", f(fred) import re as remod -assert f(remod) == "", f(remod) + +if ispy3: + assert not isinstance(remod, _LazyModule) + assert f(remod) == "" +else: + assert f(remod) == "", f(remod) import sys as re -assert f(re) == "", f(re) + +if ispy3: + assert not isinstance(re, _LazyModule) + assert f(re) == "" +else: + assert f(re) == "", f(re) -assert f(fred) == "", f(fred) +if ispy3: + assert not isinstance(fred, _LazyModule) + assert f(fred) == "", f(fred) +else: + assert f(fred) == "", f(fred) + assert f(fred.sub) == '', f(fred.sub) -assert f(fred) == "", f(fred) + +if ispy3: + assert not isinstance(fred, _LazyModule) + assert f(fred) == "", f(fred) +else: + assert f(fred) == "", f(fred) remod.escape # use remod assert f(remod) == "", f(remod) -assert f(re) == "", f(re) -assert f(re.stderr) == "', mode 'w' at 0x?>", f(re.stderr) -assert f(re) == "", f(re) +if ispy3: + assert not isinstance(re, _LazyModule) + assert f(re) == "" + assert f(type(re.stderr)) == "", f( + type(re.stderr) + ) + assert f(re) == "" +else: + assert f(re) == "", f(re) + assert f(re.stderr) == "', mode 'w' at 0x?>", f( + re.stderr + ) + assert f(re) == "", f(re) import contextlib -assert f(contextlib) == "", f(contextlib) + +if ispy3: + assert not isinstance(contextlib, _LazyModule) + assert f(contextlib) == "" +else: + assert f(contextlib) == "", f(contextlib) + try: from contextlib import unknownattr @@ -113,7 +210,9 @@ 'module:\ncontextlib.unknownattr = %s' % f(unknownattr) ) except ImportError as inst: - assert rsub(r"'", '', str(inst)) == 'cannot import name unknownattr' + assert rsub(r"'", '', str(inst)).startswith( + 'cannot import name unknownattr' + ) from mercurial import util