pylib: add mozhg.util.import_module() and use it to detect mercurial.configitems (bug 1454296) r?gps draft
authorbyron jones <glob@mozilla.com>
Tue, 01 May 2018 12:30:01 +0800
changeset 12242 82e67fc44a719a2b019aae4ad717a8cc86b31392
parent 12241 83ddcb66f8a5d5ce1b69c070408aba674bba2907
child 12243 9d798d6e06c29cfe6d87b6a6c7605d062518a431
push id1919
push userbjones@mozilla.com
push dateWed, 02 May 2018 05:27:15 +0000
reviewersgps
bugs1454296
pylib: add mozhg.util.import_module() and use it to detect mercurial.configitems (bug 1454296) r?gps MozReview-Commit-ID: F3XBpAFVJ5r
hgext/configwizard/__init__.py
hgext/firefoxreleases/__init__.py
hgext/firefoxtree/__init__.py
hgext/hgmo/__init__.py
hgext/mozext/__init__.py
hgext/mqext/__init__.py
hgext/obsolescencehacks/__init__.py
hgext/overlay/__init__.py
hgext/pushlog/__init__.py
hgext/reviewboard/client.py
hgext/reviewboard/server.py
hgext/robustcheckout/__init__.py
hghooks/mozhghooks/extension.py
pylib/mozhg/mozhg/auth.py
pylib/mozhg/mozhg/util.py
pylib/vcsreplicator/vcsreplicator/hgext.py
--- a/hgext/configwizard/__init__.py
+++ b/hgext/configwizard/__init__.py
@@ -25,16 +25,17 @@ from mercurial.commands import (
     pull as hgpull,
     update as hgupdate,
 )
 
 OUR_DIR = os.path.dirname(__file__)
 execfile(os.path.join(OUR_DIR, '..', 'bootstrap.py'))
 
 from configobj import ConfigObj
+from mozhg.util import import_module
 
 
 HOST_FINGERPRINTS = {
     'bitbucket.org': '3f:d3:c5:17:23:3c:cd:f5:2d:17:76:06:93:7e:ee:97:42:21:14:aa',
     'bugzilla.mozilla.org': '7c:7a:c4:6c:91:3b:6b:89:cf:f2:8c:13:b8:02:c4:25:bd:1e:25:17',
     'hg.mozilla.org': '73:7f:ef:ab:68:0f:49:3f:88:91:f0:b7:06:69:fd:8f:f2:55:c9:56',
 }
 
@@ -346,34 +347,27 @@ testedwith = '3.9 4.0 4.1 4.2 4.3 4.4'
 buglink = 'https://bugzilla.mozilla.org/enter_bug.cgi?product=Developer%20Services&component=Mercurial%3A%20configwizard'
 
 cmdtable = {}
 
 # We want the extension to load on ancient versions of Mercurial.
 # cmdutil.command was introduced in 1.9.
 # registrar.command was introduced in 4.3 as a replacement for cmdutil.command.
 # The registrar module itself was introduced around Mercurial 4.0.
+registrar = import_module('mercurial.registrar')
 try:
-    from mercurial import registrar
     command = registrar.command(cmdtable)
-except (ImportError, AttributeError):
+except AttributeError:
     try:
         command = cmdutil.command(cmdtable)
     except AttributeError:
         command = None
 
-try:
-    from mercurial import registrar
-except ImportError:
-    registrar = None
-
-try:
-    from mercurial import configitems
-except ImportError:
-    configitems = None
+# TRACKING hg43
+configitems = import_module('mercurial.configitems')
 
 if registrar and util.safehasattr(registrar, 'configitem'):
     configtable = {}
     configitem = registrar.configitem(configtable)
 
     # TODO some of these are registered elsewhere. This can produce a warning
     # for duplicate registration. We should ideally call a shared function
     # that only registers once.
--- a/hgext/firefoxreleases/__init__.py
+++ b/hgext/firefoxreleases/__init__.py
@@ -14,31 +14,29 @@ from mercurial import (
     revset,
     util,
 )
 from mercurial.hgweb import (
     webcommands,
     webutil,
 )
 
