blob: 82e09240ebe2b879fc47cf7b401f030906290dcb [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 _create_module(name):
"""Create a single top-level module"""
module = types.ModuleType(name)
sys.modules[name] = module
return module
def _create_module_and_parents(name):
"""Create a module, and all the necessary parents"""
parts = name.split('.')
# first create the top-level module
parent = _create_module(parts[0])
created_parts = [parts[0]]
parts.pop(0)
# now, create any remaining child modules
while parts:
child_name = parts.pop(0)
module = types.ModuleType(child_name)
setattr(parent, child_name, module)
created_parts.append(child_name)
sys.modules['.'.join(created_parts)] = module
parent = module
def _import_children_into_module(parent_module_name, path):
"""Import all the packages on a path into a parent module"""
# find all the packages at 'path'
names = []
for filename in os.listdir(path):
full_name = os.path.join(path, filename)
if not os.path.isdir(full_name):
continue # skip files
if '.' in filename:
continue # if '.' is in the name it's not a valid package name
if not os.access(full_name, os.R_OK | os.X_OK):
continue # need read + exec access to make a dir importable
if '__init__.py' in os.listdir(full_name):
names.append(filename)
# import all the packages and insert them into 'parent_module'
sys.path.insert(0, path)
for name in names:
module = __import__(name)
# add the package to the parent
parent_module = sys.modules[parent_module_name]
setattr(parent_module, name, module)
full_name = parent_module_name + '.' + name
sys.modules[full_name] = module
# restore the system path
sys.path.pop(0)
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 setup(base_path, root_module_name=""):
"""
Perform all the necessary setup so that all the packages at
'base_path' can be imported via "import root_module_name.package".
If root_module_name is empty, then all the packages at base_path
are inserted as top-level packages.
Also, setup all the common.* aliases for modules in the common
library.
The setup must be different if you are running on an Autotest server
or on a test machine that just has the client directories installed.
"""
# Hack... Any better ideas?
if (root_module_name == 'autotest_lib.client' and
os.path.exists(os.path.join(os.path.dirname(__file__),
'..', 'server'))):
root_module_name = 'autotest_lib'
base_path = os.path.abspath(os.path.join(base_path, '..'))
_create_module_and_parents(root_module_name)
_import_children_into_module(root_module_name, base_path)
if root_module_name == 'autotest_lib':
# 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(base_path, 'site-packages'))
_monkeypatch_logging_handle_error()