blob: eecf709641bc8497a3d8e57480a65b3a8071d029 [file] [log] [blame]
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Various utility functions and classes not specific to any single area."""
import logging
import logging.handlers
import optparse
import os
import sys
import time
class OptionParserWithLogging(optparse.OptionParser):
"""Adds --verbose option."""
# Set to True to enable --log-file options.
enable_log_file = True
def __init__(self, verbose=0, log_file=None, **kwargs):
kwargs.setdefault('description', sys.modules['__main__'].__doc__)
optparse.OptionParser.__init__(self, **kwargs)
self.add_option(
'-v', '--verbose',
action='count',
default=verbose,
help='Use multiple times to increase verbosity')
if self.enable_log_file:
self.add_option(
'-l', '--log-file',
default=log_file,
help='The name of the file to store rotating log details')
self.add_option(
'--no-log', action='store_const', const='', dest='log_file',
help='Disable log file')
def parse_args(self, *args, **kwargs):
options, args = optparse.OptionParser.parse_args(self, *args, **kwargs)
levels = [logging.ERROR, logging.INFO, logging.DEBUG]
level = levels[min(len(levels) - 1, options.verbose)]
logging_console = logging.StreamHandler()
logging_console.setFormatter(logging.Formatter(
'%(levelname)5s %(module)15s(%(lineno)3d): %(message)s'))
logging_console.setLevel(level)
logging.getLogger().setLevel(level)
logging.getLogger().addHandler(logging_console)
if self.enable_log_file and options.log_file:
# This is necessary otherwise attached handler will miss the messages.
logging.getLogger().setLevel(logging.DEBUG)
logging_rotating_file = logging.handlers.RotatingFileHandler(
options.log_file,
maxBytes=10 * 1024 * 1024,
backupCount=5,
encoding='utf-8')
# log files are always at DEBUG level.
logging_rotating_file.setLevel(logging.DEBUG)
logging_rotating_file.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)-8s %(module)15s(%(lineno)3d): %(message)s'))
logging.getLogger().addHandler(logging_rotating_file)
return options, args
class Profiler(object):
"""Context manager that records time spend inside its body."""
def __init__(self, name):
self.name = name
self.start_time = None
def __enter__(self):
self.start_time = time.time()
return self
def __exit__(self, _exc_type, _exec_value, _traceback):
time_taken = time.time() - self.start_time
logging.info('Profiling: Section %s took %3.3f seconds',
self.name, time_taken)
class Unbuffered(object):
"""Disable buffering on a file object."""
def __init__(self, stream):
self.stream = stream
def write(self, data):
self.stream.write(data)
if '\n' in data:
self.stream.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
def disable_buffering():
"""Makes this process and child processes stdout unbuffered."""
if not os.environ.get('PYTHONUNBUFFERED'):
# Since sys.stdout is a C++ object, it's impossible to do
# sys.stdout.write = lambda...
sys.stdout = Unbuffered(sys.stdout)
os.environ['PYTHONUNBUFFERED'] = 'x'
def fix_python_path(cmd):
"""Returns the fixed command line to call the right python executable."""
out = cmd[:]
if out[0] == 'python':
out[0] = sys.executable
elif out[0].endswith('.py'):
out.insert(0, sys.executable)
return out