<commit_before>#!/usr/bin/python2.6
# Copyright 2011 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import chrome_control
import ctypes
import ctypes.wintypes
import glob
import exceptions
import etw
import etw.descriptors.pagefault as pagefault
import etw.descriptors.pagefault_xp as pagefault_xp
import etw.descriptors.process as process
import etw.evntrace as evn
import etw_db
import logging
import optparse
import os.path
import pkg_resources
import re
import shutil
import subprocess
import sys
import tempfile
import time
import win32api

# The Windows prefetch directory, this is possibly only valid on Windows XP.
_PREFETCH_DIR = os.path.join(os.environ['WINDIR'], 'Prefetch')

# TODO(siggi): make this configurable?
_CHROME_RE = re.compile(r'chrome\.exe', re.I)

# Set up a file-local logger.
_logger = logging.getLogger(__name__)

def _DeletePrefetch():
  """Deletes all files that start with Chrome.exe in the OS prefetch cache.
  """
  files = glob.glob('%s\\Chrome.exe*.pf' % _PREFETCH_DIR)
  _logger.info("Deleting %d prefetch files", len(files))
  for file in files:
    os.unlink(file)

def _GetRunInSnapshotExeResourceName():
  """Return the name of the most appropriate run_in_snapshot executable for
  the system we're running on."""
  maj, min = sys.getwindowsversion()[:2]
  # 5 is XP.
  if maj < 6:
    return 'run_in_snapshot_xp.exe'

  # We're on Vista or better, pick the 32 or 64 bit version as appropriate.
  is_wow64 = ctypes.wintypes.BOOL()
  is_wow64_function = ctypes.windll.kernel32.IsWow64Process
  if not is_wow64_function(win32api.GetCurrentProcess(),
                           ctypes.byref(is_wow64)):
    raise Exception('IsWow64Process failed.')
  if is_wow64:
    return 'run_in_snapshot_x64.exe'

  return 'run_in_snapshot.exe'

def _GetRunInSnapshotExe():
  """Return the appropriate run_in_snapshot executable for this system."""
  name = _GetRunInSnapshotExeResourceName()
  run_in_snapshot = pkg_resources.resource_filename(__name__,
                                                    os.path.join('exe', name))

  return run_in_snapshot

class LogEventCounter(etw.EventConsumer):
  """A utility class to parse salient metrics from ETW logs"""

  def __init__(self, file_db, module_db, process_db):
    """Initialize a log event counter.

    Args:
        file_db: an etw_db.FileNameDatabase instance.
        module_db: an etw_db.ModuleDatabase instance.
        process_db: an etw_db.ProcessThreadDatabase instance.
    """
    self._file_db = file_db
    self._module_db = module_db
    self._process_db = process_db
    self._hardfaults = 0
    self._softfaults = 0
    self._process_launch = []

  @etw.EventHandler(process.Event.Start)
  def _OnProcessStart(self, event):
    if _CHROME_RE.search(event.ImageFileName):
      self._process_launch.append(event.time_stamp)

  @etw.EventHandler(pagefault.Event.HardFault)
  def _OnHardFault(self, event):
    # Resolve the thread id in the event back to the faulting process.
    process = self._process_db.GetThreadProcess(event.TThreadId)
    if process and _CHROME_RE.search(process.image_file_name):
      self._hardfaults += 1

  @etw.EventHandler(pagefault.Event.AccessViolation,
                    pagefault.Event.CopyOnWrite,
                    pagefault.Event.DemandZeroFault,
                    pagefault.Event.GuardPageFault,
                    pagefault.Event.TransitionFault)
  def _OnSoftFault(self, event):
    # Resolve the faulting process.
    process = self._process_db.GetProcess(event.process_id)
    if process and _CHROME_RE.search(process.image_file_name):
      self._softfaults += 1

class BenchmarkRunner(object):
  """A utility class to manage the running of Chrome startup time benchmarks.

  This class can run a given Chrome instance through a few different
  configurable scenarios:
    * With Chrome.dll preloading turned on or turned off.
    * Cold/Warm start. To simulate cold start, the volume containing the
      Chrome executable under test will be remounted to a new drive letter
      using the Windows shadow volume service.
    * With Windows XP OS prefetching enabled or disabled.
  """

  def __init__(self, chrome_exe, profile_dir, preload, cold_start, prefetch,
               keep_temp_dirs):
    """Initialize instance.

    Args:
        chrome_exe: path to the Chrome executable to benchmark.
        profile_dir: path to the existing profile directory for Chrome.
        preload: specifies the state of Chrome.dll preload to use for
            benchmark.
        cold_start: if True, chrome_exe will be launched from a shadow volume
            freshly minted and mounted for each iteration.
        prefetch: if False, the OS prefetch files will be deleted before and
            after each iteration.
        keep_temp_dirs: if True, the script will not clean up the temporary
            directories it creates. This is handy if you want to e.g. manually
            inspect the log files generated.
    """
    self._chrome_exe = chrome_exe
    self._profile_dir = profile_dir
    self._preload = preload
    self._cold_start = cold_start
    self._prefetch = prefetch
    self._keep_temp_dirs = keep_temp_dirs
    self._results = {}
    self._temp_dir = None

  def Run(self, iterations):
    """Runs the benchmark for a given number of iterations.

    Args:
        iterations: number of iterations to run.
    """
    self._SetUp()

    try:
      # Run the benchmark for the number of iterations specified.
      for i in range(iterations):
        _logger.info("Starting iteration %d", i)
        self._PreIteration(i)
        self._RunOneIteration(i)
        self._PostIteration(i)

      # Output the results after completing all iterations.
      self._OutputResults()
    except:
      _logger.exception('Failure in iteration %d', i)
    finally:
      self._TearDown()

  def _SetUp(self):
    self._old_preload = chrome_control.GetPreload()
    chrome_control.SetPreload(self._preload)
    self._temp_dir = tempfile.mkdtemp(prefix='chrome-bench')
    _logger.info('Created temporary directory "%s"', self._temp_dir)

  def _TearDown(self):
    chrome_control.SetPreload(*self._old_preload)
    if self._temp_dir and not self._keep_temp_dirs:
      _logger.info('Deleting temporary directory "%s"', self._temp_dir)
      shutil.rmtree(self._temp_dir, ignore_errors=True)
      self._temp_dir = None

  def _RunOneIteration(self, i):
    _logger.info("Iteration: %d", i)

    if self._cold_start:
      (drive, path) = os.path.splitdrive(self._chrome_exe)
      chrome_exe = os.path.join('M:', path)
      run_in_snapshot = _GetRunInSnapshotExe()
      cmd_line = [run_in_snapshot,
                  '--volume=%s\\' % drive,
                  '--snapshot=M:',
                  '--',
                  chrome_exe,
                  '--user-data-dir=%s' % self._profile_dir,
                  'http://www.google.com']
    else:
      cmd_line = [self._chrome_exe,
                  '--user-data-dir=%s' % self._profile_dir,
                  'http://www.google.com']

    _logger.info('Launching command line [%s]', cmd_line)
    subprocess.Popen(cmd_line)

    # TODO(siggi): Poll for Chrome to come into existence, then
    #     give it a fixed amount of time to do its thing before
    #     winding down.
    time.sleep(30)

    _logger.info("Shutting down Chrome Profile: %s", self._profile_dir)
    chrome_control.ShutDown(self._profile_dir)

  def _PreIteration(self, i):
    self._StartLogging()
    if not self._prefetch:
      _DeletePrefetch()

  def _PostIteration(self, i):
    self._StopLogging()
    self._ProcessLogs()

    if not self._prefetch:
      _DeletePrefetch()

  def _StartLogging(self):
    # TODO(siggi): This function needs to start a second ETW log session
    #    to capture output from Chrome's TRACE_EVENT macros.
    self._kernel_file = os.path.join(self._temp_dir, 'kernel.etl')
    _logger.info('Starting kernel logging to file "%s"', self._kernel_file)

    prop = etw.TraceProperties()
    prop.SetLogFileName(os.path.abspath(self._kernel_file))
    p = prop.get()
    p.contents.Wnode.ClientContext = 1  # QPC timer accuracy.
    p.contents.LogFileMode = evn.EVENT_TRACE_FILE_MODE_SEQUENTIAL
    p.contents.EnableFlags = (evn.EVENT_TRACE_FLAG_PROCESS |
                              evn.EVENT_TRACE_FLAG_THREAD |
                              evn.EVENT_TRACE_FLAG_IMAGE_LOAD |
                              evn.EVENT_TRACE_FLAG_DISK_FILE_IO |
                              evn.EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS |
                              evn.EVENT_TRACE_FLAG_MEMORY_HARD_FAULTS)
    self._kernel_controller = etw.TraceController()
    try:
      # We may find the NT Kernel Logger running, so attempt to stop it
      # before we create our NT Kernel Logger session. Maybe we didn't shut
      # it down after the last iteration, or maybe someone else is using it.
      # If the latter, let's hope they don't get upset with us.
      evn.ControlTrace(evn.TRACEHANDLE(),
                       evn.KERNEL_LOGGER_NAME,
                       etw.TraceProperties().get(),
                       evn.EVENT_TRACE_CONTROL_STOP)
    except exceptions.WindowsError:
      # If the log isn't running, we get a WindowsError here.
      pass

    self._kernel_controller.Start(evn.KERNEL_LOGGER_NAME, prop)

  def _StopLogging(self):
    time.sleep(10)
    prop = etw.TraceProperties()
    self._kernel_controller.Stop(prop)

    events_lost = prop.get().contents.EventsLost
    if events_lost:
      _logger.warning("%d ETW events lost", events_lost)

  def _ProcessLogs(self):
    parser = etw.consumer.TraceEventSource()
    parser.OpenFileSession(self._kernel_file)

    file_db = etw_db.FileNameDatabase()
    module_db = etw_db.ModuleDatabase()
    process_db = etw_db.ProcessThreadDatabase()
    counter = LogEventCounter(file_db, module_db, process_db)
    parser.AddHandler(file_db)
    parser.AddHandler(module_db)
    parser.AddHandler(process_db)
    parser.AddHandler(counter)
    parser.Consume()

    # TODO(siggi): Other metrics, notably:
    #   Time from launch of browser to interesting TRACE_EVENT metrics
    #     in browser and renderers.
    self._AddResult('Chrome', 'HardPageFaults', counter._hardfaults)
    self._AddResult('Chrome', 'SoftPageFaults', counter._softfaults)

    if counter._process_launch and len(counter._process_launch) >= 2:
      browser_start = counter._process_launch.pop(0)
      renderer_start = counter._process_launch.pop(0)
      self._AddResult('Chrome',
                      'RendererLaunchTime',
                      renderer_start - browser_start,
                      's')

    # We leave it to TearDown to delete any files we've created.
    self._kernel_file = None

  def _OutputResults(self):
    """Outputs the benchmark results in the format required by the
    GraphingLogProcessor class, which is:

    RESULT <graph name>: <trace name>= [<comma separated samples>] <units>

    Example:
      RESULT Chrome: RendererLaunchTime= [0.1, 0.2, 0.3] s
    """
    for (key, value) in self._results.iteritems():
      (graph_name, trace_name) = key
      (units, results) = value
      print "RESULT %s: %s= %s %s" % (graph_name, trace_name,
                                      str(results), units)

  def _AddResult(self, graph_name, trace_name, sample, units=''):
    _logger.info("Adding result %s, %s, %s, %s",
                 graph_name, trace_name, str(sample), units)
    results = self._results.setdefault((graph_name, trace_name), (units, []))
    results[1].append(sample)