-# TRACKING hg43
-try:
-    from mercurial import configitems
-except ImportError:
-    configitems = None
-
 OUR_DIR = os.path.normpath(os.path.dirname(__file__))
 execfile(os.path.join(OUR_DIR, '..', 'bootstrap.py'))
 
 import mozautomation.releasedb as releasedb
 
 from mozhg.util import (
+    import_module,
     is_firefox_repo,
 )
 
+# TRACKING hg43
+configitems = import_module('mercurial.configitems')
+
 minimumhgversion = '4.1'
 testedwith = '4.1 4.2 4.3 4.4'
 
 if util.safehasattr(registrar, 'configitem'):
     configtable = {}
     configitem  = registrar.configitem(configtable)
 
     configitem('mozilla', 'enablefirefoxreleases',
--- a/hgext/firefoxtree/__init__.py
+++ b/hgext/firefoxtree/__init__.py
@@ -96,43 +96,36 @@ from mercurial.error import RepoError
 from mercurial.i18n import _
 from mercurial.node import (
     bin,
     hex,
     nullrev,
     short,
 )
 
-with demandimport.deactivated():
-    # TRACKING hg43
-    try:
-        from mercurial import configitems
-    except ImportError:
-        configitems = None
-
-    # TRACKING hg46
-    try:
-        from mercurial import logcmdutil
-    except ImportError:
-        logcmdutil = None
-
-    try:
-        from mercurial import wireprotov1server as wireproto
-    except ImportError:
-        from mercurial import wireproto
-
 OUR_DIR = os.path.dirname(__file__)
 execfile(os.path.join(OUR_DIR, '..', 'bootstrap.py'))
 
 from mozautomation.repository import (
     MULTI_TREE_ALIASES,
     resolve_trees_to_uris,
     resolve_uri_to_tree,
     TRY_TREES,
 )
+from mozhg.util import import_module
+
+# TRACKING hg43
+configitems = import_module('mercurial.configitems')
+
+# TRACKING hg46
+logcmdutil = import_module('mercurial.logcmdutil')
+
+wireproto = import_module('mercurial.wireprotov1server')
+if not wireproto:
+    wireproto = import_module('mercurial.wireproto')
 
 testedwith = '4.2 4.3 4.4 4.5'
 minimumhgversion = '4.2'
 buglink = 'https://bugzilla.mozilla.org/enter_bug.cgi?product=Developer%20Services&component=Mercurial%3A%20firefoxtree'
 # The root revisions in mozilla-central and comm-central, respectively.
 MOZ_ROOT_REV = '8ba995b74e18334ab3707f27e9eb8f4e37ba3d29'
 COMM_ROOT_REV = 'e4f4569d451a5e0d12a6aa33ebd916f979dd8faa'
 
--- a/hgext/hgmo/__init__.py
+++ b/hgext/hgmo/__init__.py
@@ -104,28 +104,25 @@ from mercurial.hgweb.common import (
     ErrorResponse,
     HTTP_OK,
     HTTP_NOT_FOUND,
 )
 from mercurial.hgweb.protocol import (
     webproto,
 )
 
-# TRACKING hg43
-try:
-    from mercurial import configitems
-except ImportError:
-    configitems = None
-
 OUR_DIR = os.path.dirname(__file__)
 ROOT = os.path.normpath(os.path.join(OUR_DIR, '..', '..'))
 execfile(os.path.join(OUR_DIR, '..', 'bootstrap.py'))
 
 import mozautomation.commitparser as commitparser
+from mozhg.util import import_module
 
+# TRACKING hg43
+configitems = import_module('mercurial.configitems')
 
 minimumhgversion = '4.1'
 testedwith = '4.1 4.2 4.3 4.4'
 
 cmdtable = {}
 
 # TRACKING hg43 Mercurial 4.3 introduced registrar.command as a replacement for
 # cmdutil.command.
--- a/hgext/mozext/__init__.py
+++ b/hgext/mozext/__init__.py
@@ -302,26 +302,23 @@ from mercurial import (
     scmutil,
     sshpeer,
     templatefilters,
     templatekw,
     templater,
     util,
 )
 
-# TRACKING hg43
-try:
-    from mercurial import configitems
-except ImportError:
-    configitems = None
-
-
 OUR_DIR = os.path.normpath(os.path.dirname(__file__))
 execfile(os.path.join(OUR_DIR, '..', 'bootstrap.py'))
 
+from mozhg.util import import_module
+
+# TRACKING hg43
+configitems = import_module('mercurial.configitems')
 
 # Disable demand importing for mozautomation because "requests" doesn't
 # play nice with the demand importer.
 with demandimport.deactivated():
     from mozautomation.changetracker import (
         ChangeTracker,
     )
 
--- a/hgext/mqext/__init__.py
+++ b/hgext/mqext/__init__.py
@@ -101,30 +101,27 @@ from mercurial import (
     patch,
     pathutil,
     registrar,
     scmutil,
     url,
     util,
 )
 
-# TRACKING hg43
-try:
-    from mercurial import configitems
-except ImportError:
-    configitems = None
-
 from hgext import mq
 from collections import Counter
 
 OUR_DIR = os.path.dirname(__file__)
 execfile(os.path.join(OUR_DIR, '..', 'bootstrap.py'))
 
 from mozautomation.commitparser import BUG_RE
+from mozhg.util import import_module
 
+# TRACKING hg43
+configitems = import_module('mercurial.configitems')
 
 # TRACKING hg43 Mercurial 4.3 introduced the config registrar. 4.4
 # requires config items to be registered to avoid a devel warning.
 if util.safehasattr(registrar, 'configitem'):
     configtable = {}
     configitem = registrar.configitem(configtable)
 
     # TRACKING hg44 generic argument added in 4.4.
--- a/hgext/obsolescencehacks/__init__.py
+++ b/hgext/obsolescencehacks/__init__.py
@@ -8,22 +8,23 @@ import pwd
 
 from mercurial import (
     error,
     obsolete,
     registrar,
     util,
 )
 
+OUR_DIR = os.path.normpath(os.path.dirname(__file__))
+execfile(os.path.join(OUR_DIR, '..', 'bootstrap.py'))
+
+from mozhg.util import import_module
+
 # TRACKING hg43
-try:
-    from mercurial import configitems
-except ImportError:
-    configitems = None
-
+configitems = import_module('mercurial.configitems')
 
 testedwith = '4.1 4.2 4.3 4.4'
 minimumhgversion = '4.1'
 
 
 # TRACKING hg43 Mercurial 4.3 introduced the config registrar. 4.4 requires
 # config items to be registered to avoid a devel warning.
 if util.safehasattr(registrar, 'configitems'):
--- a/hgext/overlay/__init__.py
+++ b/hgext/overlay/__init__.py
@@ -24,21 +24,23 @@ from mercurial import (
     filelog,
     hg,
     registrar,
     scmutil,
     store,
     util,
 )
 
+OUR_DIR = os.path.normpath(os.path.dirname(__file__))
+execfile(os.path.join(OUR_DIR, '..', 'bootstrap.py'))
+
+from mozhg.util import import_module
+
 # TRACKING hg43
-try:
-    from mercurial import configitems
-except ImportError:
-    configitems = None
+configitems = import_module('mercurial.configitems')
 
 testedwith = '4.1 4.2 4.3 4.4 4.5'
 
 cmdtable = {}
 
 # Mercurial 4.3 introduced registrar.command as a replacement for
 # cmdutil.command.
 if util.safehasattr(registrar, 'command'):
--- a/hgext/pushlog/__init__.py
+++ b/hgext/pushlog/__init__.py
@@ -25,21 +25,23 @@ from mercurial import (
     templatekw,
     util,
     wireproto,
 )
 from mercurial.hgweb import (
     webutil,
 )
 
+OUR_DIR = os.path.normpath(os.path.dirname(__file__))
+execfile(os.path.join(OUR_DIR, '..', 'bootstrap.py'))
+
+from mozhg.util import import_module
+
 # TRACKING hg43
-try:
-    from mercurial import configitems
-except ImportError:
-    configitems = None
+configitems = import_module('mercurial.configitems')
 
 Abort = error.Abort
 RepoLookupError = error.RepoLookupError
 
 minimumhgversion = '4.1'
 testedwith = '4.1 4.2 4.3 4.4'
 buglink = 'https://bugzilla.mozilla.org/enter_bug.cgi?product=Developer%20Services&component=Mercurial%3A%20Pushlog'
 
--- a/hgext/reviewboard/client.py
+++ b/hgext/reviewboard/client.py
@@ -45,22 +45,16 @@ from mercurial import (
     phases,
     registrar,
     scmutil,
     sshpeer,
     templatekw,
     util,
 )
 
-# TRACKING hg43
-try:
-    from mercurial import configitems
-except ImportError:
-    configitems = None
-
 from mercurial.i18n import _
 from mercurial.node import bin, hex
 
 OUR_DIR = os.path.normpath(os.path.dirname(__file__))
 execfile(os.path.join(OUR_DIR, '..', 'bootstrap.py'))
 
 with demandimport.deactivated():
     try:
@@ -84,16 +78,20 @@ from mozhg.auth import (
     getbugzillaauth,
     configureautobmoapikeyauth,
     register_config_items,
 )
 from mozhg.rewrite import (
     newparents,
     replacechangesets,
 )
+from mozhg.util import import_module
+
+# TRACKING hg43
+configitems = import_module('mercurial.configitems')
 
 testedwith = '4.1 4.2 4.3 4.4'
 minimumhgversion = '4.1'
 buglink = 'https://bugzilla.mozilla.org/enter_bug.cgi?product=MozReview&component=Integration%3A%20Mercurial'
 
 cmdtable = {}
 
 # Mercurial 4.3 introduced registrar.command as a replacement for
--- a/hgext/reviewboard/server.py
+++ b/hgext/reviewboard/server.py
@@ -28,37 +28,36 @@ from mercurial import (
     demandimport,
     extensions,
     hg,
     registrar,
     util,
     wireproto,
 )
 
-# TRACKING hg43
-try:
-    from mercurial import configitems
-except ImportError:
-    configitems = None
-
 from mercurial.i18n import _
 from mercurial.node import (
     hex,
     nullid,
 )
 from mercurial.hgweb import (
     webcommands,
 )
 from mercurial.hgweb.common import (
     HTTP_OK,
 )
 
 OUR_DIR = os.path.normpath(os.path.dirname(__file__))
 execfile(os.path.join(OUR_DIR, '..', 'bootstrap.py'))
 
+from mozhg.util import import_module
+
+# TRACKING hg43
+configitems = import_module('mercurial.configitems')
+
 with demandimport.deactivated():
     try:
         import hgrb.proto
     except ImportError:
         sys.path.insert(0, OUR_DIR)
         import hgrb.proto
 
 testedwith = '4.1 4.2 4.3 4.4'
--- a/hgext/robustcheckout/__init__.py
+++ b/hgext/robustcheckout/__init__.py
@@ -33,21 +33,23 @@ from mercurial import (
     cmdutil,
     hg,
     match as matchmod,
     registrar,
     scmutil,
     util,
 )
 
+OUR_DIR = os.path.normpath(os.path.dirname(__file__))
+execfile(os.path.join(OUR_DIR, '..', 'bootstrap.py'))
+
+from mozhg.util import import_module
+
 # TRACKING hg43
-try:
-    from mercurial import configitems
-except ImportError:
-    configitems = None
+configitems = import_module('mercurial.configitems')
 
 testedwith = '3.7 3.8 3.9 4.0 4.1 4.2 4.3 4.4 4.5'
 minimumhgversion = '3.7'
 
 cmdtable = {}
 
 # TRACKING hg43 Mercurial 4.3 introduced registrar.command as a replacement for
 # cmdutil.command.
--- a/hghooks/mozhghooks/extension.py
+++ b/hghooks/mozhghooks/extension.py
@@ -4,26 +4,24 @@
 """Mercurial extension to run hooks on repositories."""
 
 from __future__ import absolute_import
 
 from mercurial import (
     registrar,
     util,
 )
-
-try:
-    from mercurial import configitems
-except ImportError:
-    configitems = None
-
 from mozhg.util import (
     identify_repo,
+    import_module,
 )
 
+# TRACKING hg43
+configitems = import_module('mercurial.configitems')
+
 testedwith = '4.2 4.3 4.4'
 minimumhgversion = '4.2'
 buglink = 'https://bugzilla.mozilla.org/enter_bug.cgi?product=Developer%20Services&component=Mercurial%3A%20hg.mozilla.org'
 
 # TRACKING hg43 Mercurial 4.3 introduced the config registrar. 4.4
 # requires config items to be registered to avoid a devel warning.
 if util.safehasattr(registrar, 'configitem'):
     configtable = {}
--- a/pylib/mozhg/mozhg/auth.py
+++ b/pylib/mozhg/mozhg/auth.py
@@ -7,23 +7,26 @@ import os
 import platform
 import shutil
 import tempfile
 import urlparse
 
 from mercurial import config, util
 from mercurial.i18n import _
 
+from util import import_module
+
 
 def register_config_items(configitem):
     """Registers config items with Mercurial's registrar.
 
     The argument is a ``registrar.configitem`` instance.
     """
-    from mercurial import configitems
+    # TRACKING hg43
+    configitems = import_module('mercurial.configitems')
 
     configitem('bugzilla', 'username',
                default=configitems.dynamicdefault)
     configitem('bugzilla', 'apikey',
                default=configitems.dynamicdefault)
     configitem('bugzilla', 'password',
                default=configitems.dynamicdefault)
     configitem('bugzilla', 'userid',
--- a/pylib/mozhg/mozhg/util.py
+++ b/pylib/mozhg/mozhg/util.py
@@ -1,23 +1,41 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
 from __future__ import absolute_import
+import importlib
 
 from mercurial import (
     error,
 )
 
+
+def import_module(name):
+    """Mercurial's demandimport makes checking for a module's existence tricky.
+
+    As demandimport lazyloads even invalid modules, you cannot use a normal
+    `import` catching ImportErrors. Performing the import inside a
+    `demandimport.disabled()` block will work, however this will also disable
+    lazyloading of dependent modules.
+
+    The correct method is to load with demandimport enabled then query an
+    attribute of the method."""
+    try:
+        module = importlib.import_module(name)
+        # __name__ will throw if the module cannot be imported; wrapped in an
+        # `if` to avoid "useless statement" warnings.
+        if module.__name__:
+            return module
+    except ImportError:
+        return None
+
+
 # TRACKING hg43
-try:
-    from mercurial import configitems
-except ImportError:
-    configitems = None
-
+configitems = import_module('mercurial.configitems')
 
 FIREFOX_ROOT_NODE = '8ba995b74e18334ab3707f27e9eb8f4e37ba3d29'
 THUNDERBIRD_ROOT_NODE = 'e4f4569d451a5e0d12a6aa33ebd916f979dd8faa'
 
 
 def is_firefox_repo(repo):
     """Determine if a repository is a Firefox repository."""
     try:
@@ -92,8 +110,9 @@ def identify_repo(repo):
     # We could potentially exclude more Firefox repos from this list. For now,
     # be liberal in what we apply this label to.
     d['firefox_releasing'] = (
         d['firefox']
         and d['publishing']
         and not d['user_repo'])
 
     return d
+
--- a/pylib/vcsreplicator/vcsreplicator/hgext.py
+++ b/pylib/vcsreplicator/vcsreplicator/hgext.py
@@ -29,28 +29,30 @@ from mercurial import (
     hg,
     obsolete,
     policy,
     registrar,
     util,
     wireproto,
 )
 
+OUR_DIR = os.path.normpath(os.path.dirname(__file__))
+execfile(os.path.join(OUR_DIR, '..', 'bootstrap.py'))
+
+from mozhg.util import import_module
+
+# TRACKING hg43
+configitems = import_module('mercurial.configitems')
+
 # TRACKING hg43 4.3 imports variable modules with policy.importmod().
 if util.safehasattr(policy, 'importmod'):
     base85 = policy.importmod('base85')
 else:
     from mercurial import base85
 
-# TRACKING hg43
-try:
-    from mercurial import configitems
-except ImportError:
-    configitems = None
-
 testedwith = '4.1 4.2'
 
 cmdtable = {}
 
 # Mercurial 4.3 introduced registrar.command as a replacement for
 # cmdutil.command.
 if util.safehasattr(registrar, 'command'):
     command = registrar.command(cmdtable)