Bug 1653094 - Ignore some properties in BaseScript when collecting decorated methods. r=gbrown
authorBob Clary <bclary@bclary.com>
Fri, 31 Jul 2020 18:56:17 +0000
changeset 542945 59448e5759d83b20e84d9df356f4574d88b4fdfc
parent 542944 6037c7a2a7de7d5e2791756ad80d4dcdd6ab51eb
child 542946 159df7ad75ad56d020c36f7b010a958a567b0f61
push id37657
push usernerli@mozilla.com
push dateSat, 01 Aug 2020 09:48:10 +0000
treeherdermozilla-central@750bc4c5c4ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgbrown
bugs1653094
milestone81.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1653094 - Ignore some properties in BaseScript when collecting decorated methods. r=gbrown Differential Revision: https://phabricator.services.mozilla.com/D85581
testing/mozharness/mozharness/base/script.py
--- a/testing/mozharness/mozharness/base/script.py
+++ b/testing/mozharness/mozharness/base/script.py
@@ -1934,20 +1934,22 @@ class BaseScript(ScriptMixin, LogMixin, 
         self._listeners = dict(
             pre_run=[],
             pre_action=[],
             post_action=[],
             post_run=[],
         )
         for k in dir(self):
             try:
-                item = getattr(self, k)
+                item = self._getattr(k)
             except Exception as e:
+                item = None
                 self.warning("BaseScript collecting decorated methods: "
                              "failure to get attribute {}: {}".format(k, str(e)))
+            if not item:
                 continue
 
             # We only decorate methods, so ignore other types.
             if not inspect.ismethod(item):
                 continue
 
             if hasattr(item, '_pre_run_listener'):
                 self._listeners['pre_run'].append(k)
@@ -1960,16 +1962,41 @@ class BaseScript(ScriptMixin, LogMixin, 
             if hasattr(item, '_post_action_listener'):
                 self._listeners['post_action'].append((
                     k,
                     item._post_action_listener))
 
             if hasattr(item, '_post_run_listener'):
                 self._listeners['post_run'].append(k)
 
+    def _getattr(self, name):
+        # `getattr(self, k)` will call the method `k` for any property
+        # access. If the property depends upon a module which has not
+        # been imported at the time the BaseScript initializer is
+        # executed, this property access will result in an
+        # Exception. Until Python 3's `inspect.getattr_static` is
+        # available, the simplest approach is to ignore the specific
+        # properties which are known to cause issues. Currently
+        # adb_path and device are ignored since they require the
+        # availablity of the mozdevice package which is not guaranteed
+        # when BaseScript is called.
+        property_list = set(['adb_path', 'device'])
+        if six.PY2:
+            if name in property_list:
+                item = None
+            else:
+                item = getattr(self, name)
+        else:
+            item = inspect.getattr_static(self, name)
+            if type(item) == property:
+                item = None
+            else:
+                item = getattr(self, name)
+        return item
+
     def _dump_config_hierarchy(self, cfg_files):
         """ interpret each config file used.
 
         This will show which keys/values are being added or overwritten by
         other config files depending on their hierarchy (when they were added).
         """
         # go through each config_file. We will start with the lowest and
         # print its keys/values that are being used in self.config. If any
@@ -2031,17 +2058,17 @@ class BaseScript(ScriptMixin, LogMixin, 
         """After this point, the config is locked and should not be
         manipulated (based on mozharness.base.config.ReadOnlyDict)
         """
         self.config.lock()
 
     def _possibly_run_method(self, method_name, error_if_missing=False):
         """This is here for run().
         """
-        if hasattr(self, method_name) and callable(getattr(self, method_name)):
+        if hasattr(self, method_name) and callable(self._getattr(method_name)):
             return getattr(self, method_name)()
         elif error_if_missing:
             self.error("No such method %s!" % method_name)
 
     def run_action(self, action):
         if action not in self.actions:
             self.action_message("Skipping %s step." % action)
             return