_USAGE = """\
%prog [options] chrome-executable

Benchmarks the Chrome executable provided for a number of iterations,
tallies the results and prints them out to STDOUT in a format suitable
for the Chrome dashboard scripts.
"""

def _GetOptionParser():
  parser = optparse.OptionParser(usage=_USAGE)
  parser.add_option('--verbose', dest='verbose',
                    default=False, action='store_true',
                    help='Verbose logging.')
  parser.add_option('--user-data-dir', dest='profile',
                    help='The profile directory to use for the benchmark.')
  parser.add_option('--iterations', dest='iterations', type='int',
                    default=10,
                    help="Number of iterations, 10 by default.")
  parser.add_option('--no-preload', dest='preload', action='store_false',
                    default=True,
                    help="Turn Chrome.dll pre-loading off (on by default).")
  parser.add_option('--cold-start', dest='cold_start', action='store_true',
                    default=False,
                    help='Test cold start by creating a shadow volume of the '
                          'volume Chrome resides on and running it from that '
                          'newly mounted volume for each iteration of the '
                          'test.')
  parser.add_option('--no-prefetch', dest='prefetch', action='store_false',
                    default=True,
                    help='Turn OS pre-fetch off (on by default).')
  parser.add_option('--keep-temp-dirs', dest='keep_temp_dirs',
                    action='store_true', default=False,
                    help='Keep the temporary directories created during '
                         'benchmarking. This makes it easy to look at the '
                         'resultant log files.')
  return parser

def main():
  """Parses arguments and runs benchmarks."""
  parser = _GetOptionParser()
  (opts, args) = parser.parse_args()
  if len(args) != 1:
    parser.error("You must provide the Chrome.exe instance to benchmark.")

  # Minimally configure logging.
  if opts.verbose:
    logging.basicConfig(level=logging.INFO)
  else:
    logging.basicConfig(level=logging.WARNING)

  chrome_exe = args[0]
  if not os.path.exists(chrome_exe):
    parser.error("\"%s\" does not exist" % chrome_exe)

  runner = BenchmarkRunner(chrome_exe,
                           opts.profile,
                           opts.preload,
                           opts.cold_start,
                           opts.prefetch,
                           not opts.keep_temp_dirs)
  try:
    runner.Run(opts.iterations)
  except:
    logging.exception('Exception in Run.')

  return 0

if __name__ == '__main__':
  sys.exit(main())
<commit_msg>Fixes to benchmarking script.  Fix reversed keep-temp-files flag. Increase kernel log buffer sizes. Capture disk IO to kernel trace. Launch chrome to new tab page, instead of to www.google.com, to match our current profiling case.<commit_after>#!/usr/bin/python2.6
# Copyright 2011 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import chrome_control
import ctypes
import ctypes.wintypes
import glob
import exceptions
import etw
import etw.descriptors.pagefault as pagefault
import etw.descriptors.pagefault_xp as pagefault_xp
import etw.descriptors.process as process
import etw.evntrace as evn
import etw_db
import logging
import optparse
import os.path
import pkg_resources
import re
import shutil
import subprocess
import sys
import tempfile
import time
import win32api

# The Windows prefetch directory, this is possibly only valid on Windows XP.
_PREFETCH_DIR = os.path.join(os.environ['WINDIR'], 'Prefetch')

# TODO(siggi): make this configurable?
_CHROME_RE = re.compile(r'chrome\.exe', re.I)

# Set up a file-local logger.
_logger = logging.getLogger(__name__)

def _DeletePrefetch():
  """Deletes all files that start with Chrome.exe in the OS prefetch cache.
  """
  files = glob.glob('%s\\Chrome.exe*.pf' % _PREFETCH_DIR)
  _logger.info("Deleting %d prefetch files", len(files))
  for file in files:
    os.unlink(file)

def _GetRunInSnapshotExeResourceName():
  """Return the name of the most appropriate run_in_snapshot executable for
  the system we're running on."""
  maj, min = sys.getwindowsversion()[:2]
  # 5 is XP.
  if maj < 6:
    return 'run_in_snapshot_xp.exe'

  # We're on Vista or better, pick the 32 or 64 bit version as appropriate.
  is_wow64 = ctypes.wintypes.BOOL()
  is_wow64_function = ctypes.windll.kernel32.IsWow64Process
  if not is_wow64_function(win32api.GetCurrentProcess(),
                           ctypes.byref(is_wow64)):
    raise Exception('IsWow64Process failed.')
  if is_wow64:
    return 'run_in_snapshot_x64.exe'

  return 'run_in_snapshot.exe'

def _GetRunInSnapshotExe():
  """Return the appropriate run_in_snapshot executable for this system."""
  name = _GetRunInSnapshotExeResourceName()
  run_in_snapshot = pkg_resources.resource_filename(__name__,
                                                    os.path.join('exe', name))

  return run_in_snapshot

class LogEventCounter(etw.EventConsumer):
  """A utility class to parse salient metrics from ETW logs"""

  def __init__(self, file_db, module_db, process_db):
    """Initialize a log event counter.

    Args:
        file_db: an etw_db.FileNameDatabase instance.
        module_db: an etw_db.ModuleDatabase instance.
        process_db: an etw_db.ProcessThreadDatabase instance.
    """
    self._file_db = file_db
    self._module_db = module_db
    self._process_db = process_db
    self._hardfaults = 0
    self._softfaults = 0
    self._process_launch = []

  @etw.EventHandler(process.Event.Start)
  def _OnProcessStart(self, event):
    if _CHROME_RE.search(event.ImageFileName):
      self._process_launch.append(event.time_stamp)

  @etw.EventHandler(pagefault.Event.HardFault)
  def _OnHardFault(self, event):
    # Resolve the thread id in the event back to the faulting process.
    process = self._process_db.GetThreadProcess(event.TThreadId)
    if process and _CHROME_RE.search(process.image_file_name):
      self._hardfaults += 1

  @etw.EventHandler(pagefault.Event.AccessViolation,
                    pagefault.Event.CopyOnWrite,
                    pagefault.Event.DemandZeroFault,
                    pagefault.Event.GuardPageFault,
                    pagefault.Event.TransitionFault)
  def _OnSoftFault(self, event):
    # Resolve the faulting process.
    process = self._process_db.GetProcess(event.process_id)
    if process and _CHROME_RE.search(process.image_file_name):
      self._softfaults += 1

