blob: ca508368d5594f5849a4c60166c66aa446b66405 [file] [log] [blame]
import os
import re
import sys
# This must run on Python versions less than 2.4.
dirname = os.path.dirname(sys.modules[__name__].__file__)
common_dir = os.path.abspath(os.path.join(dirname, 'common_lib'))
sys.path.insert(0, common_dir)
import check_version
sys.path.pop(0)
def _get_pyversion_from_args():
"""Extract, format, & pop the current py_version from args, if provided."""
py_version = 2
py_version_re = re.compile(r'--py_version=(\w+)\b')
version_found = False
for i, arg in enumerate(sys.argv):
if not arg.startswith('--py_version'):
continue
result = py_version_re.search(arg)
if result:
if version_found:
raise ValueError('--py_version may only be specified once.')
py_version = result.group(1)
version_found = True
if py_version not in ('2', '3'):
raise ValueError('Python version must be "2" or "3".')
# Remove the arg so other argparsers don't get grumpy.
sys.argv.pop(i)
return py_version
def _desired_version():
"""
Returns desired python version.
If the PY_VERSION env var is set, just return that. This is the case
when autoserv kicks of autotest on the server side via a job.run(), or
a process created a subprocess.
Otherwise, parse & pop the sys.argv for the '--py_version' flag. If no
flag is set, default to python 2 (for now).
"""
# Even if the arg is in the env vars, we will attempt to get it from the
# args, so that it can be popped prior to other argparsers hitting.
py_version = _get_pyversion_from_args()
if os.getenv('PY_VERSION'):
return int(os.getenv('PY_VERSION'))
os.environ['PY_VERSION'] = str(py_version)
return int(py_version)
desired_version = _desired_version()
if desired_version == sys.version_info.major:
os.environ['AUTOTEST_NO_RESTART'] = 'True'
else:
# There are cases were this can be set (ie by test_that), but a subprocess
# is launched in the incorrect version.
if os.getenv('AUTOTEST_NO_RESTART'):
del os.environ['AUTOTEST_NO_RESTART']
check_version.check_python_version(desired_version)
import glob, traceback, types
def import_module(module, from_where):
"""Equivalent to 'from from_where import module'
Returns the corresponding module"""
from_module = __import__(from_where, globals(), locals(), [module])
return getattr(from_module, module)
def _autotest_logging_handle_error(self, record):
"""Method to monkey patch into logging.Handler to replace handleError."""
# The same as the default logging.Handler.handleError but also prints
# out the original record causing the error so there is -some- idea
# about which call caused the logging error.
import logging
if logging.raiseExceptions:
# Avoid recursion as the below output can end up back in here when
# something has *seriously* gone wrong in autotest.
logging.raiseExceptions = 0
sys.stderr.write('Exception occurred formatting message: '
'%r using args %r\n' % (record.msg, record.args))
traceback.print_stack()
sys.stderr.write('-' * 50 + '\n')
traceback.print_exc()
sys.stderr.write('Future logging formatting exceptions disabled.\n')
def _monkeypatch_logging_handle_error():
# Hack out logging.py*
logging_py = os.path.join(os.path.dirname(__file__), 'common_lib',
'logging.py*')
if glob.glob(logging_py):
os.system('rm -f %s' % logging_py)
# Monkey patch our own handleError into the logging module's StreamHandler.
# A nicer way of doing this -might- be to have our own logging module define
# an autotest Logger instance that added our own Handler subclass with this
# handleError method in it. But that would mean modifying tons of code.
import logging
assert callable(logging.Handler.handleError)
logging.Handler.handleError = _autotest_logging_handle_error
def _insert_site_packages(root):
# Allow locally installed third party packages to be found
# before any that are installed on the system itself when not.
# running as a client.
# This is primarily for the benefit of frontend and tko so that they
# may use libraries other than those available as system packages.
sys.path.insert(0, os.path.join(root, 'site-packages'))
import importlib
ROOT_MODULE_NAME_ALLOW_LIST = (
'autotest_lib',
'autotest_lib.client',
)
def _setup_top_level_symlink(base_path):
"""Create a self pointing symlink in the base_path)."""
if os.path.islink(os.path.join(base_path, 'autotest_lib')):
return
os.chdir(base_path)
os.symlink('.', 'autotest_lib')
def _setup_client_symlink(base_path):
"""Setup the client symlink for the DUT.
Creates a "autotest_lib" folder in client, then creates a symlink called
"client" pointing back to ../, as well as an __init__ for the folder.
"""
def _create_client_symlink():
os.chdir(autotest_lib_dir)
with open('__init__.py', 'w'):
pass
os.symlink('../', 'client')
autotest_lib_dir = os.path.join(base_path, 'autotest_lib')
if os.path.isdir(autotest_lib_dir):
if os.path.islink(os.path.join(autotest_lib_dir, 'client')):
return
_create_client_symlink()
return
os.mkdir(autotest_lib_dir)
_create_client_symlink()
def _symlink_check(base_path, root_dir):
"""Verify the required symlinks are present, and add them if not."""
# Note the starting cwd to later change back to it.
starting_dir = os.getcwd()
if root_dir == 'autotest_lib':
_setup_top_level_symlink(base_path)
elif root_dir == 'autotest_lib.client':
_setup_client_symlink(base_path)
os.chdir(starting_dir)
def setup(base_path, root_module_name):
_symlink_check(base_path, root_module_name)
if root_module_name not in ROOT_MODULE_NAME_ALLOW_LIST:
raise Exception('Unexpected root module: ' + root_module_name)
_insert_site_packages(base_path)
# Ie, server (or just not /client)
if root_module_name == 'autotest_lib':
# Base path is just x/x/x/x/autotest/files
_setup_autotest_lib(base_path)
_preimport_top_level_packages(os.path.join(base_path, 'autotest_lib'),
parent='autotest_lib')
else: # aka, in /client/
if os.path.exists(os.path.join(os.path.dirname(base_path), 'server')):
# Takes you from /client/ to /files
# this is because on DUT there is no files/client
autotest_base_path = os.path.dirname(base_path)
else:
autotest_base_path = base_path
_setup_autotest_lib(autotest_base_path)
_preimport_top_level_packages(os.path.join(autotest_base_path,
'autotest_lib'),
parent='autotest_lib')
_preimport_top_level_packages(
os.path.join(autotest_base_path, 'autotest_lib', 'client'),
parent='autotest_lib.client',
)
_monkeypatch_logging_handle_error()
def _setup_autotest_lib(path):
sys.path.insert(0, path)
# This is a symlink back to the root directory, that does all the magic.
importlib.import_module('autotest_lib')
sys.path.pop(0)
def _preimport_top_level_packages(root, parent):
# The old code to setup the packages used to fetch the top-level packages
# inside autotest_lib. We keep that behaviour in order to avoid having to
# add import statements for the top-level packages all over the codebase.
#
# e.g.,
# import common
# from autotest_lib.server import utils
#
# must continue to work. The _right_ way to do that import would be.
#
# import common
# import autotest_lib.server
# from autotest_lib.server import utils
names = []
for filename in os.listdir(root):
path = os.path.join(root, filename)
if not os.path.isdir(path):
continue # skip files
if '.' in filename:
continue # if "." is in the name it's not a valid package name
if not os.access(path, os.R_OK | os.X_OK):
continue # need read + exec access to make a dir importable
if '__init__.py' in os.listdir(path):
names.append(filename)
for name in names:
pname = parent + '.' + name
importlib.import_module(pname)
if name != 'autotest_lib':
sys.modules[name] = sys.modules[pname]