blob: bd232f631211d5d1ff1962affd133a341077b316 [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
Allen Li75f88082017-11-15 15:24:33 -08007Autotest imports should be done by calling monkeypatch() first and then
8calling load(). monkeypatch() should only be called once from a
9script's main function.
Allen Li0459da32017-11-10 12:11:42 -080010
11chromite imports should be done with chromite_load(), and any third
12party packages should be imported with deps_load(). The reason for this
13is to present a clear API for these unsafe imports, making it easier to
14identify which imports are currently unsafe. Eventually, everything
15should be moved to virtualenv, but that will not be in the near future.
Allen Li464220f2017-09-12 17:14:22 -070016"""
17
18from __future__ import absolute_import
19from __future__ import division
20from __future__ import print_function
21
Allen Li75f88082017-11-15 15:24:33 -080022import contextlib
Allen Li464220f2017-09-12 17:14:22 -070023import imp
24import importlib
25import logging
26import os
27import site
28import sys
29
30import autotest_lib
31
32_AUTOTEST_DIR = autotest_lib.__path__[0]
33_SITEPKG_DIR = os.path.join(_AUTOTEST_DIR, 'site-packages')
34
35_setup_done = False
36
37logger = logging.getLogger(__name__)
38
39
Allen Li75f88082017-11-15 15:24:33 -080040def monkeypatch():
41 """Monkeypatch everything needed to import Autotest.
Allen Libca5d012017-10-20 13:44:36 -070042
43 This should be called before any calls to load(). Only the main
44 function in scripts should call this function.
Allen Libca5d012017-10-20 13:44:36 -070045 """
Allen Li75f88082017-11-15 15:24:33 -080046 with _global_setup():
47 _monkeypatch_body()
Allen Libca5d012017-10-20 13:44:36 -070048
Allen Lif4b62ae2017-11-09 15:48:05 -080049
Allen Li75f88082017-11-15 15:24:33 -080050@contextlib.contextmanager
51def _global_setup():
52 """Context manager for checking and setting global _setup_done variable."""
53 global _setup_done
54 assert not _setup_done
55 try:
56 yield
57 except Exception: # pragma: no cover
58 # We cannot recover from this since we leave the interpreter in
59 # an unknown state.
60 logger.exception('Uncaught exception escaped Autotest setup')
61 sys.exit(1)
62 else:
63 _setup_done = True
Allen Libca5d012017-10-20 13:44:36 -070064
Allen Li75f88082017-11-15 15:24:33 -080065
66def _monkeypatch_body():
67 """The body of monkeypatch() running within _global_setup() context."""
68 # Add Autotest's site-packages.
69 site.addsitedir(_SITEPKG_DIR)
70
71 # Dummy out common imports as they may cause problems.
72 sys.meta_path.insert(0, _CommonRemovingFinder())
73
74 # Add chromite's third-party to the import path (chromite does this
75 # on import).
76 importlib.import_module('chromite')
77
78 # Set up Django environment variables.
79 importlib.import_module('autotest_lib.frontend.setup_django_environment')
80
81 # Make Django app paths absolute.
82 settings = importlib.import_module('autotest_lib.frontend.settings')
Allen Libca5d012017-10-20 13:44:36 -070083 settings.INSTALLED_APPS = (
84 'autotest_lib.frontend.afe',
85 'autotest_lib.frontend.tko',
86 'django.contrib.admin',
87 'django.contrib.auth',
88 'django.contrib.contenttypes',
89 'django.contrib.sessions',
90 'django.contrib.sites',
91 )
92
Allen Li75f88082017-11-15 15:24:33 -080093 # drone_utility uses this.
94 common = importlib.import_module('autotest_lib.scheduler.common')
Allen Lif4b62ae2017-11-09 15:48:05 -080095 common.autotest_dir = _AUTOTEST_DIR
96
Allen Libca5d012017-10-20 13:44:36 -070097
Allen Li464220f2017-09-12 17:14:22 -070098class _CommonRemovingFinder(object):
99 """Python import finder that neuters Autotest's common.py
100
101 The common module is replaced with an empty module everywhere it is
102 imported. common.py should have only been imported for side
103 effects, so nothing should actually use the imported module.
104
105 See also https://www.python.org/dev/peps/pep-0302/
106 """
107
108 def find_module(self, fullname, path=None):
109 """Find module."""
110 del path # unused
111 if not self._is_autotest_common(fullname):
112 return None
113 logger.debug('Dummying out %s import', fullname)
114 return self
115
116 def _is_autotest_common(self, fullname):
117 return (fullname.partition('.')[0] == 'autotest_lib'
118 and fullname.rpartition('.')[-1] == 'common')
119
120 def load_module(self, fullname):
121 """Load module."""
Allen Libca5d012017-10-20 13:44:36 -0700122 if fullname in sys.modules: # pragma: no cover
Allen Li464220f2017-09-12 17:14:22 -0700123 return sys.modules[fullname]
124 mod = imp.new_module(fullname)
125 mod.__file__ = '<removed>'
126 mod.__loader__ = self
127 mod.__package__ = fullname.rpartition('.')[0]
128 sys.modules[fullname] = mod
129 return mod
130
131
132def load(name):
133 """Import module from autotest.
134
Allen Li2678d362017-10-11 16:59:07 -0700135 This enforces that monkeypatch() is called first.
Allen Licc153502017-09-15 16:08:25 -0700136
137 @param name: name of module as string, e.g., 'frontend.afe.models'
Allen Li464220f2017-09-12 17:14:22 -0700138 """
Allen Li0459da32017-11-10 12:11:42 -0800139 return _load('autotest_lib.%s' % name)
140
141
142def chromite_load(name):
143 """Import module from chromite.lib.
144
145 This enforces that monkeypatch() is called first.
146
147 @param name: name of module as string, e.g., 'metrics'
148 """
149 return _load('chromite.lib.%s' % name)
150
151
152def deps_load(name):
153 """Import module from chromite.lib.
154
155 This enforces that monkeypatch() is called first.
156
157 @param name: name of module as string, e.g., 'metrics'
158 """
159 assert not name.startswith('autotest_lib')
160 assert not name.startswith('chromite.lib')
161 return _load(name)
162
163
164def _load(name):
165 """Import a module.
166
167 This enforces that monkeypatch() is called first.
168
169 @param name: name of module as string
170 """
Allen Li464220f2017-09-12 17:14:22 -0700171 if not _setup_done:
Allen Li0459da32017-11-10 12:11:42 -0800172 raise ImportError('cannot load chromite modules before monkeypatching')
173 return importlib.import_module(name)