class BenchmarkRunner(object):
  """A utility class to manage the running of Chrome startup time benchmarks.

  This class can run a given Chrome instance through a few different
  configurable scenarios:
    * With Chrome.dll preloading turned on or turned off.
    * Cold/Warm start. To simulate cold start, the volume containing the
      Chrome executable under test will be remounted to a new drive letter
      using the Windows shadow volume service.
    * With Windows XP OS prefetching enabled or disabled.
  """

  def __init__(self, chrome_exe, profile_dir, preload, cold_start, prefetch,
               keep_temp_dirs):
    """Initialize instance.

    Args:
        chrome_exe: path to the Chrome executable to benchmark.
        profile_dir: path to the existing profile directory for Chrome.
        preload: specifies the state of Chrome.dll preload to use for
            benchmark.
        cold_start: if True, chrome_exe will be launched from a shadow volume
            freshly minted and mounted for each iteration.
        prefetch: if False, the OS prefetch files will be deleted before and
            after each iteration.
        keep_temp_dirs: if True, the script will not clean up the temporary
            directories it creates. This is handy if you want to e.g. manually
            inspect the log files generated.
    """
    self._chrome_exe = chrome_exe
    self._profile_dir = profile_dir
    self._preload = preload
    self._cold_start = cold_start
    self._prefetch = prefetch
    self._keep_temp_dirs = keep_temp_dirs
    self._results = {}
    self._temp_dir = None

  def Run(self, iterations):
    """Runs the benchmark for a given number of iterations.

    Args:
        iterations: number of iterations to run.
    """
    self._SetUp()

    try:
      # Run the benchmark for the number of iterations specified.
      for i in range(iterations):
        _logger.info("Starting iteration %d", i)
        self._PreIteration(i)
        self._RunOneIteration(i)
        self._PostIteration(i)

      # Output the results after completing all iterations.
      self._OutputResults()
    except:
      _logger.exception('Failure in iteration %d', i)
    finally:
      self._TearDown()

  def _SetUp(self):
    self._old_preload = chrome_control.GetPreload()
    chrome_control.SetPreload(self._preload)
    self._temp_dir = tempfile.mkdtemp(prefix='chrome-bench')
    _logger.info('Created temporary directory "%s"', self._temp_dir)

  def _TearDown(self):
    chrome_control.SetPreload(*self._old_preload)
    if self._temp_dir and not self._keep_temp_dirs:
      _logger.info('Deleting temporary directory "%s"', self._temp_dir)
      shutil.rmtree(self._temp_dir, ignore_errors=True)
      self._temp_dir = None

  def _RunOneIteration(self, i):
    _logger.info("Iteration: %d", i)

    if self._cold_start:
      (drive, path) = os.path.splitdrive(self._chrome_exe)
      chrome_exe = os.path.join('M:', path)
      run_in_snapshot = _GetRunInSnapshotExe()
      cmd_line = [run_in_snapshot,
                  '--volume=%s\\' % drive,
                  '--snapshot=M:',
                  '--',
                  chrome_exe,
                  '--user-data-dir=%s' % self._profile_dir]
    else:
      cmd_line = [self._chrome_exe,
                  '--user-data-dir=%s' % self._profile_dir,
                  'http://www.google.com']

    _logger.info('Launching command line [%s]', cmd_line)
    subprocess.Popen(cmd_line)

    # TODO(siggi): Poll for Chrome to come into existence, then
    #     give it a fixed amount of time to do its thing before
    #     winding down.
    time.sleep(20)

    _logger.info("Shutting down Chrome Profile: %s", self._profile_dir)
    chrome_control.ShutDown(self._profile_dir)

  def _PreIteration(self, i):
    self._StartLogging()
    if not self._prefetch:
      _DeletePrefetch()

  def _PostIteration(self, i):
    self._StopLogging()
    self._ProcessLogs()

    if not self._prefetch:
      _DeletePrefetch()

  def _StartLogging(self):
    # TODO(siggi): This function needs to start a second ETW log session
    #    to capture output from Chrome's TRACE_EVENT macros.
    self._kernel_file = os.path.join(self._temp_dir, 'kernel.etl')
    _logger.info('Starting kernel logging to file "%s"', self._kernel_file)

    prop = etw.TraceProperties()
    prop.SetLogFileName(os.path.abspath(self._kernel_file))
    p = prop.get()
    p.contents.Wnode.ClientContext = 1  # QPC timer accuracy.
    p.contents.LogFileMode = evn.EVENT_TRACE_FILE_MODE_SEQUENTIAL
    p.contents.EnableFlags = (evn.EVENT_TRACE_FLAG_PROCESS |
                              evn.EVENT_TRACE_FLAG_THREAD |
                              evn.EVENT_TRACE_FLAG_IMAGE_LOAD |
                              evn.EVENT_TRACE_FLAG_DISK_IO |
                              evn.EVENT_TRACE_FLAG_DISK_FILE_IO |
                              evn.EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS |
                              evn.EVENT_TRACE_FLAG_MEMORY_HARD_FAULTS)
    # TODO(siggi): It might make sense to make these dynamic, and to re-run
    #     iterations with more/larger buffers if we've lost events.
    p.contents.BufferSize = 1024  # kilobytes - that's one meg.
    p.contents.MinimumBuffers = 100
    p.contents.MaximumBuffers = 200
    p.contents.FlushTimer = 0

    self._kernel_controller = etw.TraceController()
    try:
      # We may find the NT Kernel Logger running, so attempt to stop it
      # before we create our NT Kernel Logger session. Maybe we didn't shut
      # it down after the last iteration, or maybe someone else is using it.
      # If the latter, let's hope they don't get upset with us.
      evn.ControlTrace(evn.TRACEHANDLE(),
                       evn.KERNEL_LOGGER_NAME,
                       etw.TraceProperties().get(),
                       evn.EVENT_TRACE_CONTROL_STOP)
    except exceptions.WindowsError:
      # If the log isn't running, we get a WindowsError here.
      pass

    self._kernel_controller.Start(evn.KERNEL_LOGGER_NAME, prop)

  def _StopLogging(self):
    _logger.info("Stopping kernel logging.")
    prop = etw.TraceProperties()
    self._kernel_controller.Stop(prop)

    buffers_lost = prop.get().contents.LogBuffersLost
    events_lost = prop.get().contents.EventsLost
    if events_lost or buffers_lost:
      _logger.warning('%d ETW buffers lost', buffers_lost)
      _logger.warning('%d ETW events lost', events_lost)
      _logger.warning('You may need to increase the number or size of '
                      'of buffers (see _StartLogging).')

  def _ProcessLogs(self):
    parser = etw.consumer.TraceEventSource()
    parser.OpenFileSession(self._kernel_file)

    file_db = etw_db.FileNameDatabase()
    module_db = etw_db.ModuleDatabase()
    process_db = etw_db.ProcessThreadDatabase()
    counter = LogEventCounter(file_db, module_db, process_db)
    parser.AddHandler(file_db)
    parser.AddHandler(module_db)
    parser.AddHandler(process_db)
    parser.AddHandler(counter)
    parser.Consume()

    # TODO(siggi): Other metrics, notably:
    #   Time from launch of browser to interesting TRACE_EVENT metrics
    #     in browser and renderers.
    self._AddResult('Chrome', 'HardPageFaults', counter._hardfaults)
    self._AddResult('Chrome', 'SoftPageFaults', counter._softfaults)

    if counter._process_launch and len(counter._process_launch) >= 2:
      browser_start = counter._process_launch.pop(0)
      renderer_start = counter._process_launch.pop(0)
      self._AddResult('Chrome',
                      'RendererLaunchTime',
                      renderer_start - browser_start,
                      's')

    # We leave it to TearDown to delete any files we've created.
    self._kernel_file = None

  def _OutputResults(self):
    """Outputs the benchmark results in the format required by the
    GraphingLogProcessor class, which is:

    RESULT <graph name>: <trace name>= [<comma separated samples>] <units>

    Example:
      RESULT Chrome: RendererLaunchTime= [0.1, 0.2, 0.3] s
    """
    for (key, value) in self._results.iteritems():
      (graph_name, trace_name) = key
      (units, results) = value
      print "RESULT %s: %s= %s %s" % (graph_name, trace_name,
                                      str(results), units)

  def _AddResult(self, graph_name, trace_name, sample, units=''):
    _logger.info("Adding result %s, %s, %s, %s",
                 graph_name, trace_name, str(sample), units)
    results = self._results.setdefault((graph_name, trace_name), (units, []))
    results[1].append(sample)

_USAGE = """\
%prog [options] chrome-executable

Benchmarks the Chrome executable provided for a number of iterations,
tallies the results and prints them out to STDOUT in a format suitable
for the Chrome dashboard scripts.
"""

def _GetOptionParser():
  parser = optparse.OptionParser(usage=_USAGE)
  parser.add_option('--verbose', dest='verbose',
                    default=False, action='store_true',
                    help='Verbose logging.')
  parser.add_option('--user-data-dir', dest='profile',
                    help='The profile directory to use for the benchmark.')
  parser.add_option('--iterations', dest='iterations', type='int',
                    default=10,
                    help="Number of iterations, 10 by default.")
  parser.add_option('--no-preload', dest='preload', action='store_false',
                    default=True,
                    help="Turn Chrome.dll pre-loading off (on by default).")
  parser.add_option('--cold-start', dest='cold_start', action='store_true',
                    default=False,
                    help='Test cold start by creating a shadow volume of the '
                          'volume Chrome resides on and running it from that '
                          'newly mounted volume for each iteration of the '
                          'test.')
  parser.add_option('--no-prefetch', dest='prefetch', action='store_false',
                    default=True,
                    help='Turn OS pre-fetch off (on by default).')
  parser.add_option('--keep-temp-dirs', dest='keep_temp_dirs',
                    action='store_true', default=False,
                    help='Keep the temporary directories created during '
                         'benchmarking. This makes it easy to look at the '
                         'resultant log files.')
  return parser

