blob: 8ca1882bf9dbc170b2cbdf7d5cf06e09700d05d8 [file] [log] [blame]
Allen Li464220f2017-09-12 17:14:22 -07001# Copyright 2017 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Kludges to support legacy Autotest code.
6
7Autotest imports should be done by calling monkeypatch() first and then
8calling load(). monkeypatch() should only be called once from a
9script's main function.
10"""
11
12from __future__ import absolute_import
13from __future__ import division
14from __future__ import print_function
15
16import imp
17import importlib
18import logging
19import os
20import site
21import sys
22
23import autotest_lib
24
25_AUTOTEST_DIR = autotest_lib.__path__[0]
26_SITEPKG_DIR = os.path.join(_AUTOTEST_DIR, 'site-packages')
27
28_setup_done = False
29
30logger = logging.getLogger(__name__)
31
32
33def monkeypatch():
34 """Do necessary Autotest monkeypatching.
35
Allen Li2678d362017-10-11 16:59:07 -070036 This should be called before any calls to load(). Only the main
37 function in scripts should call this function.
Allen Li464220f2017-09-12 17:14:22 -070038
39 This should be called no more than once.
40
Allen Li2678d362017-10-11 16:59:07 -070041 This adds Autotest's site-packages to the import path and modifies
42 sys.meta_path so that all common.py imports are no-ops.
Allen Li464220f2017-09-12 17:14:22 -070043 """
44 global _setup_done
45 assert not _setup_done
46 site.addsitedir(_SITEPKG_DIR)
47 sys.meta_path.insert(0, _CommonRemovingFinder())
48 _setup_done = True
49
50
51class _CommonRemovingFinder(object):
52 """Python import finder that neuters Autotest's common.py
53
54 The common module is replaced with an empty module everywhere it is
55 imported. common.py should have only been imported for side
56 effects, so nothing should actually use the imported module.
57
58 See also https://www.python.org/dev/peps/pep-0302/
59 """
60
61 def find_module(self, fullname, path=None):
62 """Find module."""
63 del path # unused
64 if not self._is_autotest_common(fullname):
65 return None
66 logger.debug('Dummying out %s import', fullname)
67 return self
68
69 def _is_autotest_common(self, fullname):
70 return (fullname.partition('.')[0] == 'autotest_lib'
71 and fullname.rpartition('.')[-1] == 'common')
72
73 def load_module(self, fullname):
74 """Load module."""
75 if fullname in sys.modules:
76 return sys.modules[fullname]
77 mod = imp.new_module(fullname)
78 mod.__file__ = '<removed>'
79 mod.__loader__ = self
80 mod.__package__ = fullname.rpartition('.')[0]
81 sys.modules[fullname] = mod
82 return mod
83
84
85def load(name):
86 """Import module from autotest.
87
Allen Li2678d362017-10-11 16:59:07 -070088 This enforces that monkeypatch() is called first.
Allen Licc153502017-09-15 16:08:25 -070089
90 @param name: name of module as string, e.g., 'frontend.afe.models'
Allen Li464220f2017-09-12 17:14:22 -070091 """
92 if not _setup_done:
93 raise ImportError('cannot load Autotest modules before monkeypatching')
94 relpath = name.lstrip('.')
95 return importlib.import_module('.%s' % relpath, package='autotest_lib')