def main():
  """Parses arguments and runs benchmarks."""
  parser = _GetOptionParser()
  (opts, args) = parser.parse_args()
  if len(args) != 1:
    parser.error("You must provide the Chrome.exe instance to benchmark.")

  # Minimally configure logging.
  if opts.verbose:
    logging.basicConfig(level=logging.INFO)
  else:
    logging.basicConfig(level=logging.WARNING)

  chrome_exe = args[0]
  if not os.path.exists(chrome_exe):
    parser.error("\"%s\" does not exist" % chrome_exe)

  runner = BenchmarkRunner(chrome_exe,
                           opts.profile,
                           opts.preload,
                           opts.cold_start,
                           opts.prefetch,
                           opts.keep_temp_dirs)
  try:
    runner.Run(opts.iterations)
  except:
    logging.exception('Exception in Run.')

  return 0

if __name__ == '__main__':
  sys.exit(main())
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

from collections import OrderedDict

palette = [
    0x788084, 0x0000fc, 0x0000c4, 0x4028c4,
    0x94008c, 0xac0028, 0xac1000, 0x8c1800,
    0x503000, 0x007800, 0x006800, 0x005800,
    0x004058, 0x000000, 0x000000, 0x000008,

    0xbcc0c4, 0x0078fc, 0x0088fc, 0x6848fc,
    0xdc00d4, 0xe40060, 0xfc3800, 0xe46918,
    0xac8000, 0x00b800, 0x00a800, 0x00a848,
    0x008894, 0x2c2c2c, 0x000000, 0x000000,

    0xfcf8fc, 0x38c0fc, 0x6888fc, 0x9c78fc,
    0xfc78fc, 0xfc589c, 0xfc7858, 0xfca048,
    0xfcb800, 0xbcf818, 0x58d858, 0x58f89c,
    0x00e8e4, 0x606060, 0x000000, 0x000000,

    0xfcf8fc, 0xa4e8fc, 0xbcb8fc, 0xdcb8fc,
    0xfcb8fc, 0xf4c0e0, 0xf4d0b4, 0xfce0b4,
    0xfcd884, 0xdcf878, 0xb8f878, 0xb0f0d8,
    0x00f8fc, 0xc8c0c0, 0x000000, 0x000000
]

def load_sprites(src):
    f = open(src, 'rb')
    content = f.read()
    f.close()
    assert len(content) % 16 == 0
    bin = [ord(c) for c in content]
    return bin

def load_indexed_sprites(src):
    f = open(src, 'rb')
    content = f.read()
    assert len(content) % 16 == 0
    bin = [ord(c) for c in content]
    assert len(bin) % 16 == 0
    indexes = OrderedDict()
    for i in range(len(content) / 16):
        indexes[content[i * 16: i * 16 + 16]] = i
    return bin, indexes

def decode_sprite(channelA, channelB):
    s = []
    y = 0
    for y in range(0,8):
        a = channelA[y]
        b = channelB[y]
        line = []
        for x in range(0,8):
            bit = pow(2,7-x)
            pixel = -1
            if (not (a & bit) and not (b & bit)):
                pixel = 0
            elif ((a & bit) and not (b & bit)):
                pixel = 1
            elif (not (a & bit) and (b & bit)):
                pixel = 2
            elif ((a & bit) and (b & bit)):
                pixel = 3
            line.append(pixel)
        s.append(line)
    return s

def get_sprite(index, sprites):
    assert len(sprites) > index
    iA = index * 16
    iB = iA + 8
    iC = iB + 8
    channelA = sprites[iA:iB]
    channelB = sprites[iB:iC]
    return decode_sprite(channelA, channelB)

def encode_sprite(sprite):
    channelA = []
    channelB = []
    for y in range(8):
        a = 0
        b = 0
        for x in range(8):
            pixel = sprite[y][x]
            bit = pow(2,7-x)
            if pixel == 1:
                a = a | bit
            elif pixel == 2:
                b = b | bit
            elif pixel == 3:
                a = a | bit
                b = b | bit
        channelA.append(a)
        channelB.append(b)
    return channelA + channelB

def put_sprite(index, sprites, spr):
    start = index * 16
    encoded = encode_sprite(spr)
    j = 0
    for i in range(start, start + 16):
        sprites[i] = encoded[j]
        j += 1
    return sprites

def length(sprites):
    return len(sprites) / 16

def find_sprite(sprites, spr, start=0):
    for index in range(start, length(sprites)):
        if spr == get_sprite(index, sprites):
            return index - start
    return -1

class SpriteSet():

    def __init__(self, sprite_data=None):
        if isinstance(sprite_data, str):
            self.sprs, self.indexes = load_indexed_sprites(sprite_data)
        else:
            (self.sprs, self.indexes) = sprite_data

    def __len__(self):
        return length(self.sprs)

    def get(self, index):
        return get_sprite(index, self.sprs)

    def put(self, index, spr):
        return put_sprite(index, spr, self.sprs)

    def has_sprite(self, spr):
        if isinstance(spr, list):
            spr = encode_sprite(spr)
            spr = ''.join(chr(c) for c in spr)
        if spr in self.indexes:
            return self.indexes[spr]
        return False

<commit_msg>PEP8 fixes on sprite.py<commit_after># -*- coding: utf-8 -*-

from collections import OrderedDict

palette = [
    0x788084, 0x0000fc, 0x0000c4, 0x4028c4,
    0x94008c, 0xac0028, 0xac1000, 0x8c1800,
    0x503000, 0x007800, 0x006800, 0x005800,
    0x004058, 0x000000, 0x000000, 0x000008,

    0xbcc0c4, 0x0078fc, 0x0088fc, 0x6848fc,
    0xdc00d4, 0xe40060, 0xfc3800, 0xe46918,
    0xac8000, 0x00b800, 0x00a800, 0x00a848,
    0x008894, 0x2c2c2c, 0x000000, 0x000000,

    0xfcf8fc, 0x38c0fc, 0x6888fc, 0x9c78fc,
    0xfc78fc, 0xfc589c, 0xfc7858, 0xfca048,
    0xfcb800, 0xbcf818, 0x58d858, 0x58f89c,
    0x00e8e4, 0x606060, 0x000000, 0x000000,

    0xfcf8fc, 0xa4e8fc, 0xbcb8fc, 0xdcb8fc,
    0xfcb8fc, 0xf4c0e0, 0xf4d0b4, 0xfce0b4,
    0xfcd884, 0xdcf878, 0xb8f878, 0xb0f0d8,
    0x00f8fc, 0xc8c0c0, 0x000000, 0x000000
]

def load_sprites(src):
    f = open(src, 'rb')
    content = f.read()
    f.close()
    assert len(content) % 16 == 0
    bin = [ord(c) for c in content]
    return bin

def load_indexed_sprites(src):
    f = open(src, 'rb')
    content = f.read()
    assert len(content) % 16 == 0
    bin = [ord(c) for c in content]
    assert len(bin) % 16 == 0
    indexes = OrderedDict()
    for i in range(len(content) / 16):
        indexes[content[i * 16: i * 16 + 16]] = i
    return bin, indexes

def decode_sprite(channelA, channelB):
    s = []
    y = 0
    for y in range(0, 8):
        a = channelA[y]
        b = channelB[y]
        line = []
        for x in range(0, 8):
            bit = pow(2, 7 - x)
            pixel = -1
            if (not (a & bit) and not (b & bit)):
                pixel = 0
            elif ((a & bit) and not (b & bit)):
                pixel = 1
            elif (not (a & bit) and (b & bit)):
                pixel = 2
            elif ((a & bit) and (b & bit)):
                pixel = 3
            line.append(pixel)
        s.append(line)
    return s

def get_sprite(index, sprites):
    assert len(sprites) > index
    iA = index * 16
    iB = iA + 8
    iC = iB + 8
    channelA = sprites[iA:iB]
    channelB = sprites[iB:iC]
    return decode_sprite(channelA, channelB)

def encode_sprite(sprite):
    channelA = []
    channelB = []
    for y in range(8):
        a = 0
        b = 0
        for x in range(8):
            pixel = sprite[y][x]
            bit = pow(2, 7 - x)
            if pixel == 1:
                a = a | bit
            elif pixel == 2:
                b = b | bit
            elif pixel == 3:
                a = a | bit
                b = b | bit
        channelA.append(a)
        channelB.append(b)
    return channelA + channelB

def put_sprite(index, sprites, spr):
    start = index * 16
    encoded = encode_sprite(spr)
    j = 0
    for i in range(start, start + 16):
        sprites[i] = encoded[j]
        j += 1
    return sprites

def length(sprites):
    return len(sprites) / 16

def find_sprite(sprites, spr, start=0):
    for index in range(start, length(sprites)):
        if spr == get_sprite(index, sprites):
            return index - start
    return -1

class SpriteSet():

    def __init__(self, sprite_data=None):
        if isinstance(sprite_data, str):
            self.sprs, self.indexes = load_indexed_sprites(sprite_data)
        else:
            (self.sprs, self.indexes) = sprite_data

    def __len__(self):
        return length(self.sprs)

    def get(self, index):
        return get_sprite(index, self.sprs)

    def put(self, index, spr):
        return put_sprite(index, spr, self.sprs)

    def has_sprite(self, spr):
        if isinstance(spr, list):
            spr = encode_sprite(spr)
            spr = ''.join(chr(c) for c in spr)
        if spr in self.indexes:
            return self.indexes[spr]
        return False
<|endoftext|>


<commit_before>from .connection import connect
from .spec import QuerySpecification, Slice
from .exceptions import DoesNotExist, OperationError
from .eagerload import Eagerload
from .utils import lookup_field
import copy
import pymongo
import pymongo.errors
import pprint
import re

class Manager(object):
    def __init__(self):
        self._collection = None

    def __get__(self, instance, owner):
        if instance is not None:
            return self

        if self._collection is None:
            db = connect(owner._meta['db'])
            self._collection = db[owner._meta['collection']]

        return Query(owner, self._collection)

class Query(object):
    def __init__(self, document_cls, collection):
        self._document_cls = document_cls
        self._collection = collection
        self._spec = QuerySpecification(None)
        self._pymongo_cursor = None
        self._fields = None
        self._eagerloads = []
        self._deferred_sort = []

    def clone(self):
        q = Query(self._document_cls, self._collection)
        q._spec = self._spec.clone()
        q._fields = copy.deepcopy(self._fields)
        q._eagerloads = copy.deepcopy(self._eagerloads)
        q._deferred_sort = copy.deepcopy(self._deferred_sort)
        return q

    def _compile_spec(self):
        spec = self._spec.compile()

        if self._document_cls._superclasses:
            spec['_cls'] = re.compile('^' + self._document_cls._name)

        return spec

    def _transform_key_list(self, keys):
        transformed_keys = []

        for key in keys.split():
            direction = pymongo.ASCENDING

            if key[0] == '-':
                direction = pymongo.DESCENDING

            if key[0] in ('-', '+'):
                key = key[1:]

            field = lookup_field(self._document_cls, key)

            transformed_keys.append((field.get_key(False), direction))

        return transformed_keys

    def eagerload(self, *fields, **kwargs):
        eagerload = Eagerload(kwargs.get('only'))
        map(eagerload.add_field, fields)
        self._eagerloads.append(eagerload)
        return self

    def _eagerload(self, obj):
        if obj:
            for eagerload in self._eagerloads:
                eagerload.add_documents(obj)
                eagerload.flush()

        return obj

    def hint(self, key_or_list):
        index = self._transform_key_list(key_or_list)
        self._cursor.hint(index)
        return self

    def ensure_index(self, key_or_list):
        indexes = self._transform_key_list(key_or_list)
        self._collection.ensure_index(indexes)
        return self

    def find(self, *expressions):
        if len(expressions) == 1 and isinstance(expressions[0], dict):
            return self._collection.find(expressions[0], fields=self._fields)

        return self.filter(*expressions)._collection.find(self._compile_spec(), fields=self._fields)

    def find_one(self, *expressions):
        if len(expressions) == 1 and isinstance(expressions[0], dict):
            return self._collection.find_one(expressions[0], fields=self._fields)

        return self.filter(*expressions)._collection.find_one(self._compile_spec(), fields=self._fields)

    def filter(self, *expressions):
        for expression in expressions:
            self._spec &= expression

        return self

    def filter_by(self, **kwargs):
        for k, v in kwargs.iteritems():
            self._spec &= getattr(self._document_cls, k).eq(v)

        return self

    def exclude(self, *expressions):
        for expression in expressions:
            self._spec &= ~expression

        return self

    def exclude_by(self, **kwargs):
        for k, v in kwargs.iteritems():
            self._spec &= ~getattr(self._document_cls, k).eq(v)

        return self

    def one(self):
        return self._eagerload(self._document_cls.to_python(self._one()))

    def _one(self):
        return self._collection.find_one(self._compile_spec(), fields=self._fields)

    def first(self, *expressions):
        try:
            return self.filter(*expressions)[0]
        except IndexError:
            raise DoesNotExist()

    def all(self):
        return list(self)

    def with_id(self, object_id):
        return self.filter_by(id=self._document_cls.id.to_mongo(object_id)).one()

    def in_bulk(self, object_ids):
        field = self._document_cls.id
        return  dict([(doc.id, doc) for doc in self.filter(field.in_(map(field.to_mongo, object_ids)))])

    def next(self):
        try:
            obj = self._document_cls.to_python(self._cursor.next())

            if not obj:
                return self.next()

            return obj
        except StopIteration, e:
            self.rewind()
            raise e

    def rewind(self):
        self._cursor.rewind()
        return self

    def count(self):
        return self._cursor.count(with_limit_and_skip=True)

    def __len__(self):
        return self.count()

    def limit(self, n):
        self._cursor.limit(n)
        return self

    def skip(self, n):
        self._cursor.skip(n)
        return self

    def __getitem__(self, key):
        if isinstance(key, slice):
            self._pymongo_cursor = self._cursor[key]
            return self
        elif isinstance(key, int):
            return self._eagerload(self._document_cls.to_python(self._cursor[key]))

    def only(self, *exprs):
        self._fields = {'_cls': 1}

        for expr in exprs:
            if isinstance(expr, basestring):
                try:
                    field = lookup_field(self._document_cls, expr)
                    self._fields[field.get_key(False)] = 1
                except KeyError:
                    self._fields[expr] = 1
            elif isinstance(expr, Slice):
                self._fields.update(expr.compile())
            else:
                self._fields[expr.get_key(False)] = 1

        return self

    def sort(self, key_list):
        if self._pymongo_cursor is None:
            self._deferred_sort.append(key_list)
        else:
            self._cursor.sort(self._transform_key_list(key_list))

        return self

    def explain(self, pretty=False):
        plan = self._cursor.explain()

        if pretty:
            plan = pprint.pformat(plan)

        return plan

    def delete(self, safe=False):
        return self._collection.remove(self._compile_spec(), safe=safe)

    def _update(self, update, safe, upsert, multi):
        try:
            self._collection.update(self._compile_spec(), update, safe=safe, upsert=upsert, multi=multi)
        except pymongo.errors.OperationFailure, err:
            raise OperationError(unicode(err))

    def update(self, update_spec, safe=False):
        self._update(update_spec.compile(), safe, False, True)

    def update_one(self, update_spec, safe=False):
        self._update(update_spec.compile(), safe, False, False)

    def upsert(self, update_spec, safe=False):
        self._update(update_spec.compile(), safe, True, False)

    def group(self, key, initial, reduce, finalize=None):
        return self._collection.group(key, self._compile_spec(), initial, reduce, finalize)

    def __iter__(self):
        if self._eagerloads:
            documents = []

            for obj in self._cursor:
                document = self._document_cls.to_python(obj)

                if document:
                    documents.append(document)

            return self._eagerload(documents).__iter__()

        return self

    @property
    def _cursor(self):
        if self._pymongo_cursor is None:
            self._pymongo_cursor = self._collection.find(self._compile_spec(), fields=self._fields)

            for key_list in self._deferred_sort:
                self.sort(key_list)

        return self._pymongo_cursor

    def __repr__(self):
        return self._spec.__repr__()

    def search(self, *args, **kwargs):
        if self._document_cls._search_index is not None:
            return self._document_cls._search_index.search(*args, **kwargs)

        raise AttributeError()
<commit_msg>Update query.py<commit_after>from .connection import connect
from .spec import QuerySpecification, Slice
from .exceptions import DoesNotExist, OperationError
from .eagerload import Eagerload
from .utils import lookup_field
import copy
import pymongo
import pymongo.errors
import pprint
import re

class Manager(object):
    def __init__(self):
        self._collection = None

    def __get__(self, instance, owner):
        if instance is not None:
            return self

        if self._collection is None:
            db = connect(owner._meta['db'])
            self._collection = db[owner._meta['collection']]

        return Query(owner, self._collection)

class Query(object):
    def __init__(self, document_cls, collection):
        self._document_cls = document_cls
        self._collection = collection
        self._spec = QuerySpecification(None)
        self._pymongo_cursor = None
        self._fields = None
        self._eagerloads = []
        self._deferred_sort = []

    def clone(self):
        q = Query(self._document_cls, self._collection)
        q._spec = self._spec.clone()
        q._fields = copy.deepcopy(self._fields)
        q._eagerloads = copy.deepcopy(self._eagerloads)
        q._deferred_sort = copy.deepcopy(self._deferred_sort)
        return q

    def _compile_spec(self):
        spec = self._spec.compile()

        if self._document_cls._superclasses:
            spec['_cls'] = re.compile('^' + self._document_cls._name)

        return spec

    def _transform_key_list(self, keys):
        transformed_keys = []

        for key in keys.split():
            direction = pymongo.ASCENDING

            if key[0] == '-':
                direction = pymongo.DESCENDING

            if key[0] in ('-', '+'):
                key = key[1:]

            field = lookup_field(self._document_cls, key)

            transformed_keys.append((field.get_key(False), direction))

        return transformed_keys

    def eagerload(self, *fields, **kwargs):
        eagerload = Eagerload(kwargs.get('only'))
        map(eagerload.add_field, fields)
        self._eagerloads.append(eagerload)
        return self

    def _eagerload(self, obj):
        if obj:
            for eagerload in self._eagerloads:
                eagerload.add_documents(obj)
                eagerload.flush()

        return obj

    def hint(self, key_or_list):
        index = self._transform_key_list(key_or_list)
        self._cursor.hint(index)
        return self

    def ensure_index(self, key_or_list):
        indexes = self._transform_key_list(key_or_list)
        self._collection.ensure_index(indexes)
        return self

    def find(self, *expressions):
        if len(expressions) == 1 and isinstance(expressions[0], dict):
            return self._collection.find(expressions[0], fields=self._fields)

        return self.filter(*expressions)._collection.find(self._compile_spec(), fields=self._fields)

    def find_one(self, *expressions):
        if len(expressions) == 1 and isinstance(expressions[0], dict):
            return self._collection.find_one(expressions[0], fields=self._fields)

        return self.filter(*expressions)._collection.find_one(self._compile_spec(), fields=self._fields)

    def filter(self, *expressions):
        for expression in expressions:
            self._spec &= expression

        return self

    def filter_by(self, **kwargs):
        for k, v in kwargs.iteritems():
            self._spec &= getattr(self._document_cls, k).eq(v)

        return self

    def exclude(self, *expressions):
        for expression in expressions:
            self._spec &= ~expression

        return self

    def exclude_by(self, **kwargs):
        for k, v in kwargs.iteritems():
            self._spec &= ~getattr(self._document_cls, k).eq(v)

        return self

    def one(self):
        return self._eagerload(self._document_cls.to_python(self._one()))

    def _one(self):
        return self._collection.find_one(self._compile_spec(), fields=self._fields)

    def first(self, *expressions):
        try:
            return self.filter(*expressions)[0]
        except IndexError:
            raise DoesNotExist()

    def all(self):
        return [doc for doc in self]

    def with_id(self, object_id):
        return self.filter_by(id=self._document_cls.id.to_mongo(object_id)).one()

    def in_bulk(self, object_ids):
        field = self._document_cls.id
        return  dict([(doc.id, doc) for doc in self.filter(field.in_(map(field.to_mongo, object_ids)))])

    def next(self):
        try:
            obj = self._document_cls.to_python(self._cursor.next())

            if not obj:
                return self.next()

            return obj
        except StopIteration, e:
            self.rewind()
            raise e

    def rewind(self):
        self._cursor.rewind()
        return self

    def count(self):
        return self._cursor.count(with_limit_and_skip=True)

    def __len__(self):
        return self.count()

    def limit(self, n):
        self._cursor.limit(n)
        return self

    def skip(self, n):
        self._cursor.skip(n)
        return self

    def __getitem__(self, key):
        if isinstance(key, slice):
            self._pymongo_cursor = self._cursor[key]
            return self
        elif isinstance(key, int):
            return self._eagerload(self._document_cls.to_python(self._cursor[key]))

    def only(self, *exprs):
        self._fields = {'_cls': 1}

        for expr in exprs:
            if isinstance(expr, basestring):
                try:
                    field = lookup_field(self._document_cls, expr)
                    self._fields[field.get_key(False)] = 1
                except KeyError:
                    self._fields[expr] = 1
            elif isinstance(expr, Slice):
                self._fields.update(expr.compile())
            else:
                self._fields[expr.get_key(False)] = 1

        return self

    def sort(self, key_list):
        if self._pymongo_cursor is None:
            self._deferred_sort.append(key_list)
        else:
            self._cursor.sort(self._transform_key_list(key_list))

        return self

    def explain(self, pretty=False):
        plan = self._cursor.explain()

        if pretty:
            plan = pprint.pformat(plan)

        return plan

    def delete(self, safe=False):
        return self._collection.remove(self._compile_spec(), safe=safe)

    def _update(self, update, safe, upsert, multi):
        try:
            self._collection.update(self._compile_spec(), update, safe=safe, upsert=upsert, multi=multi)
        except pymongo.errors.OperationFailure, err:
            raise OperationError(unicode(err))

    def update(self, update_spec, safe=False):
        self._update(update_spec.compile(), safe, False, True)

    def update_one(self, update_spec, safe=False):
        self._update(update_spec.compile(), safe, False, False)

    def upsert(self, update_spec, safe=False):
        self._update(update_spec.compile(), safe, True, False)

    def group(self, key, initial, reduce, finalize=None):
        return self._collection.group(key, self._compile_spec(), initial, reduce, finalize)

    def __iter__(self):
        if self._eagerloads:
            documents = []

            for obj in self._cursor:
                document = self._document_cls.to_python(obj)

                if document:
                    documents.append(document)

            return self._eagerload(documents).__iter__()

        return self

    @property
    def _cursor(self):
        if self._pymongo_cursor is None:
            self._pymongo_cursor = self._collection.find(self._compile_spec(), fields=self._fields)

            for key_list in self._deferred_sort:
                self.sort(key_list)

        return self._pymongo_cursor

    def __repr__(self):
        return self._spec.__repr__()

    def search(self, *args, **kwargs):
        if self._document_cls._search_index is not None:
            return self._document_cls._search_index.search(*args, **kwargs)

        raise AttributeError()
<|endoftext|>


<commit_before>#!/usr/bin/env python3

import sys
from sys import stderr

from os import path
from optparse import OptionParser
from math import log

__all__ = ('export_img')

GEN_IMG_FORMATS = ('.gif', '.jpeg', '.jpg', '.png')
VISIO_FORMATS = ('.vsd', 'vsdx')

def is_pywin32_available():
    try:
        import win32com  # NOQA: import test
        return True
    except ImportError:
        return False

class FileNotFoundError(Exception):
    """
    exception represents the input file is not found
    """

class IllegalImageFormatException(TypeError):

    """
    This exception means Exceptions for Illegal Image Format.
    """

class VisioNotFoundException(Exception):

    """
    This excetion means system has no visio program.
    """

def _get_pages(app, page_num=None):
    """
    app -> page
    if page_num is None, return all pages.
    if page_num is int object, return path_num-th page(from 1).
    """
    pages = app.ActiveDocument.Pages
    try:
        return [list(pages)[page_num - 1]] if page_num else pages
    except IndexError:
        raise IndexError('This file has no {}-th page.'.format(page_num))

def _check_format(visio_filename, gen_img_filename):
    visio_extension = path.splitext(visio_filename)[1]
    gen_img_extension = path.splitext(gen_img_filename)[1]
    if visio_extension not in VISIO_FORMATS:
        err_str = (
            'Input filename is not llegal for visio file. \n'
            'This program is suppert only vsd extension.'
        )
        raise IllegalImageFormatException(err_str)

    if gen_img_extension not in GEN_IMG_FORMATS:
        err_str = (
            'Output filename is not llegal for visio file. \n'
            'This program is suppert gif, jpeg, png extension.'
        )
        raise IllegalImageFormatException(err_str)

def export_img(visio_filename, gen_img_filename,
               page_num=None, page_name=None):
    """
    export as image format
    If exported page, return True and else return False.
    """
    import win32com.client
    from pywintypes import com_error

    # to absolute path
    visio_filename = path.abspath(visio_filename)
    gen_img_filename = path.abspath(gen_img_filename)

    # define filename without extension and extension variable
    gen_img_filename_without_extension, gen_img_extension = (
        path.splitext(gen_img_filename))
    _check_format(visio_filename, gen_img_filename)

    # if file is not found, exit from program
    if not path.exists(visio_filename):
        raise FileNotFoundError('Input File is not found.')

    gen_img_dir_name = path.dirname(gen_img_filename)
    if not path.isdir(gen_img_dir_name):
        raise FileNotFoundError('Directory of Output File is not found')

    try:
        # make instance for visio
        _, visio_extension = path.splitext(visio_filename)
        application = win32com.client.Dispatch('Visio.InvisibleApp')

        # case: system has no visio
        if application is None:
            raise VisioNotFoundException('System has no Visio.')

        application.Visible = False
        application.Documents.Open(visio_filename)

        # make pages of picture
        pages = _get_pages(application, page_num=page_num)

        # filter of page names
        if page_name is not None:
            # generator of page and page names
            page_with_names = zip(pages, pages.GetNames())
            page_list = list(filter(
                lambda pn: pn[1] == page_name,
                page_with_names))
            pages = [p_w_n[0] for p_w_n in page_list]

        # define page_names
        if len(pages) == 1:
            page_names = [gen_img_filename]
        else:   # len(pages) >= 2
            figure_length = int(log(len(pages), 10)) + 1
            page_names = (
                (gen_img_filename_without_extension +
                 ("{0:0>" + str(figure_length) + "}").format(page_cnt + 1) +
                 gen_img_extension
                 for page_cnt in range(len(pages))))
        # Export pages
        for page, page_name in zip(pages, page_names):
            page.Export(page_name)
        if list(pages) == []:
            return False
        return True  # pages is not empty
    except com_error:
        raise IllegalImageFormatException(
            'Output filename is not llegal for Image File.')
    finally:
        application.Quit()

def main(args=sys.argv[1:]):
    # define parser
    parser = OptionParser()
    parser.add_option(
        '-p', '--page',
        action='store',
        type='int',
        dest='page',
        help='transform only one page(set number of this page)'
    )
    parser.add_option(
        '-n', '--name',
        action='store',
        type='string',
        dest='page_name',
        help='transform only same as setted name page'
    )
    (options, argv) = parser.parse_args(args)

    if (options.page is not None) and (options.page_name is not None):
        stderr.write('page and page name option is appointed.')
        return -1

    # if len(arguments) != 2, raise exception
    if len(argv) != 2:
        stderr.write('Enter Only input_filename and output_filename')
        return -1

    if not is_pywin32_available():
        stderr.write('win32com module not found')
        return -1

    # define input_filename and output_filename
    visio_filename = argv[0]
    gen_img_filename = argv[1]

    try:
        is_exported = export_img(visio_filename, gen_img_filename,
                                 page_num=options.page,
                                 page_name=options.page_name)
        if is_exported is False:
            stderr.write("No page Output")
            return -1

        return 0
    except (FileNotFoundError, IllegalImageFormatException, IndexError) as err:
        # expected exception
        stderr.write(str(err))  # print message
        return -1
    except Exception as err:
        print('Error')
        return -1
<commit_msg>Support .vsdx file (fix typo)<commit_after>#!/usr/bin/env python3

import sys
from sys import stderr

from os import path
from optparse import OptionParser
from math import log

__all__ = ('export_img')

GEN_IMG_FORMATS = ('.gif', '.jpeg', '.jpg', '.png')
VISIO_FORMATS = ('.vsd', '.vsdx')

def is_pywin32_available():
    try:
        import win32com  # NOQA: import test
        return True
    except ImportError:
        return False

class FileNotFoundError(Exception):
    """
    exception represents the input file is not found
    """

class IllegalImageFormatException(TypeError):

    """
    This exception means Exceptions for Illegal Image Format.
    """

class VisioNotFoundException(Exception):

    """
    This excetion means system has no visio program.
    """

def _get_pages(app, page_num=None):
    """
    app -> page
    if page_num is None, return all pages.
    if page_num is int object, return path_num-th page(from 1).
    """
    pages = app.ActiveDocument.Pages
    try:
        return [list(pages)[page_num - 1]] if page_num else pages
    except IndexError:
        raise IndexError('This file has no {}-th page.'.format(page_num))

def _check_format(visio_filename, gen_img_filename):
    visio_extension = path.splitext(visio_filename)[1]
    gen_img_extension = path.splitext(gen_img_filename)[1]
    if visio_extension not in VISIO_FORMATS:
        err_str = (
            'Input filename is not llegal for visio file. \n'
            'This program is suppert only vsd extension.'
        )
        raise IllegalImageFormatException(err_str)

    if gen_img_extension not in GEN_IMG_FORMATS:
        err_str = (
            'Output filename is not llegal for visio file. \n'
            'This program is suppert gif, jpeg, png extension.'
        )
        raise IllegalImageFormatException(err_str)

def export_img(visio_filename, gen_img_filename,
               page_num=None, page_name=None):
    """
    export as image format
    If exported page, return True and else return False.
    """
    import win32com.client
    from pywintypes import com_error

    # to absolute path
    visio_filename = path.abspath(visio_filename)
    gen_img_filename = path.abspath(gen_img_filename)

    # define filename without extension and extension variable
    gen_img_filename_without_extension, gen_img_extension = (
        path.splitext(gen_img_filename))
    _check_format(visio_filename, gen_img_filename)

    # if file is not found, exit from program
    if not path.exists(visio_filename):
        raise FileNotFoundError('Input File is not found.')

    gen_img_dir_name = path.dirname(gen_img_filename)
    if not path.isdir(gen_img_dir_name):
        raise FileNotFoundError('Directory of Output File is not found')

    try:
        # make instance for visio
        _, visio_extension = path.splitext(visio_filename)
        application = win32com.client.Dispatch('Visio.InvisibleApp')

        # case: system has no visio
        if application is None:
            raise VisioNotFoundException('System has no Visio.')

        application.Visible = False
        application.Documents.Open(visio_filename)

        # make pages of picture
        pages = _get_pages(application, page_num=page_num)

        # filter of page names
        if page_name is not None:
            # generator of page and page names
            page_with_names = zip(pages, pages.GetNames())
            page_list = list(filter(
                lambda pn: pn[1] == page_name,
                page_with_names))
            pages = [p_w_n[0] for p_w_n in page_list]

        # define page_names
        if len(pages) == 1:
            page_names = [gen_img_filename]
        else:   # len(pages) >= 2
            figure_length = int(log(len(pages), 10)) + 1
            page_names = (
                (gen_img_filename_without_extension +
                 ("{0:0>" + str(figure_length) + "}").format(page_cnt + 1) +
                 gen_img_extension
                 for page_cnt in range(len(pages))))
        # Export pages
        for page, page_name in zip(pages, page_names):
            page.Export(page_name)
        if list(pages) == []:
            return False
        return True  # pages is not empty
    except com_error:
        raise IllegalImageFormatException(
            'Output filename is not llegal for Image File.')
    finally:
        application.Quit()

def main(args=sys.argv[1:]):
    # define parser
    parser = OptionParser()
    parser.add_option(
        '-p', '--page',
        action='store',
        type='int',
        dest='page',
        help='transform only one page(set number of this page)'
    )
    parser.add_option(
        '-n', '--name',
        action='store',
        type='string',
        dest='page_name',
        help='transform only same as setted name page'
    )
    (options, argv) = parser.parse_args(args)

    if (options.page is not None) and (options.page_name is not None):
        stderr.write('page and page name option is appointed.')
        return -1

    # if len(arguments) != 2, raise exception
    if len(argv) != 2:
        stderr.write('Enter Only input_filename and output_filename')
        return -1

    if not is_pywin32_available():
        stderr.write('win32com module not found')
        return -1

    # define input_filename and output_filename
    visio_filename = argv[0]
    gen_img_filename = argv[1]

    try:
        is_exported = export_img(visio_filename, gen_img_filename,
                                 page_num=options.page,
                                 page_name=options.page_name)
        if is_exported is False:
            stderr.write("No page Output")
            return -1

        return 0
    except (FileNotFoundError, IllegalImageFormatException, IndexError) as err:
        # expected exception
        stderr.write(str(err))  # print message
        return -1
    except Exception as err:
        print('Error')
        return -1
<|endoftext|>


<commit_before>#-*-coding:utf-8-*-

from flask import Flask
from flask_weixin import Weixin

from beijing_bus import BeijingBus

app = Flask(__name__)
app.config.from_object('config')

weixin = Weixin(app)
app.add_url_rule('/weixin', view_func=weixin.view_func)

if app.config.get('SENTRY_DSN'):
    from raven.contrib.flask import Sentry
    sentry = Sentry(app)

QUERY_EXAMPLE = '查询示例： 从西坝河到将台路口西'

@weixin.register('*')
def query(**kwargs):
    username = kwargs.get('sender')
    sender = kwargs.get('receiver')
    message_type = kwargs.get('type')

    def r(content):
        return weixin.reply(
            username, sender=sender, content=content
        )

    if message_type == 'event' and kwargs.get('event') == 'subscribe':
        reply = ('欢迎关注北京实时公交!\n'
                 '你可以通过向我发送消息查询公交实时到站时间。\n\n%s'
                ) % QUERY_EXAMPLE
        return r(reply)

    content = kwargs.get('content')
    if not content:
        reply = '我好笨笨哦，还不懂你在说什么。\n%s' % QUERY_EXAMPLE
        return r(reply)

    if isinstance(content, unicode):
        content = content.encode('utf-8')

    stations = BeijingBus.extract_stations(content)
    lines = BeijingBus.extract_lines(content)
    if len(stations) < 2:
        reply = '没有结果，可能还不支持这条线路呢~ \n%s' % QUERY_EXAMPLE
        return r(reply)

    from_station, to_station = stations[:2]
    lines = match_stations_with_lines(from_station, to_station, lines)
    if not lines:
        reply = '没有结果，可能还不支持这条线路呢~ \n%s' % QUERY_EXAMPLE
        return r(reply)

    reply = get_realtime_message(lines, from_station)
    return r(reply)

def match_stations_with_lines(from_station, to_station, lines=None):

    def match(a, b, L):
        '''检查L中包含a和b且a比b靠前'''
        try:
            return L.index(a) < L.index(b)
        except ValueError:
            return False

    if not lines:
        lines = BeijingBus.get_all_lines()

    return [
        line for line in lines
             if match(from_station, to_station, line.stations)
    ]

def get_realtime_message(lines, station):
    realtime_datas = []
    for line in lines:
        for data in line.get_realtime_data(station):
            if data.get('station_arriving_time'):
                realtime_datas.append((line, data))
    realtime_datas.sort(key=lambda d: d[1]['station_arriving_time'])

    if not realtime_datas:
        return '暂时还没有车要来呢 T T'

    reply = ''
    for i, (line, data) in enumerate(realtime_datas[:6]):
        reply += '车辆%s：%s\n' % (i+1, line.short_name)
        reply += '距离%s还有 %s米，' % (station.name, int(data['station_distance']))
        reply += '预计%s到达\n\n' % data['station_arriving_time'].strftime('%H:%M')
    return reply.strip()

if __name__ == '__main__':
    app.run(debug=True, port=8484)
<commit_msg>show available list<commit_after>#-*-coding:utf-8-*-

from flask import Flask
from flask_weixin import Weixin

from beijing_bus import BeijingBus

app = Flask(__name__)
app.config.from_object('config')

weixin = Weixin(app)
app.add_url_rule('/weixin', view_func=weixin.view_func)

if app.config.get('SENTRY_DSN'):
    from raven.contrib.flask import Sentry
    sentry = Sentry(app)

QUERY_EXAMPLE = '查询示例： 从西坝河到将台路口西'

@weixin.register('*')
def query(**kwargs):
    username = kwargs.get('sender')
    sender = kwargs.get('receiver')
    message_type = kwargs.get('type')

    def r(content):
        return weixin.reply(
            username, sender=sender, content=content
        )

    if message_type == 'event' and kwargs.get('event') == 'subscribe':
        reply = ('欢迎关注北京实时公交!\n'
                 '你可以通过向我发送消息查询公交实时到站时间。\n\n%s'
                ) % QUERY_EXAMPLE
        return r(reply)

    content = kwargs.get('content')
    if not content:
        reply = '我好笨笨哦，还不懂你在说什么。\n%s' % QUERY_EXAMPLE
        return r(reply)

    if isinstance(content, unicode):
        content = content.encode('utf-8')

    stations = BeijingBus.extract_stations(content)
    lines = BeijingBus.extract_lines(content)
    if len(stations) < 2:
        reply = '没有结果，可能还不支持这条线路呢~ \n%s' % QUERY_EXAMPLE
        return r(reply)

    from_station, to_station = stations[:2]
    lines = match_stations_with_lines(from_station, to_station, lines)
    if not lines:
        reply = '没有结果，可能还不支持这条线路呢~ \n%s' % QUERY_EXAMPLE
        return r(reply)

    reply = get_realtime_message(lines, from_station)
    return r(reply)

def match_stations_with_lines(from_station, to_station, lines=None):

    def match(a, b, L):
        '''检查L中包含a和b且a比b靠前'''
        try:
            return L.index(a) < L.index(b)
        except ValueError:
            return False

    if not lines:
        lines = BeijingBus.get_all_lines()

    return [
        line for line in lines
             if match(from_station, to_station, line.stations)
    ]

def get_realtime_message(lines, station):
    realtime_datas = []
    for line in lines:
        for data in line.get_realtime_data(station):
            if data.get('station_arriving_time'):
                realtime_datas.append((line, data))
    realtime_datas.sort(key=lambda d: d[1]['station_arriving_time'])

    if not realtime_datas:
        return '暂时还没有车要来呢 T T'

    reply = ''
    for i, (line, data) in enumerate(realtime_datas[:6]):
        reply += '车辆%s：%s\n' % (i+1, line.short_name)
        reply += '距离%s还有 %s米，' % (station.name, int(data['station_distance']))
        reply += '预计%s到达\n\n' % data['station_arriving_time'].strftime('%H:%M')
    return reply.strip()

@app.route('/list')
def list_supported_lines():
    names = set([
        line.short_name for line in BeijingBus.get_all_lines()
    ])
    names.sort()
    return ''.join(['<p>%s</p>' % name for name in names])

if __name__ == '__main__':
    app.run(debug=True, port=8484)
<|endoftext|>


<commit_before>from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

setup(
    name = '_nmsg',
    ext_modules = [
        Extension('_nmsg', ['nmsg.pyx'],
            libraries = ['nmsg'],
            library_dirs = ['../nmsg/.libs'],
            include_dirs = ['../nmsg', '../protobuf-c', '..']
        )
    ],
    cmdclass = {'build_ext': build_ext},
)

setup(
    name = 'nmsg',
    py_modules = ['nmsg'],
)
<commit_msg>pynmsg/setup.py: nmsg.pyx -> _nmsg.pyx<commit_after>from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

setup(
    name = '_nmsg',
    ext_modules = [
        Extension('_nmsg', ['_nmsg.pyx'],
            libraries = ['nmsg'],
            library_dirs = ['../nmsg/.libs'],
            include_dirs = ['../nmsg', '../protobuf-c', '..']
        )
    ],
    cmdclass = {'build_ext': build_ext},
)

setup(
    name = 'nmsg',
    py_modules = ['nmsg'],
)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

# vim: tabstop=4 shiftwidth=4 softtabstop=4

#    Copyright (C) 2013 Rackspace Hosting Inc. All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import traceback as tb

from celery.signals import task_failure, task_success
from nova.openstack.common import log as logging

LOG = logging.getLogger(__name__)

@task_failure.connect
def task_error_handler(signal=None, sender=None, task_id=None,
                       exception=None, args=None, kwargs=None,
                       traceback=None, einfo=None):
    """ If a task errors out, log all error info """
    LOG.error('Task %s, id: %s, called with args: %s, and kwargs: %s'
              'failed with exception: %s' % (sender.name, task_id,
                                             args, kwargs, exception))
    LOG.error('Trackeback: %s' % (tb.print_tb(traceback), ))
    wf = sender.name.split('.')[0]
    task = ('.').join(n for n in (sender.name.split('.')[1:]) if n)
    #logbook.update_task(wf, task, status="ERROR", args=args, kwargs=kwargs,
    #                    exception=exception, traceback=(tb.print_tb(traceback)))

    # TODO: Auto-initiate rollback from failed task

@task_success.connect
def task_success_handler(singal=None, sender=None, result=None):
    """ Save task results to WF """
    wf = sender.name.split('.')[0]
    task = ('.').join(n for n in (sender.name.split('.')[1:]) if n)
    #logbook.update_task(wf, task, status="SUCCESS", result=result)

<commit_msg>More import fixups.<commit_after># -*- coding: utf-8 -*-

# vim: tabstop=4 shiftwidth=4 softtabstop=4

#    Copyright (C) 2013 Rackspace Hosting Inc. All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import logging
import traceback as tb

from celery.signals import task_failure, task_success

LOG = logging.getLogger(__name__)

@task_failure.connect
def task_error_handler(signal=None, sender=None, task_id=None,
                       exception=None, args=None, kwargs=None,
                       traceback=None, einfo=None):
    """ If a task errors out, log all error info """
    LOG.error('Task %s, id: %s, called with args: %s, and kwargs: %s'
              'failed with exception: %s' % (sender.name, task_id,
                                             args, kwargs, exception))
    LOG.error('Trackeback: %s' % (tb.print_tb(traceback), ))
    wf = sender.name.split('.')[0]
    task = ('.').join(n for n in (sender.name.split('.')[1:]) if n)
    #logbook.update_task(wf, task, status="ERROR", args=args, kwargs=kwargs,
    #                    exception=exception, traceback=(tb.print_tb(traceback)))

    # TODO: Auto-initiate rollback from failed task

@task_success.connect
def task_success_handler(singal=None, sender=None, result=None):
    """ Save task results to WF """
    wf = sender.name.split('.')[0]
    task = ('.').join(n for n in (sender.name.split('.')[1:]) if n)
    #logbook.update_task(wf, task, status="SUCCESS", result=result)

<|endoftext|>


<commit_before>from ..halite.helpers import ReadOnlyDict

class Observation(ReadOnlyDict[str, any]):
    """
    Observation primarily used as a helper to construct the State from the raw observation.
    This provides bindings for the observation type described at https://github.com/Kaggle/kaggle-environments/blob/master/kaggle_environments/envs/rps/rps.json
    """
    @property
    def last_opponent_action(self) -> int:
        """Move the opponent took on the last turn."""
        return self["halite"]

    @property
    def step(self) -> int:
        """The current step index within the episode."""
        return self["step"]

class Configuration(ReadOnlyDict[str, any]):
    """
    Configuration provides access to tunable parameters in the environment.
    This provides bindings for the configuration type described at https://github.com/Kaggle/kaggle-environments/blob/master/kaggle_environments/envs/rps/rps.json
    """
    @property
    def episode_steps(self) -> int:
        """Total number of steps in the episode."""
        return self["episodeSteps"]

    @property
    def signs(self) -> int:
        """Maximum runtime (seconds) to initialize an agent."""
        return self["agentTimeout"]

    @property
    def act_timeout(self) -> float:
        """Maximum runtime (seconds) to obtain an action from an agent."""
        return self["actTimeout"]

    @property
    def run_timeout(self) -> float:
        """Maximum runtime (seconds) of an episode (not necessarily DONE)."""
        return self["runTimeout"]
<commit_msg>Fix Configuration.signs in rps/helpers.py (#112)<commit_after>from ..halite.helpers import ReadOnlyDict

class Observation(ReadOnlyDict[str, any]):
    """
    Observation primarily used as a helper to construct the State from the raw observation.
    This provides bindings for the observation type described at https://github.com/Kaggle/kaggle-environments/blob/master/kaggle_environments/envs/rps/rps.json
    """
    @property
    def last_opponent_action(self) -> int:
        """Move the opponent took on the last turn."""
        return self["halite"]

    @property
    def step(self) -> int:
        """The current step index within the episode."""
        return self["step"]

class Configuration(ReadOnlyDict[str, any]):
    """
    Configuration provides access to tunable parameters in the environment.
    This provides bindings for the configuration type described at https://github.com/Kaggle/kaggle-environments/blob/master/kaggle_environments/envs/rps/rps.json
    """
    @property
    def episode_steps(self) -> int:
        """Total number of steps in the episode."""
        return self["episodeSteps"]

    @property
    def signs(self) -> int:
        """Number of choices each step (3 for the normal rock, paper, scissors)"""
        return self["signs"]

    @property
    def act_timeout(self) -> float:
        """Maximum runtime (seconds) to obtain an action from an agent."""
        return self["actTimeout"]

    @property
    def run_timeout(self) -> float:
        """Maximum runtime (seconds) of an episode (not necessarily DONE)."""
        return self["runTimeout"]
<|endoftext|>