<commit_before># Copyright 2009 Google 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.

"""Tk user interface implementation for namebench."""

__author__ = 'tstromberg@google.com (Thomas Stromberg)'

import os
import sys
import threading
from Tkinter import *

import base_ui
import history_parser
import util

def closedWindowHandler():
  print 'Au revoir, mes amis!'
  sys.exit(1)

class WorkerThread(threading.Thread, base_ui.BaseUI):
  """Handle benchmarking and preparation in a separate UI thread."""

  def __init__(self, primary, secondary, options, status_callback=None,
               runstate_callback=None, history_parser=None):
    self.UpdateStatus('created workerthread')
    threading.Thread.__init__(self)
    self.runstate_callback = runstate_callback
    self.status_callback = status_callback
    self.hparser = history_parser
    self.primary = primary
    self.secondary = secondary
    self.options = options
    self.resource_dir = os.path.dirname(os.path.dirname(__file__))

  def run(self):
    if self.runstate_callback:
      self.runstate_callback(running=True)
    self.PrepareBenchmark()
    self.UpdateStatus('Here comes the pain...')
    self.RunBenchmark()
    if self.runstate_callback:
      self.runstate_callback(running=False)

class NameBenchGui(Frame, base_ui.BaseUI):
  """The main Tk GUI class."""

  def __init__(self, options, supplied_ns, global_ns, regional_ns, version=None):
    self.options = options
    self.supplied_ns = supplied_ns
    self.global_ns = global_ns
    self.regional_ns = regional_ns
    self.version = version
    Frame.__init__(self)
    self.master.protocol('WM_DELETE_WINDOW', closedWindowHandler)

  def Execute(self):
    """Called by namebench.py, begins the UI drawing process."""
    self.DrawWindow()
    self.mainloop()

  def DrawWindow(self):
    """Draws the user interface."""
    self.nameserver_form = StringVar()
    self.status = StringVar()
    self.num_tests = IntVar()
    self.num_runs = IntVar()
    self.data_source = StringVar()
    self.selection_mode = StringVar()
    self.use_global = IntVar()
    self.broken_tk = False
    self.use_regional = IntVar()

    self.master.title("namebench")
    outer_frame = Frame(self.master)
    outer_frame.grid(row=0, padx=16, pady=16)
    inner_frame = Frame(outer_frame, relief=GROOVE, bd=2, padx=12, pady=12)
    inner_frame.grid(row=0, columnspan=2)
    Label(inner_frame, text="Nameservers").grid(row=0, columnspan=2, sticky=W)

    nameservers = Entry(inner_frame, bg="white", textvariable=self.nameserver_form, width=65)
    nameservers.grid(row=1, columnspan=2, sticky=W)
    self.nameserver_form.set(', '.join(util.InternalNameServers()))

    global_button = Checkbutton(inner_frame, text="Include global DNS providers (OpenDNS, UltraDNS)", variable=self.use_global)
    global_button.grid(row=2, columnspan=2, sticky=W)
    global_button.toggle()

    regional_button = Checkbutton(inner_frame, text="Include best available regional DNS services", variable=self.use_regional)
    regional_button.grid(row=3, columnspan=2, sticky=W)
    regional_button.toggle()

    separator = Frame(inner_frame, height=2, width=515, bd=1, relief=SUNKEN)
    separator.grid(row=4, padx=5, pady=5, columnspan=2)

    Label(inner_frame, text="Benchmark Data Source").grid(row=5, column=0, sticky=W)
    Label(inner_frame, text="Number of tests").grid(row=5, column=1, sticky=W)

    self.DiscoverSources()
    source_titles = [history_parser.sourceToTitle(x) for x in self.sources]
    data_source = OptionMenu(inner_frame, self.data_source, *source_titles)
    data_source.configure(width=35)
    data_source.grid(row=6, column=0, sticky=W)
    self.data_source.set(source_titles[0])

    num_tests = Entry(inner_frame, bg="white", textvariable=self.num_tests)
    num_tests.grid(row=6, column=1, sticky=W,)
    self.num_tests.set(self.options.test_count)

    Label(inner_frame, text="Benchmark Data Selection").grid(row=7, column=0, sticky=W)
    Label(inner_frame, text="Number of runs").grid(row=7, column=1, sticky=W)

    selection_mode = OptionMenu(inner_frame, self.selection_mode, "Weighted", "Random", "Chunk")
    selection_mode.configure(width=35)
    selection_mode.grid(row=8, column=0, sticky=W)
    self.selection_mode.set('Weighted')

    num_runs = Entry(inner_frame, bg="white", textvariable=self.num_runs)
    num_runs.grid(row=8, column=1, sticky=W)
    self.num_runs.set(self.options.run_count)

    self.button = Button(outer_frame, command=self.StartJob)
    status = Label(outer_frame, text='...', textvariable=self.status)
    status.grid(row=15, sticky=W, column=0)
    self.button.grid(row=15, sticky=E, column=1)
    self.UpdateRunState(running=True)
    self.UpdateRunState(running=False)
    self.UpdateStatus('Ready')

  def UpdateStatus(self, message, count=None, total=None, error=None):
    """Update our little status window."""

    # TODO(tstromberg): Add specific Error support
    if total and count:
      state = '%s [%s/%s]' % (message, count, total)
    elif count:
      state = '%s%s' % (message, '.' * count)
    else:
      state = message

    print "> %s" % state
    self.status.set(state[0:60])

  def UpdateRunState(self, running=True):
    # try/except blocks added to work around broken Tcl/Tk libraries
    # shipped with Fedora 11 (not thread-safe).
    # See http://code.google.com/p/namebench/issues/detail?id=23'
    if self.broken_tk:
      return

    if running:
      self.UpdateStatus('disabling button')
      try:
        self.button.config(state=DISABLED)
        self.UpdateStatus('Changing button text')
        self.button.config(text='Running')
      except TclError:
        self.broken_tk = True
        self.UpdateStatus('Unable to disable button due to broken Tk library')
      self.UpdateStatus('Running...')
    else:
      try:
        self.button.config(state=NORMAL)
        self.UpdateStatus('Changing button text')
        self.button.config(text='Start Benchmark')
      except TclError:
        pass

  def StartJob(self):
    """Events that get called when the Start button is pressed."""

    self.ProcessForm()
    thread = WorkerThread(self.primary, self.secondary, self.options,
                          history_parser=self.hparser,
                          status_callback=self.UpdateStatus,
                          runstate_callback=self.UpdateRunState)
    thread.start()

  def ProcessForm(self):
    """Read form and populate instance variables."""
    self.primary = self.supplied_ns + util.ExtractIPTuplesFromString(self.nameserver_form.get())
    print 'GLOBAL: %s' % self.use_global.get()

    if self.use_global.get():
      self.primary += self.global_ns
    if self.use_regional.get():
      self.secondary = self.regional_ns
    else:
      self.secondary = []
    self.options.run_count = self.num_runs.get()
    self.options.test_count = self.num_tests.get()
    self.options.data_source = self.ParseSourceSelection(self.data_source.get())
    self.options.select_mode = self.selection_mode.get().lower()
<commit_msg>Make the labels bold, adjust spacing for better appearance on windows<commit_after># Copyright 2009 Google 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.

"""Tk user interface implementation for namebench."""

__author__ = 'tstromberg@google.com (Thomas Stromberg)'

import os
import sys
import threading
import tkFont
from Tkinter import *

import base_ui
import history_parser
import util

def closedWindowHandler():
  print 'Au revoir, mes amis!'
  sys.exit(1)

class WorkerThread(threading.Thread, base_ui.BaseUI):
  """Handle benchmarking and preparation in a separate UI thread."""

  def __init__(self, primary, secondary, options, status_callback=None,
               runstate_callback=None, history_parser=None):
    self.UpdateStatus('created workerthread')
    threading.Thread.__init__(self)
    self.runstate_callback = runstate_callback
    self.status_callback = status_callback
    self.hparser = history_parser
    self.primary = primary
    self.secondary = secondary
    self.options = options
    self.resource_dir = os.path.dirname(os.path.dirname(__file__))

  def run(self):
    if self.runstate_callback:
      self.runstate_callback(running=True)
    self.PrepareBenchmark()
    self.UpdateStatus('Here comes the pain...')
    self.RunBenchmark()
    if self.runstate_callback:
      self.runstate_callback(running=False)

class NameBenchGui(Frame, base_ui.BaseUI):
  """The main Tk GUI class."""

  def __init__(self, options, supplied_ns, global_ns, regional_ns, version=None):
    self.options = options
    self.supplied_ns = supplied_ns
    self.global_ns = global_ns
    self.regional_ns = regional_ns
    self.version = version
    Frame.__init__(self)
    self.master.protocol('WM_DELETE_WINDOW', closedWindowHandler)

  def Execute(self):
    """Called by namebench.py, begins the UI drawing process."""
    self.DrawWindow()
    self.mainloop()

  def DrawWindow(self):
    """Draws the user interface."""
    self.nameserver_form = StringVar()
    self.status = StringVar()
    self.num_tests = IntVar()
    self.num_runs = IntVar()
    self.data_source = StringVar()
    self.selection_mode = StringVar()
    self.use_global = IntVar()
    self.broken_tk = False
    self.use_regional = IntVar()

    self.master.title("namebench")
    outer_frame = Frame(self.master)
    outer_frame.grid(row=0, padx=16, pady=16)
    inner_frame = Frame(outer_frame, relief=GROOVE, bd=2, padx=12, pady=12)
    inner_frame.grid(row=0, columnspan=2)
    status = Label(outer_frame, text='...', textvariable=self.status)
    status.grid(row=15, sticky=W, column=0)

    bold_font = tkFont.Font(font=status['font'])
    bold_font['weight'] = 'bold'

    ns_label = Label(inner_frame, text="Nameservers")
    ns_label.grid(row=0, columnspan=2, sticky=W)
    ns_label['font'] = bold_font

    nameservers = Entry(inner_frame, bg="white", textvariable=self.nameserver_form, width=65)
    nameservers.grid(row=1, columnspan=2, sticky=W, padx=4, pady=2)
    self.nameserver_form.set(', '.join(util.InternalNameServers()))

    global_button = Checkbutton(inner_frame, text="Include global DNS providers (OpenDNS, UltraDNS)", variable=self.use_global)
    global_button.grid(row=2, columnspan=2, sticky=W)
    global_button.toggle()

    regional_button = Checkbutton(inner_frame, text="Include best available regional DNS services", variable=self.use_regional)
    regional_button.grid(row=3, columnspan=2, sticky=W)
    regional_button.toggle()

    separator = Frame(inner_frame, height=2, width=515, bd=1, relief=SUNKEN)
    separator.grid(row=4, padx=5, pady=5, columnspan=2)

    ds_label = Label(inner_frame, text="Benchmark Data Source")
    ds_label.grid(row=5, column=0, sticky=W)
    ds_label['font'] = bold_font

    numtests_label = Label(inner_frame, text="Number of tests")
    numtests_label.grid(row=5, column=1, sticky=W)
    numtests_label['font'] = bold_font

    self.DiscoverSources()
    source_titles = [history_parser.sourceToTitle(x) for x in self.sources]
    data_source = OptionMenu(inner_frame, self.data_source, *source_titles)
    data_source.configure(width=35)
    data_source.grid(row=6, column=0, sticky=W)
    self.data_source.set(source_titles[0])

    num_tests = Entry(inner_frame, bg="white", textvariable=self.num_tests)
    num_tests.grid(row=6, column=1, sticky=W, padx=4)
    self.num_tests.set(self.options.test_count)

    bds_label = Label(inner_frame, text="Benchmark Data Selection")
    bds_label.grid(row=7, column=0, sticky=W)
    bds_label['font'] = bold_font

    num_runs_label = Label(inner_frame, text="Number of runs")
    num_runs_label.grid(row=7, column=1, sticky=W)
    num_runs_label['font'] = bold_font

    selection_mode = OptionMenu(inner_frame, self.selection_mode, "Weighted", "Random", "Chunk")
    selection_mode.configure(width=35)
    selection_mode.grid(row=8, column=0, sticky=W)
    self.selection_mode.set('Weighted')

    num_runs = Entry(inner_frame, bg="white", textvariable=self.num_runs)
    num_runs.grid(row=8, column=1, sticky=W, padx=4)
    self.num_runs.set(self.options.run_count)

    self.button = Button(outer_frame, command=self.StartJob)
    self.button.grid(row=15, sticky=E, column=1, pady=4, padx=1)
    self.UpdateRunState(running=True)
    self.UpdateRunState(running=False)
    self.UpdateStatus('Ready')

  def UpdateStatus(self, message, count=None, total=None, error=None):
    """Update our little status window."""

    # TODO(tstromberg): Add specific Error support
    if total and count:
      state = '%s [%s/%s]' % (message, count, total)
    elif count:
      state = '%s%s' % (message, '.' * count)
    else:
      state = message

    print "> %s" % state
    self.status.set(state[0:60])

  def UpdateRunState(self, running=True):
    # try/except blocks added to work around broken Tcl/Tk libraries
    # shipped with Fedora 11 (not thread-safe).
    # See http://code.google.com/p/namebench/issues/detail?id=23'
    if self.broken_tk:
      return

    if running:
      self.UpdateStatus('disabling button')
      try:
        self.button.config(state=DISABLED)
        self.UpdateStatus('Changing button text')
        self.button.config(text='Running')
      except TclError:
        self.broken_tk = True
        self.UpdateStatus('Unable to disable button due to broken Tk library')
      self.UpdateStatus('Running...')
    else:
      try:
        self.button.config(state=NORMAL)
        self.UpdateStatus('Changing button text')
        self.button.config(text='Start Benchmark')
      except TclError:
        pass

  def StartJob(self):
    """Events that get called when the Start button is pressed."""

    self.ProcessForm()
    thread = WorkerThread(self.primary, self.secondary, self.options,
                          history_parser=self.hparser,
                          status_callback=self.UpdateStatus,
                          runstate_callback=self.UpdateRunState)
    thread.start()

  def ProcessForm(self):
    """Read form and populate instance variables."""
    self.primary = self.supplied_ns + util.ExtractIPTuplesFromString(self.nameserver_form.get())
    print 'GLOBAL: %s' % self.use_global.get()

    if self.use_global.get():
      self.primary += self.global_ns
    if self.use_regional.get():
      self.secondary = self.regional_ns
    else:
      self.secondary = []
    self.options.run_count = self.num_runs.get()
    self.options.test_count = self.num_tests.get()
    self.options.data_source = self.ParseSourceSelection(self.data_source.get())
    self.options.select_mode = self.selection_mode.get().lower()
<|endoftext|>


<commit_before>"""Modelling of FreeBSD ports."""

from __future__ import absolute_import

from contextlib import contextmanager
from ..signal import SignalProperty

__all__ = ["Port"]

# TODO:
# Non-privleged mode
# remove NO_DEPENDS once thoroughly tested???
# handle IS_INTERACTIVE

class Lock(object):
  """A simple Uniprocessor lock."""

  def __init__(self):
    """Initialise lock."""
    self._locked = False

  def acquire(self):
    """Acquire lock."""
    from ..event import suspend

    if self._locked:
      return False
    self._locked = True
    suspend()
    return True

  def release(self):
    """Release lock."""
    from ..event import resume

    assert self._locked
    self._locked = False
    resume()

  @contextmanager
  def lock(self):
    """Create a context manager for a lock."""
    self.acquire()
    try:
      yield
    finally:
      self.release()

class FileLock(object):
  """A file lock, excludes accessing the same files from different ports."""

  def __init__(self):
    """Initialise the locks and database of files."""
    self._files = set()

  def acquire(self, files):
    """Acquire a lock for the given files."""
    if not self._files.isdisjoint(files):
      return False
    self._files.update(files)
    return True

  def release(self, files):
    """Release a lock fir the given files."""
    assert self._files.issuperset(files)

    self._files.symmetric_difference_update(files)

  @contextmanager
  def lock(self, files):
    """Create a context manager for a lock of the given files."""
    self.acquire(files)
    try:
      yield
    finally:
      self.release(files)

class Port(object):
  """A FreeBSD port class.

  Signals:
   - stage_completed(Port):     Indicates the port completed a stage
  """

  # Installed status flags
  ABSENT  = 0
  OLDER   = 1
  CURRENT = 2
  NEWER   = 3

  # Build stage status flags
  ZERO       = 0
  CONFIG     = 1
  DEPEND     = 2
  CHECKSUM   = 3
  FETCH      = 4
  BUILD      = 5
  INSTALL    = 6
  PACKAGE    = 7
  PKGINSTALL = 8

  _config_lock = Lock()
  _checksum_lock = FileLock()
  _fetch_lock = FileLock()
  _bad_checksum = set()
  _fetched = set()
  _fetch_failed = set()

  stage_completed = SignalProperty("stage_completed")

  def __init__(self, origin, attr):
    """Itialise the port with the required information."""
    from .mk import status
    from .dependhandler import Dependant

    self.attr = attr
    self.log_file = None
    self.failed = False
    self.force = False
    self.load = attr["jobs_number"]
    self.origin = origin
    self.priority = 0
    self.working = False

    self.stage = Port.ZERO
    self.install_status = status(self)

    self.dependancy = None
    self.dependant = Dependant(self)

    if not len(self.attr["options"]) or self._check_config():
      self.stage = Port.CONFIG

  def __repr__(self):
    return "<Port(%s)>" % (self.origin)

  def clean(self):
    """Clean the ports working directory any log file."""
    if self.stage >= Port.BUILD:
      assert not self.working
      from ..job import CleanJob
      from ..queue import clean_queue
      self.working = True
      clean_queue.add(CleanJob(self).connect(self._cleaned))

  def _cleaned(self, job=None):
    """Mark the port as clean."""
    self.working = False
    if job and not job.status:
      self.failed = True
    if not self.failed and self.stage >= Port.BUILD:
      from os.path import isfile
      from os import unlink

      if isfile(self.log_file):
        unlink(self.log_file)

  def build_stage(self, stage):
    """Build the requested stage."""
    from time import time
    from ..job import StalledJob

    pre_map = (self._pre_config, self._pre_depend, self._pre_checksum,
               self._pre_fetch, self._pre_build, self._pre_install,
               self._pre_package, None)

    if self.working or self.stage != stage - 1 or self.failed:
      # Don't do stage if not able to
      return False
    if self.stage >= Port.DEPEND and self.dependancy.check(stage):
      # Don't do stage if not configured
      return False

    self.working = time()
    try:
      status = pre_map[stage - 1]()
      if isinstance(status, bool):
        self._finalise(stage - 1, status)
        return True
    except StalledJob:
      self.working = False
      raise
    return status

  def _pre_config(self):
    """Configure the ports options."""
    if not self._config_lock.acquire():
      from ..job import StalledJob
      raise StalledJob()
    return self._make_target("config", pipe=False)

  def _post_config(self, _make, status):
    """Refetch attr data if ports were configured successfully."""
    self._config_lock.release()
    if status:
      from .mk import Attr

      Attr(self.origin).connect(self._load_attr).get()
      return None
    return status

  def _load_attr(self, _origin, attr):
    """Load the attributes for this port."""
    self.attr = attr
    self._finalise(self.stage, attr is not None)

  def _pre_depend(self):
    """Create a dependancy object for this port."""
    from os.path import join
    from ..env import flags
    from .dependhandler import Dependancy

    self.log_file = join(flags["log_dir"], self.attr["pkgname"])
    self.priority = self._get_priority()
    self.dependant.priority += self.priority
    depends = ('depend_build', 'depend_extract', 'depend_fetch', 'depend_lib',
               'depend_run', 'depend_patch')
    self.dependancy = Dependancy(self, [self.attr[i] for i in depends])

  def _pre_checksum(self):
    """Check if distfiles are available."""
    from os.path import join, isfile
    from ..env import flags

    if flags["no_op"]:
      return True

    distfiles = self.attr["distfiles"]
    if self._fetched.issuperset(distfiles):
      # If files are already fetched
      self.stage = Port.FETCH
      return True
    if not self._bad_checksum.isdisjoint(distfiles):
      # If some files have already failed
      return True
    distdir = self.attr["distdir"]
    for i in distfiles:
      if not isfile(flags["chroot"] + join(distdir, i)):
        # If file does not exist then it failed
        self._bad_checksum.add(i)
        return True
    if not self._checksum_lock.acquire(distfiles):
      from ..job import StalledJob
      raise StalledJob()
    else:
      return self._make_target("checksum", BATCH=True, DISABLE_CONFLICTS=True, FETCH_REGET=0)

  def _post_checksum(self, _make, status):
    """Advance to build stage if checksum passed."""
    distfiles = self.attr["distfiles"]
    self._checksum_lock.release(distfiles)
    if status:
      self._fetched.update(distfiles)
      self.stage = Port.FETCH
    else:
      self._bad_checksum.update(distfiles)
    return True

  def _pre_fetch(self):
    """Fetch the ports files."""
    distfiles = self.attr["distfiles"]
    if self._fetched.issuperset(distfiles):
      # If files are already fetched
      return True
    if self._fetch_failed.issuperset(distfiles):
      # If filfes have failed to fetch
      return False
    if not self._fetch_lock.acquire(distfiles):
      from ..job import StalledJob
      raise StalledJob()
    else:
      return self._make_target("checksum", BATCH=True, DISABLE_CONFLICTS=True, NO_DEPENDS=True)

  def _post_fetch(self, _make, status):
    """Register fetched files if fetch succeeded."""
    distfiles = self.attr["distfiles"]
    self._fetch_lock.release(distfiles)
    if status:
      self._bad_checksum.difference_update(distfiles)
      self._fetched.update(distfiles)
    else:
      self._bad_checksum.update(distfiles)
      self._fetch_failed.update(distfiles)
    return status

  def _pre_build(self):
    """Build the port."""
    return self._make_target(["clean","all"], BATCH=True, NOCLEANDEPENDS=True, NO_DEPENDS=True)

  @staticmethod
  def _post_build(_make, status):
    """Indicate build status."""
    return status

  def _pre_install(self):
    """Install the port."""
    if self.install_status == Port.ABSENT:
      target = ("install",)
    else:
      target = ("deinstall", "reinstall")
    return self._make_target(target, BATCH=True, NO_DEPENDS=True)

  def _post_install(self, _make, status):
    """Update the install status."""
    if status:
      from ..env import flags
      from .mk import status

      if flags["no_op"]:
        self.install_status = Port.CURRENT
      else:
        self.install_status = status(self, True)
    else:
      # TODO???
      self.install_status = Port.ABSENT
    return status

  def _pre_package(self):
    """Package the port,"""
    return self._make_target("package", BATCH=True)

  @staticmethod
  def _post_package(_make, status):
    """Indicate package status."""
    return status

  def pkginstall(self):
    """Prepare to install the port from it's package."""
    from os.path import isfile
    from ..env import flags
    from ..make import make_target

    if self.working or not isfile(self.attr["pkgfile"]):
      return False

    self.stage = self.PKGINSTALL - 1
    self.working = True
    if self.install_status > self.ABSENT:
      return make_target(self, "deinstall").connect(self._pkginstall)
    else:
      self._pkginstall()

  def _pkginstall(self, make=None):
    """Install the port from it's package."""
    from ..env import flags
    from ..make import SUCCESS

    if make is not None:
      status = make.wait() == SUCCESS:
      if not status:
        self.stage = self.PKGINSTALL
        self.working = False
        self.stage_completed.emit(self)
      self.dependant.state_changed()
      if not status:
        return

    if flags["chroot"]:
      args = ("pkg_add", "-C", flags["chroot"], pkgfile)
    else:
      args = ("pkg_add", pkgfile)

    if flags["noop"]:
      from ..make import PopenNone

      pkg_add = PopenNone(args, self)
    else:
      from subprocess import PIPE
      from ..make import Popen

      logfile = open(self.logfile, "a")
      pkg_add = Popen(args, self, stdin=PIPE, stdout=logfile, stderr=logfile)
      pkg_add.stdin.close()
    return pkg_add.connect(self._post_pkginstall)

  def _post_pkginstall(self, pkg_add):
    """Report if the port successfully installed from it's package."""
    from ..env import flags
    from ..make import SUCCESS
    from .mk import status

    self.working = False
    if flags["noop"]:
      success = True
      self.install_status = self.CURRENT
    else:
      success = make.wait() == SUCCESS
      if success:
        self.install_status = status(self, True)

    self.failed = not success
    self.stage = self.PKGINSTALL
    self.stage_completed.emit(self)

    self.dependant.status_changed()

  def _make_target(self, targets, **kwargs):
    """Build the requested targets."""
    from ..make import make_target

    return make_target(self, targets, **kwargs).connect(self._make)

  def _make(self, make):
    """Call the _post_[stage] function and finalise the stage."""
    from ..make import SUCCESS

    post_map = (self._post_config, None, self._post_checksum, self._post_fetch,
                self._post_build, self._post_install, self._post_package, None)
    stage = self.stage
    status = post_map[stage](make, make.wait() == SUCCESS)
    if status is not None:
      self._finalise(stage, status)

  def _finalise(self, stage, status):
    """Finalise the stage."""
    from ..env import flags

    if not status:
      self.failed = True
    self.working = False
    self.stage = max(stage + 1, self.stage)
    self.stage_completed.emit(self)
    if self.failed or self.stage >= (Port.FETCH if flags["fetch_only"] else Port.INSTALL):
      self.dependant.status_changed()

  def _check_config(self):
    """Check the options file to see if it is up-to-date."""
    from os.path import isfile
    from ..env import env, flags
    from .mk import pkg_version

    if "BATCH" in env or flags["config"] == "none":
      return True
    elif flags["config"] == "all":
      return False

    optionfile = flags["chroot"] + self.attr["optionsfile"]
    pkgname = self.attr["pkgname"]
    options = set()
    if isfile(optionfile):
      for i in open(optionfile, 'r'):
        if i.startswith('_OPTIONS_READ='):
          # The option set to the last pkgname this config file was set for
          config_pkgname = i[14:-1]
        elif i.startswith('WITH'):
          options.add(i.split('_', 1)[1].split('=', 1)[0])
    if flags["config"] == "changed" and options != set(self.attr["options"]):
      return False
    if flags["config"] == "newer" and pkg_version(pkgname, config_pkgname) == Port.NEWER:
      return False
    return True

  def _get_priority(self):
    """Get the priority of this port, based on the distfiles size."""
    from os.path import isfile
    from ..env import flags

    distfiles = self.attr["distfiles"]
    distinfo = flags["chroot"] + self.attr["distinfo"]
    if not len(distfiles) or not isfile(distinfo):
      return 0
    priority = 0
    for i in open(distinfo, 'r'):
      if i.startswith("SIZE"):
        i = i.split()
        name, size = i[1], i[-1]
        name = name[1:-1]
        name = name.split('/')[-1]
        if name in distfiles:
          priority += int(size)
    return priority
<commit_msg>Fix return value from Port.pkginstall().<commit_after>"""Modelling of FreeBSD ports."""

from __future__ import absolute_import

from contextlib import contextmanager
from ..signal import SignalProperty

__all__ = ["Port"]

# TODO:
# Non-privleged mode
# remove NO_DEPENDS once thoroughly tested???
# handle IS_INTERACTIVE

class Lock(object):
  """A simple Uniprocessor lock."""

  def __init__(self):
    """Initialise lock."""
    self._locked = False

  def acquire(self):
    """Acquire lock."""
    from ..event import suspend

    if self._locked:
      return False
    self._locked = True
    suspend()
    return True

  def release(self):
    """Release lock."""
    from ..event import resume

    assert self._locked
    self._locked = False
    resume()

  @contextmanager
  def lock(self):
    """Create a context manager for a lock."""
    self.acquire()
    try:
      yield
    finally:
      self.release()

class FileLock(object):
  """A file lock, excludes accessing the same files from different ports."""

  def __init__(self):
    """Initialise the locks and database of files."""
    self._files = set()

  def acquire(self, files):
    """Acquire a lock for the given files."""
    if not self._files.isdisjoint(files):
      return False
    self._files.update(files)
    return True

  def release(self, files):
    """Release a lock fir the given files."""
    assert self._files.issuperset(files)

    self._files.symmetric_difference_update(files)

  @contextmanager
  def lock(self, files):
    """Create a context manager for a lock of the given files."""
    self.acquire(files)
    try:
      yield
    finally:
      self.release(files)

class Port(object):
  """A FreeBSD port class.

  Signals:
   - stage_completed(Port):     Indicates the port completed a stage
  """

  # Installed status flags
  ABSENT  = 0
  OLDER   = 1
  CURRENT = 2
  NEWER   = 3

  # Build stage status flags
  ZERO       = 0
  CONFIG     = 1
  DEPEND     = 2
  CHECKSUM   = 3
  FETCH      = 4
  BUILD      = 5
  INSTALL    = 6
  PACKAGE    = 7
  PKGINSTALL = 8

  _config_lock = Lock()
  _checksum_lock = FileLock()
  _fetch_lock = FileLock()
  _bad_checksum = set()
  _fetched = set()
  _fetch_failed = set()

  stage_completed = SignalProperty("stage_completed")

  def __init__(self, origin, attr):
    """Itialise the port with the required information."""
    from .mk import status
    from .dependhandler import Dependant

    self.attr = attr
    self.log_file = None
    self.failed = False
    self.force = False
    self.load = attr["jobs_number"]
    self.origin = origin
    self.priority = 0
    self.working = False

    self.stage = Port.ZERO
    self.install_status = status(self)

    self.dependancy = None
    self.dependant = Dependant(self)

    if not len(self.attr["options"]) or self._check_config():
      self.stage = Port.CONFIG

  def __repr__(self):
    return "<Port(%s)>" % (self.origin)

  def clean(self):
    """Clean the ports working directory and log file."""
    if self.stage >= Port.BUILD:
      assert not self.working
      from ..job import CleanJob
      from ..queue import clean_queue
      self.working = True
      clean_queue.add(CleanJob(self).connect(self._cleaned))

  def _cleaned(self, job=None):
    """Mark the port as clean."""
    self.working = False
    if job and not job.status:
      self.failed = True
    if not self.failed and self.stage >= Port.BUILD:
      from os.path import isfile
      from os import unlink

      if isfile(self.log_file):
        unlink(self.log_file)

  def build_stage(self, stage):
    """Build the requested stage."""
    from time import time
    from ..job import StalledJob

    pre_map = (self._pre_config, self._pre_depend, self._pre_checksum,
               self._pre_fetch, self._pre_build, self._pre_install,
               self._pre_package, None)

    if self.working or self.stage != stage - 1 or self.failed:
      # Don't do stage if not able to
      return False
    if self.stage >= Port.DEPEND and self.dependancy.check(stage):
      # Don't do stage if not configured
      return False

    self.working = time()
    try:
      status = pre_map[stage - 1]()
      if isinstance(status, bool):
        self._finalise(stage - 1, status)
        return True
    except StalledJob:
      self.working = False
      raise
    return status

  def _pre_config(self):
    """Configure the ports options."""
    if not self._config_lock.acquire():
      from ..job import StalledJob
      raise StalledJob()
    return self._make_target("config", pipe=False)

  def _post_config(self, _make, status):
    """Refetch attr data if ports were configured successfully."""
    self._config_lock.release()
    if status:
      from .mk import Attr

      Attr(self.origin).connect(self._load_attr).get()
      return None
    return status

  def _load_attr(self, _origin, attr):
    """Load the attributes for this port."""
    self.attr = attr
    self._finalise(self.stage, attr is not None)

  def _pre_depend(self):
    """Create a dependancy object for this port."""
    from os.path import join
    from ..env import flags
    from .dependhandler import Dependancy

    self.log_file = join(flags["log_dir"], self.attr["pkgname"])
    self.priority = self._get_priority()
    self.dependant.priority += self.priority
    depends = ('depend_build', 'depend_extract', 'depend_fetch', 'depend_lib',
               'depend_run', 'depend_patch')
    self.dependancy = Dependancy(self, [self.attr[i] for i in depends])

  def _pre_checksum(self):
    """Check if distfiles are available."""
    from os.path import join, isfile
    from ..env import flags

    if flags["no_op"]:
      return True

    distfiles = self.attr["distfiles"]
    if self._fetched.issuperset(distfiles):
      # If files are already fetched
      self.stage = Port.FETCH
      return True
    if not self._bad_checksum.isdisjoint(distfiles):
      # If some files have already failed
      return True
    distdir = self.attr["distdir"]
    for i in distfiles:
      if not isfile(flags["chroot"] + join(distdir, i)):
        # If file does not exist then it failed
        self._bad_checksum.add(i)
        return True
    if not self._checksum_lock.acquire(distfiles):
      from ..job import StalledJob
      raise StalledJob()
    else:
      return self._make_target("checksum", BATCH=True, DISABLE_CONFLICTS=True, FETCH_REGET=0)

  def _post_checksum(self, _make, status):
    """Advance to build stage if checksum passed."""
    distfiles = self.attr["distfiles"]
    self._checksum_lock.release(distfiles)
    if status:
      self._fetched.update(distfiles)
      self.stage = Port.FETCH
    else:
      self._bad_checksum.update(distfiles)
    return True

  def _pre_fetch(self):
    """Fetch the ports files."""
    distfiles = self.attr["distfiles"]
    if self._fetched.issuperset(distfiles):
      # If files are already fetched
      return True
    if self._fetch_failed.issuperset(distfiles):
      # If filfes have failed to fetch
      return False
    if not self._fetch_lock.acquire(distfiles):
      from ..job import StalledJob
      raise StalledJob()
    else:
      return self._make_target("checksum", BATCH=True, DISABLE_CONFLICTS=True, NO_DEPENDS=True)

  def _post_fetch(self, _make, status):
    """Register fetched files if fetch succeeded."""
    distfiles = self.attr["distfiles"]
    self._fetch_lock.release(distfiles)
    if status:
      self._bad_checksum.difference_update(distfiles)
      self._fetched.update(distfiles)
    else:
      self._bad_checksum.update(distfiles)
      self._fetch_failed.update(distfiles)
    return status

  def _pre_build(self):
    """Build the port."""
    return self._make_target(["clean","all"], BATCH=True, NOCLEANDEPENDS=True, NO_DEPENDS=True)

  @staticmethod
  def _post_build(_make, status):
    """Indicate build status."""
    return status

  def _pre_install(self):
    """Install the port."""
    if self.install_status == Port.ABSENT:
      target = ("install",)
    else:
      target = ("deinstall", "reinstall")
    return self._make_target(target, BATCH=True, NO_DEPENDS=True)

  def _post_install(self, _make, status):
    """Update the install status."""
    if status:
      from ..env import flags
      from .mk import status

      if flags["no_op"]:
        self.install_status = Port.CURRENT
      else:
        self.install_status = status(self, True)
    else:
      # TODO???
      self.install_status = Port.ABSENT
    return status

  def _pre_package(self):
    """Package the port,"""
    return self._make_target("package", BATCH=True)

  @staticmethod
  def _post_package(_make, status):
    """Indicate package status."""
    return status

  def pkginstall(self):
    """Prepare to install the port from it's package."""
    from os.path import isfile
    from ..env import flags
    from ..make import make_target

    if self.working or not isfile(self.attr["pkgfile"]):
      return False

    self.stage = self.PKGINSTALL - 1
    self.working = True
    if self.install_status > self.ABSENT:
      return make_target(self, "deinstall").connect(self._pkginstall)
    else:
      return self._pkginstall()

  def _pkginstall(self, make=None):
    """Install the port from it's package."""
    from ..env import flags
    from ..make import SUCCESS

    if make is not None:
      status = make.wait() == SUCCESS:
      if not status:
        self.stage = self.PKGINSTALL
        self.working = False
        self.stage_completed.emit(self)
      self.dependant.state_changed()
      if not status:
        return

    if flags["chroot"]:
      args = ("pkg_add", "-C", flags["chroot"], pkgfile)
    else:
      args = ("pkg_add", pkgfile)

    if flags["noop"]:
      from ..make import PopenNone

      pkg_add = PopenNone(args, self)
    else:
      from subprocess import PIPE
      from ..make import Popen

      logfile = open(self.logfile, "a")
      pkg_add = Popen(args, self, stdin=PIPE, stdout=logfile, stderr=logfile)
      pkg_add.stdin.close()
    return pkg_add.connect(self._post_pkginstall)

  def _post_pkginstall(self, pkg_add):
    """Report if the port successfully installed from it's package."""
    from ..env import flags
    from ..make import SUCCESS
    from .mk import status

    self.working = False
    if flags["noop"]:
      success = True
      self.install_status = self.CURRENT
    else:
      success = make.wait() == SUCCESS
      if success:
        self.install_status = status(self, True)

    self.failed = not success
    self.stage = self.PKGINSTALL
    self.stage_completed.emit(self)

    self.dependant.status_changed()

  def _make_target(self, targets, **kwargs):
    """Build the requested targets."""
    from ..make import make_target

    return make_target(self, targets, **kwargs).connect(self._make)

  def _make(self, make):
    """Call the _post_[stage] function and finalise the stage."""
    from ..make import SUCCESS

    post_map = (self._post_config, None, self._post_checksum, self._post_fetch,
                self._post_build, self._post_install, self._post_package, None)
    stage = self.stage
    status = post_map[stage](make, make.wait() == SUCCESS)
    if status is not None:
      self._finalise(stage, status)

  def _finalise(self, stage, status):
    """Finalise the stage."""
    from ..env import flags

    if not status:
      self.failed = True
    self.working = False
    self.stage = max(stage + 1, self.stage)
    self.stage_completed.emit(self)
    if self.failed or self.stage >= (Port.FETCH if flags["fetch_only"] else Port.INSTALL):
      self.dependant.status_changed()

  def _check_config(self):
    """Check the options file to see if it is up-to-date."""
    from os.path import isfile
    from ..env import env, flags
    from .mk import pkg_version

    if "BATCH" in env or flags["config"] == "none":
      return True
    elif flags["config"] == "all":
      return False

    optionfile = flags["chroot"] + self.attr["optionsfile"]
    pkgname = self.attr["pkgname"]
    options = set()
    if isfile(optionfile):
      for i in open(optionfile, 'r'):
        if i.startswith('_OPTIONS_READ='):
          # The option set to the last pkgname this config file was set for
          config_pkgname = i[14:-1]
        elif i.startswith('WITH'):
          options.add(i.split('_', 1)[1].split('=', 1)[0])
    if flags["config"] == "changed" and options != set(self.attr["options"]):
      return False
    if flags["config"] == "newer" and pkg_version(pkgname, config_pkgname) == Port.NEWER:
      return False
    return True

  def _get_priority(self):
    """Get the priority of this port, based on the distfiles size."""
    from os.path import isfile
    from ..env import flags

    distfiles = self.attr["distfiles"]
    distinfo = flags["chroot"] + self.attr["distinfo"]
    if not len(distfiles) or not isfile(distinfo):
      return 0
    priority = 0
    for i in open(distinfo, 'r'):
      if i.startswith("SIZE"):
        i = i.split()
        name, size = i[1], i[-1]
        name = name[1:-1]
        name = name.split('/')[-1]
        if name in distfiles:
          priority += int(size)
    return priority
<|endoftext|>


<commit_before>#
# distutils/version.py
#
# Implements multiple version numbering conventions for the
# Python Module Distribution Utilities.
#
# $Id$
#

"""Provides classes to represent module version numbers (one class for
each style of version numbering).  There are currently two such classes
implemented: StrictVersion and LooseVersion.

Every version number class implements the following interface:
  * the 'parse' method takes a string and parses it to some internal
    representation; if the string is an invalid version number,
    'parse' raises a ValueError exception
  * the class constructor takes an optional string argument which,
    if supplied, is passed to 'parse'
  * __str__ reconstructs the string that was passed to 'parse' (or
    an equivalent string -- ie. one that will generate an equivalent
    version number instance)
  * __repr__ generates Python code to recreate the version number instance
  * __cmp__ compares the current instance with either another instance
    of the same class or a string (which will be parsed to an instance
    of the same class, thus must follow the same rules)
"""

import string, re
from types import StringType

class Version:
    """Abstract base class for version numbering classes.  Just provides
    constructor (__init__) and reproducer (__repr__), because those
    seem to be the same for all version numbering classes.
    """

    def __init__ (self, vstring=None):
        if vstring:
            self.parse(vstring)

    def __repr__ (self):
        return "%s ('%s')" % (self.__class__.__name__, str(self))

# Interface for version-number classes -- must be implemented
# by the following classes (the concrete ones -- Version should
# be treated as an abstract class).
#    __init__ (string) - create and take same action as 'parse'
#                        (string parameter is optional)
#    parse (string)    - convert a string representation to whatever
#                        internal representation is appropriate for
#                        this style of version numbering
#    __str__ (self)    - convert back to a string; should be very similar
#                        (if not identical to) the string supplied to parse
#    __repr__ (self)   - generate Python code to recreate
#                        the instance
#    __cmp__ (self, other) - compare two version numbers ('other' may
#                        be an unparsed version string, or another
#                        instance of your version class)

class StrictVersion (Version):

    """Version numbering for anal retentives and software idealists.
    Implements the standard interface for version number classes as
    described above.  A version number consists of two or three
    dot-separated numeric components, with an optional "pre-release" tag
    on the end.  The pre-release tag consists of the letter 'a' or 'b'
    followed by a number.  If the numeric components of two version
    numbers are equal, then one with a pre-release tag will always
    be deemed earlier (lesser) than one without.

    The following are valid version numbers (shown in the order that
    would be obtained by sorting according to the supplied cmp function):

        0.4       0.4.0  (these two are equivalent)
        0.4.1
        0.5a1
        0.5b3
        0.5
        0.9.6
        1.0
        1.0.4a3
        1.0.4b1
        1.0.4

    The following are examples of invalid version numbers:

        1
        2.7.2.2
        1.3.a4
        1.3pl1
        1.3c4

    The rationale for this version numbering system will be explained
    in the distutils documentation.
    """

    version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$',
                            re.VERBOSE)

    def parse (self, vstring):
        match = self.version_re.match(vstring)
        if not match:
            raise ValueError, "invalid version number '%s'" % vstring

        (major, minor, patch, prerelease, prerelease_num) = \
            match.group(1, 2, 4, 5, 6)

        if patch:
            self.version = tuple(map(string.atoi, [major, minor, patch]))
        else:
            self.version = tuple(map(string.atoi, [major, minor]) + [0])

        if prerelease:
            self.prerelease = (prerelease[0], string.atoi(prerelease_num))
        else:
            self.prerelease = None

    def __str__ (self):

        if self.version[2] == 0:
            vstring = string.join(map(str, self.version[0:2]), '.')
        else:
            vstring = string.join(map(str, self.version), '.')

        if self.prerelease:
            vstring = vstring + self.prerelease[0] + str(self.prerelease[1])

        return vstring

    def __cmp__ (self, other):
        if isinstance(other, StringType):
            other = StrictVersion(other)

        compare = cmp(self.version, other.version)
        if (compare == 0):              # have to compare prerelease

            # case 1: neither has prerelease; they're equal
            # case 2: self has prerelease, other doesn't; other is greater
            # case 3: self doesn't have prerelease, other does: self is greater
            # case 4: both have prerelease: must compare them!

            if (not self.prerelease and not other.prerelease):
                return 0
            elif (self.prerelease and not other.prerelease):
                return -1
            elif (not self.prerelease and other.prerelease):
                return 1
            elif (self.prerelease and other.prerelease):
                return cmp(self.prerelease, other.prerelease)

        else:                           # numeric versions don't match --
            return compare              # prerelease stuff doesn't matter

# end class StrictVersion

# The rules according to Greg Stein:
# 1) a version number has 1 or more numbers separate by a period or by
#    sequences of letters. If only periods, then these are compared
#    left-to-right to determine an ordering.
# 2) sequences of letters are part of the tuple for comparison and are
#    compared lexicographically
# 3) recognize the numeric components may have leading zeroes
#
# The LooseVersion class below implements these rules: a version number
# string is split up into a tuple of integer and string components, and
# comparison is a simple tuple comparison.  This means that version
# numbers behave in a predictable and obvious way, but a way that might
# not necessarily be how people *want* version numbers to behave.  There
# wouldn't be a problem if people could stick to purely numeric version
# numbers: just split on period and compare the numbers as tuples.
# However, people insist on putting letters into their version numbers;
# the most common purpose seems to be:
#   - indicating a "pre-release" version
#     ('alpha', 'beta', 'a', 'b', 'pre', 'p')
#   - indicating a post-release patch ('p', 'pl', 'patch')
# but of course this can't cover all version number schemes, and there's
# no way to know what a programmer means without asking him.
#
# The problem is what to do with letters (and other non-numeric
# characters) in a version number.  The current implementation does the
# obvious and predictable thing: keep them as strings and compare
# lexically within a tuple comparison.  This has the desired effect if
# an appended letter sequence implies something "post-release":
# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".
#
# However, if letters in a version number imply a pre-release version,
# the "obvious" thing isn't correct.  Eg. you would expect that
# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison
# implemented here, this just isn't so.
#
# Two possible solutions come to mind.  The first is to tie the
# comparison algorithm to a particular set of semantic rules, as has
# been done in the StrictVersion class above.  This works great as long
# as everyone can go along with bondage and discipline.  Hopefully a
# (large) subset of Python module programmers will agree that the
# particular flavour of bondage and discipline provided by StrictVersion
# provides enough benefit to be worth using, and will submit their
# version numbering scheme to its domination.  The free-thinking
# anarchists in the lot will never give in, though, and something needs
# to be done to accommodate them.
#
# Perhaps a "moderately strict" version class could be implemented that
# lets almost anything slide (syntactically), and makes some heuristic
# assumptions about non-digits in version number strings.  This could
# sink into special-case-hell, though; if I was as talented and
# idiosyncratic as Larry Wall, I'd go ahead and implement a class that
# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is
# just as happy dealing with things like "2g6" and "1.13++".  I don't
# think I'm smart enough to do it right though.
#
# In any case, I've coded the test suite for this module (see
# ../test/test_version.py) specifically to fail on things like comparing
# "1.2a2" and "1.2".  That's not because the *code* is doing anything
# wrong, it's because the simple, obvious design doesn't match my
# complicated, hairy expectations for real-world version numbers.  It
# would be a snap to fix the test suite to say, "Yep, LooseVersion does
# the Right Thing" (ie. the code matches the conception).  But I'd rather
# have a conception that matches common notions about version numbers.

class LooseVersion (Version):

    """Version numbering for anarchists and software realists.
    Implements the standard interface for version number classes as
    described above.  A version number consists of a series of numbers,
    separated by either periods or strings of letters.  When comparing
    version numbers, the numeric components will be compared
    numerically, and the alphabetic components lexically.  The following
    are all valid version numbers, in no particular order:

        1.5.1
        1.5.2b2
        161
        3.10a
        8.02
        3.4j
        1996.07.12
        3.2.pl0
        3.1.1.6
        2g6
        11g
        0.960923
        2.2beta29
        1.13++
        5.5.kw
        2.0b1pl0

    In fact, there is no such thing as an invalid version number under
    this scheme; the rules for comparison are simple and predictable,
    but may not always give the results you want (for some definition
    of "want").
    """

    component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)

    def __init__ (self, vstring=None):
        if vstring:
            self.parse(vstring)

    def parse (self, vstring):
        # I've given up on thinking I can reconstruct the version string
        # from the parsed tuple -- so I just store the string here for
        # use by __str__
        self.vstring = vstring
        components = filter(lambda x: x and x != '.',
                            self.component_re.split(vstring))
        for i in range(len(components)):
            try:
                components[i] = int(components[i])
            except ValueError:
                pass

        self.version = components

    def __str__ (self):
        return self.vstring

    def __repr__ (self):
        return "LooseVersion ('%s')" % str(self)

    def __cmp__ (self, other):
        if isinstance(other, StringType):
            other = LooseVersion(other)

        return cmp(self.version, other.version)

# end class LooseVersion
<commit_msg>Fix typo.<commit_after>#
# distutils/version.py
#
# Implements multiple version numbering conventions for the
# Python Module Distribution Utilities.
#
# $Id$
#

"""Provides classes to represent module version numbers (one class for
each style of version numbering).  There are currently two such classes
implemented: StrictVersion and LooseVersion.

Every version number class implements the following interface:
  * the 'parse' method takes a string and parses it to some internal
    representation; if the string is an invalid version number,
    'parse' raises a ValueError exception
  * the class constructor takes an optional string argument which,
    if supplied, is passed to 'parse'
  * __str__ reconstructs the string that was passed to 'parse' (or
    an equivalent string -- ie. one that will generate an equivalent
    version number instance)
  * __repr__ generates Python code to recreate the version number instance
  * __cmp__ compares the current instance with either another instance
    of the same class or a string (which will be parsed to an instance
    of the same class, thus must follow the same rules)
"""

import string, re
from types import StringType

class Version:
    """Abstract base class for version numbering classes.  Just provides
    constructor (__init__) and reproducer (__repr__), because those
    seem to be the same for all version numbering classes.
    """

    def __init__ (self, vstring=None):
        if vstring:
            self.parse(vstring)

    def __repr__ (self):
        return "%s ('%s')" % (self.__class__.__name__, str(self))

# Interface for version-number classes -- must be implemented
# by the following classes (the concrete ones -- Version should
# be treated as an abstract class).
#    __init__ (string) - create and take same action as 'parse'
#                        (string parameter is optional)
#    parse (string)    - convert a string representation to whatever
#                        internal representation is appropriate for
#                        this style of version numbering
#    __str__ (self)    - convert back to a string; should be very similar
#                        (if not identical to) the string supplied to parse
#    __repr__ (self)   - generate Python code to recreate
#                        the instance
#    __cmp__ (self, other) - compare two version numbers ('other' may
#                        be an unparsed version string, or another
#                        instance of your version class)

class StrictVersion (Version):

    """Version numbering for anal retentives and software idealists.
    Implements the standard interface for version number classes as
    described above.  A version number consists of two or three
    dot-separated numeric components, with an optional "pre-release" tag
    on the end.  The pre-release tag consists of the letter 'a' or 'b'
    followed by a number.  If the numeric components of two version
    numbers are equal, then one with a pre-release tag will always
    be deemed earlier (lesser) than one without.

    The following are valid version numbers (shown in the order that
    would be obtained by sorting according to the supplied cmp function):

        0.4       0.4.0  (these two are equivalent)
        0.4.1
        0.5a1
        0.5b3
        0.5
        0.9.6
        1.0
        1.0.4a3
        1.0.4b1
        1.0.4

    The following are examples of invalid version numbers:

        1
        2.7.2.2
        1.3.a4
        1.3pl1
        1.3c4

    The rationale for this version numbering system will be explained
    in the distutils documentation.
    """

    version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$',
                            re.VERBOSE)

    def parse (self, vstring):
        match = self.version_re.match(vstring)
        if not match:
            raise ValueError, "invalid version number '%s'" % vstring

        (major, minor, patch, prerelease, prerelease_num) = \
            match.group(1, 2, 4, 5, 6)

        if patch:
            self.version = tuple(map(string.atoi, [major, minor, patch]))
        else:
            self.version = tuple(map(string.atoi, [major, minor]) + [0])

        if prerelease:
            self.prerelease = (prerelease[0], string.atoi(prerelease_num))
        else:
            self.prerelease = None

    def __str__ (self):

        if self.version[2] == 0:
            vstring = string.join(map(str, self.version[0:2]), '.')
        else:
            vstring = string.join(map(str, self.version), '.')

        if self.prerelease:
            vstring = vstring + self.prerelease[0] + str(self.prerelease[1])

        return vstring

    def __cmp__ (self, other):
        if isinstance(other, StringType):
            other = StrictVersion(other)

        compare = cmp(self.version, other.version)
        if (compare == 0):              # have to compare prerelease

            # case 1: neither has prerelease; they're equal
            # case 2: self has prerelease, other doesn't; other is greater
            # case 3: self doesn't have prerelease, other does: self is greater
            # case 4: both have prerelease: must compare them!

            if (not self.prerelease and not other.prerelease):
                return 0
            elif (self.prerelease and not other.prerelease):
                return -1
            elif (not self.prerelease and other.prerelease):
                return 1
            elif (self.prerelease and other.prerelease):
                return cmp(self.prerelease, other.prerelease)

        else:                           # numeric versions don't match --
            return compare              # prerelease stuff doesn't matter

# end class StrictVersion

# The rules according to Greg Stein:
# 1) a version number has 1 or more numbers separated by a period or by
#    sequences of letters. If only periods, then these are compared
#    left-to-right to determine an ordering.
# 2) sequences of letters are part of the tuple for comparison and are
#    compared lexicographically
# 3) recognize the numeric components may have leading zeroes
#
# The LooseVersion class below implements these rules: a version number
# string is split up into a tuple of integer and string components, and
# comparison is a simple tuple comparison.  This means that version
# numbers behave in a predictable and obvious way, but a way that might
# not necessarily be how people *want* version numbers to behave.  There
# wouldn't be a problem if people could stick to purely numeric version
# numbers: just split on period and compare the numbers as tuples.
# However, people insist on putting letters into their version numbers;
# the most common purpose seems to be:
#   - indicating a "pre-release" version
#     ('alpha', 'beta', 'a', 'b', 'pre', 'p')
#   - indicating a post-release patch ('p', 'pl', 'patch')
# but of course this can't cover all version number schemes, and there's
# no way to know what a programmer means without asking him.
#
# The problem is what to do with letters (and other non-numeric
# characters) in a version number.  The current implementation does the
# obvious and predictable thing: keep them as strings and compare
# lexically within a tuple comparison.  This has the desired effect if
# an appended letter sequence implies something "post-release":
# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".
#
# However, if letters in a version number imply a pre-release version,
# the "obvious" thing isn't correct.  Eg. you would expect that
# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison
# implemented here, this just isn't so.
#
# Two possible solutions come to mind.  The first is to tie the
# comparison algorithm to a particular set of semantic rules, as has
# been done in the StrictVersion class above.  This works great as long
# as everyone can go along with bondage and discipline.  Hopefully a
# (large) subset of Python module programmers will agree that the
# particular flavour of bondage and discipline provided by StrictVersion
# provides enough benefit to be worth using, and will submit their
# version numbering scheme to its domination.  The free-thinking
# anarchists in the lot will never give in, though, and something needs
# to be done to accommodate them.
#
# Perhaps a "moderately strict" version class could be implemented that
# lets almost anything slide (syntactically), and makes some heuristic
# assumptions about non-digits in version number strings.  This could
# sink into special-case-hell, though; if I was as talented and
# idiosyncratic as Larry Wall, I'd go ahead and implement a class that
# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is
# just as happy dealing with things like "2g6" and "1.13++".  I don't
# think I'm smart enough to do it right though.
#
# In any case, I've coded the test suite for this module (see
# ../test/test_version.py) specifically to fail on things like comparing
# "1.2a2" and "1.2".  That's not because the *code* is doing anything
# wrong, it's because the simple, obvious design doesn't match my
# complicated, hairy expectations for real-world version numbers.  It
# would be a snap to fix the test suite to say, "Yep, LooseVersion does
# the Right Thing" (ie. the code matches the conception).  But I'd rather
# have a conception that matches common notions about version numbers.

class LooseVersion (Version):

    """Version numbering for anarchists and software realists.
    Implements the standard interface for version number classes as
    described above.  A version number consists of a series of numbers,
    separated by either periods or strings of letters.  When comparing
    version numbers, the numeric components will be compared
    numerically, and the alphabetic components lexically.  The following
    are all valid version numbers, in no particular order:

        1.5.1
        1.5.2b2
        161
        3.10a
        8.02
        3.4j
        1996.07.12
        3.2.pl0
        3.1.1.6
        2g6
        11g
        0.960923
        2.2beta29
        1.13++
        5.5.kw
        2.0b1pl0

    In fact, there is no such thing as an invalid version number under
    this scheme; the rules for comparison are simple and predictable,
    but may not always give the results you want (for some definition
    of "want").
    """

    component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)

    def __init__ (self, vstring=None):
        if vstring:
            self.parse(vstring)

    def parse (self, vstring):
        # I've given up on thinking I can reconstruct the version string
        # from the parsed tuple -- so I just store the string here for
        # use by __str__
        self.vstring = vstring
        components = filter(lambda x: x and x != '.',
                            self.component_re.split(vstring))
        for i in range(len(components)):
            try:
                components[i] = int(components[i])
            except ValueError:
                pass

        self.version = components

    def __str__ (self):
        return self.vstring

    def __repr__ (self):
        return "LooseVersion ('%s')" % str(self)

    def __cmp__ (self, other):
        if isinstance(other, StringType):
            other = LooseVersion(other)

        return cmp(self.version, other.version)

# end class LooseVersion
<|endoftext|>


<commit_before>from test_support import TestFailed
from random import random

# XXX need many, many more tests here.

nerrors = 0

def check_close_real(x, y, eps=1e-12):
    """Return true iff floats x and y "are close\""""
    # put the one with larger magnitude second
    if abs(x) > abs(y):
        x, y = y, x
    if y == 0:
        return abs(x) < eps
    if x == 0:
        return abs(y) < eps
    # check that relative difference < eps
    return abs((x-y)/y) < eps

def check_close(x, y, eps=1e-12):
    """Return true iff complexes x and y "are close\""""
    return check_close_real(x.real, y.real, eps) and \
           check_close_real(x.imag, y.imag, eps)

def test_div(x, y):
    """Compute complex z=x*y, and check that z/x==y and z/y==x."""
    global nerrors
    z = x * y
    if x != 0:
        q = z / x
        if not check_close(q, y):
            nerrors += 1
            print `z`, "/", `x`, "==", `q`, "but expected", `y`
    if y != 0:
        q = z / y
        if not check_close(q, x):
            nerrors += 1
            print `z`, "/", `y`, "==", `q`, "but expected", `x`

simple_real = [float(i) for i in range(-5, 6)]
simple_complex = [complex(x, y) for x in simple_real for y in simple_real]
for x in simple_complex:
    for y in simple_complex:
        test_div(x, y)

# A naive complex division algorithm (such as in 2.0) is very prone to
# nonsense errors for these (overflows and underflows).
test_div(complex(1e200, 1e200), 1+0j)
test_div(complex(1e-200, 1e-200), 1+0j)

# Just for fun.
for i in range(100):
    test_div(complex(random(), random()),
             complex(random(), random()))

try:
    z = 1.0 / (0+0j)
except ZeroDivisionError:
    pass
else:
    nerrors += 1
    raise TestFailed("Division by complex 0 didn't raise ZeroDivisionError")

if nerrors:
    raise TestFailed("%d tests failed" % nerrors)
<commit_msg>This division test was too stringent in its accuracy expectations for random inputs:  if you ran the test 100 times, you could expect it to report a bogus failure.  So loosened its expectations. Also changed the way failing tests are printed, so that when run under regrtest.py we get enough info to reproduce the failure.<commit_after>from test_support import TestFailed
from random import random

# XXX need many, many more tests here.

nerrors = 0

def check_close_real(x, y, eps=1e-9):
    """Return true iff floats x and y "are close\""""
    # put the one with larger magnitude second
    if abs(x) > abs(y):
        x, y = y, x
    if y == 0:
        return abs(x) < eps
    if x == 0:
        return abs(y) < eps
    # check that relative difference < eps
    return abs((x-y)/y) < eps

def check_close(x, y, eps=1e-9):
    """Return true iff complexes x and y "are close\""""
    return check_close_real(x.real, y.real, eps) and \
           check_close_real(x.imag, y.imag, eps)

def test_div(x, y):
    """Compute complex z=x*y, and check that z/x==y and z/y==x."""
    global nerrors
    z = x * y
    if x != 0:
        q = z / x
        if not check_close(q, y):
            nerrors += 1
            print "%r / %r == %r but expected %r" % (z, x, q, y)
    if y != 0:
        q = z / y
        if not check_close(q, x):
            nerrors += 1
            print "%r / %r == %r but expected %r" % (z, y, q, x)

simple_real = [float(i) for i in range(-5, 6)]
simple_complex = [complex(x, y) for x in simple_real for y in simple_real]
for x in simple_complex:
    for y in simple_complex:
        test_div(x, y)

# A naive complex division algorithm (such as in 2.0) is very prone to
# nonsense errors for these (overflows and underflows).
test_div(complex(1e200, 1e200), 1+0j)
test_div(complex(1e-200, 1e-200), 1+0j)

# Just for fun.
for i in range(100):
    test_div(complex(random(), random()),
             complex(random(), random()))

try:
    z = 1.0 / (0+0j)
except ZeroDivisionError:
    pass
else:
    nerrors += 1
    raise TestFailed("Division by complex 0 didn't raise ZeroDivisionError")

if nerrors:
    raise TestFailed("%d tests failed" % nerrors)
<|endoftext|>


<commit_before>import hotshot
import hotshot.log
import os
import pprint
import unittest

from test import test_support

from hotshot.log import ENTER, EXIT, LINE

def shortfilename(fn):
    # We use a really shortened filename since an exact match is made,
    # and the source may be either a Python source file or a
    # pre-compiled bytecode file.
    if fn:
        return os.path.splitext(os.path.basename(fn))[0]
    else:
        return fn

class UnlinkingLogReader(hotshot.log.LogReader):
    """Extend the LogReader so the log file is unlinked when we're
    done with it."""

    def __init__(self, logfn):
        self.__logfn = logfn
        hotshot.log.LogReader.__init__(self, logfn)

    def next(self, index=None):
        try:
            return hotshot.log.LogReader.next(self)
        except (IndexError, StopIteration):
            self.close()
            os.unlink(self.__logfn)
            raise

class HotShotTestCase(unittest.TestCase):
    def new_profiler(self, lineevents=0, linetimings=1):
        self.logfn = test_support.TESTFN
        return hotshot.Profile(self.logfn, lineevents, linetimings)

    def get_logreader(self):
        return UnlinkingLogReader(self.logfn)

    def get_events_wotime(self):
        L = []
        for event in self.get_logreader():
            what, (filename, lineno, funcname), tdelta = event
            L.append((what, (shortfilename(filename), lineno, funcname)))
        return L

    def check_events(self, expected):
        events = self.get_events_wotime()
        if not __debug__:
            # Running under -O, so we don't get LINE events
            expected = [ev for ev in expected if ev[0] != LINE]
        if events != expected:
            self.fail(
                "events did not match expectation; got:\n%s\nexpected:\n%s"
                % (pprint.pformat(events), pprint.pformat(expected)))

    def run_test(self, callable, events, profiler=None):
        if profiler is None:
            profiler = self.new_profiler()
        self.failUnless(not profiler._prof.closed)
        profiler.runcall(callable)
        self.failUnless(not profiler._prof.closed)
        profiler.close()
        self.failUnless(profiler._prof.closed)
        self.check_events(events)

    def test_addinfo(self):
        def f(p):
            p.addinfo("test-key", "test-value")
        profiler = self.new_profiler()
        profiler.runcall(f, profiler)
        profiler.close()
        log = self.get_logreader()
        info = log._info
        list(log)
        self.failUnless(info["test-key"] == ["test-value"])

    def test_line_numbers(self):
        def f():
            y = 2
            x = 1
        def g():
            f()
        f_lineno = f.func_code.co_firstlineno
        g_lineno = g.func_code.co_firstlineno
        events = [(ENTER, ("test_hotshot", g_lineno, "g")),
                  (LINE,  ("test_hotshot", g_lineno, "g")),
                  (LINE,  ("test_hotshot", g_lineno+1, "g")),
                  (ENTER, ("test_hotshot", f_lineno, "f")),
                  (LINE,  ("test_hotshot", f_lineno, "f")),
                  (LINE,  ("test_hotshot", f_lineno+1, "f")),
                  (LINE,  ("test_hotshot", f_lineno+2, "f")),
                  (EXIT,  ("test_hotshot", f_lineno, "f")),
                  (EXIT,  ("test_hotshot", g_lineno, "g")),
                  ]
        self.run_test(g, events, self.new_profiler(lineevents=1))

    def test_start_stop(self):
        # Make sure we don't return NULL in the start() and stop()
        # methods when there isn't an error.  Bug in 2.2 noted by
        # Anthony Baxter.
        profiler = self.new_profiler()
        profiler.start()
        profiler.stop()
        profiler.close()
        os.unlink(self.logfn)

def test_main():
    test_support.run_unittest(HotShotTestCase)

if __name__ == "__main__":
    test_main()
<commit_msg>We only need to check for StopIteration here.<commit_after>import hotshot
import hotshot.log
import os
import pprint
import unittest

from test import test_support

from hotshot.log import ENTER, EXIT, LINE

def shortfilename(fn):
    # We use a really shortened filename since an exact match is made,
    # and the source may be either a Python source file or a
    # pre-compiled bytecode file.
    if fn:
        return os.path.splitext(os.path.basename(fn))[0]
    else:
        return fn

class UnlinkingLogReader(hotshot.log.LogReader):
    """Extend the LogReader so the log file is unlinked when we're
    done with it."""

    def __init__(self, logfn):
        self.__logfn = logfn
        hotshot.log.LogReader.__init__(self, logfn)

    def next(self, index=None):
        try:
            return hotshot.log.LogReader.next(self)
        except StopIteration:
            self.close()
            os.unlink(self.__logfn)
            raise

class HotShotTestCase(unittest.TestCase):
    def new_profiler(self, lineevents=0, linetimings=1):
        self.logfn = test_support.TESTFN
        return hotshot.Profile(self.logfn, lineevents, linetimings)

    def get_logreader(self):
        return UnlinkingLogReader(self.logfn)

    def get_events_wotime(self):
        L = []
        for event in self.get_logreader():
            what, (filename, lineno, funcname), tdelta = event
            L.append((what, (shortfilename(filename), lineno, funcname)))
        return L

    def check_events(self, expected):
        events = self.get_events_wotime()
        if not __debug__:
            # Running under -O, so we don't get LINE events
            expected = [ev for ev in expected if ev[0] != LINE]
        if events != expected:
            self.fail(
                "events did not match expectation; got:\n%s\nexpected:\n%s"
                % (pprint.pformat(events), pprint.pformat(expected)))

    def run_test(self, callable, events, profiler=None):
        if profiler is None:
            profiler = self.new_profiler()
        self.failUnless(not profiler._prof.closed)
        profiler.runcall(callable)
        self.failUnless(not profiler._prof.closed)
        profiler.close()
        self.failUnless(profiler._prof.closed)
        self.check_events(events)

    def test_addinfo(self):
        def f(p):
            p.addinfo("test-key", "test-value")
        profiler = self.new_profiler()
        profiler.runcall(f, profiler)
        profiler.close()
        log = self.get_logreader()
        info = log._info
        list(log)
        self.failUnless(info["test-key"] == ["test-value"])

    def test_line_numbers(self):
        def f():
            y = 2
            x = 1
        def g():
            f()
        f_lineno = f.func_code.co_firstlineno
        g_lineno = g.func_code.co_firstlineno
        events = [(ENTER, ("test_hotshot", g_lineno, "g")),
                  (LINE,  ("test_hotshot", g_lineno, "g")),
                  (LINE,  ("test_hotshot", g_lineno+1, "g")),
                  (ENTER, ("test_hotshot", f_lineno, "f")),
                  (LINE,  ("test_hotshot", f_lineno, "f")),
                  (LINE,  ("test_hotshot", f_lineno+1, "f")),
                  (LINE,  ("test_hotshot", f_lineno+2, "f")),
                  (EXIT,  ("test_hotshot", f_lineno, "f")),
                  (EXIT,  ("test_hotshot", g_lineno, "g")),
                  ]
        self.run_test(g, events, self.new_profiler(lineevents=1))

    def test_start_stop(self):
        # Make sure we don't return NULL in the start() and stop()
        # methods when there isn't an error.  Bug in 2.2 noted by
        # Anthony Baxter.
        profiler = self.new_profiler()
        profiler.start()
        profiler.stop()
        profiler.close()
        os.unlink(self.logfn)

def test_main():
    test_support.run_unittest(HotShotTestCase)

if __name__ == "__main__":
    test_main()
<|endoftext|>


<commit_before>#The MIT License (MIT)
#
#Copyright (C) 2014 OpenBet Limited
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of
#this software and associated documentation files (the "Software"), to deal in
#the Software without restriction, including without limitation the rights to
#use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
#of the Software, and to permit persons to whom the Software is furnished to do
#so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all
#copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#ITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
#THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#SOFTWARE.

from shutit_module import ShutItModule
import util
import pexpect

#From: https://groups.google.com/forum/#!topic/docker-user/D0n-lURDn0o
# Expose port 5901 and 6080
class vnc(ShutItModule):

	def check_ready(self,shutit):
		# Only apt-based systems are supported support atm
		return shutit.cfg['container']['install_type'] == 'apt'

	def is_installed(self,shutit):
		return shutit.file_exists('/root/start_vnc.sh')

	def build(self,shutit):
		# TODO: distr-independence
		shutit.set_default_expect(shutit.cfg['expect_prompts']['root_prompt'])
		shutit.send_and_expect('echo "deb http://archive.ubuntu.com/ubuntu precise main universe multiverse" > /etc/apt/sources.list')
		shutit.add_line_to_file('deb http://archive.ubuntu.com/ubuntu/ precise-updates main restricted','/etc/apt/sources.list')
		if shutit.cfg['container']['install_type'] == 'apt':
			shutit.send_and_expect('apt-get update -qq',timeout=10000)
		shutit.send_and_expect('apt-get upgrade -y')
		shutit.install('gnome-terminal')
		shutit.install('openjdk-6-jre')
		shutit.install('xserver-xorg')
		shutit.install('vnc4server')
		shutit.install('novnc')
		shutit.install('apt-utils')
		# apt-utils?
		if shutit.cfg['container']['install_type'] == 'apt':
			send = 'apt-get install -qq -y --no-install-recommends ubuntu-desktop > /tmp/ubuntu-desktop && rm -f /tmp/ubuntu-desktop'
		while True:
			res = shutit.send_and_expect(send,['Unpacking','Setting up',shutit.cfg['expect_prompts']['root_prompt']],timeout=9999,check_exit=False)
			if res == 2:
				break
			elif res == 0 or res == 1:
				send = ''
		send = 'vncserver'
		while True:
			res = shutit.send_and_expect(send,['assword','erify',shutit.cfg['expect_prompts']['root_prompt']],check_exit=False,fail_on_empty_before=False,record_command=False)
			if res == 0 or res == 1:
				send = shutit.cfg['shutit.tk.vnc.vnc']['password']
			elif res == 2:
				break
		shutit.add_line_to_file('# start vnc','/root/start_vnc.sh')
		shutit.add_line_to_file('rm -rf /tmp/.X*','/root/start_vnc.sh')
		shutit.add_line_to_file("""vncserver << END
""" + shutit.cfg['shutit.tk.vnc.vnc']['password'] + """
""" + shutit.cfg['shutit.tk.vnc.vnc']['password'] + """
END""",'/root/start_vnc.sh',shutit.cfg['expect_prompts']['root_prompt'])
		shutit.add_line_to_file('echo "Did you expose ports 5901 and 6080?"','/root/start_vnc.sh',shutit.cfg['expect_prompts']['root_prompt'],match_regexp='echo .Did you expose ports 5901 and 6080..')
		shutit.add_line_to_file('echo "If so, then vncviewer localhost:1 should work."','/root/start_vnc.sh',shutit.cfg['expect_prompts']['root_prompt'],match_regexp='echo .If so, then vncviewer localhost:1 should work..')
		shutit.add_line_to_file('# stop vnc','/root/stop_vnc.sh',shutit.cfg['expect_prompts']['root_prompt'])
		shutit.add_line_to_file("""ps -ef | grep Xvnc4 | grep -v grep | awk '{print $2}' | xargs kill""",'/root/stop_vnc.sh',shutit.cfg['expect_prompts']['root_prompt'])
		shutit.add_line_to_file('sleep 10','/root/stop_vnc.sh',shutit.cfg['expect_prompts']['root_prompt'])
		shutit.add_line_to_file('rm -rf /tmp/.X*-lock','/root/stop_vnc.sh',shutit.cfg['expect_prompts']['root_prompt'])
		shutit.send_and_expect('chmod +x /root/start_vnc.sh')
		shutit.send_and_expect('chmod +x /root/stop_vnc.sh')
		return True

	def start(self,shutit):
		shutit.send_and_expect('/root/start_vnc.sh',check_exit=False)
		return True

	def stop(self,shutit):
		shutit.send_and_expect('/root/stop_vnc.sh',check_exit=False)
		return True

	def get_config(self,shutit):
		cfg = shutit.cfg
		cp = cfg['config_parser']
		cfg['shutit.tk.vnc.vnc']['password'] = cp.get('shutit.tk.vnc.vnc','password')
		return True

if not util.module_exists('shutit.tk.vnc.vnc'):
	obj = vnc('shutit.tk.vnc.vnc',0.322,'VNC ShutIt module. Contains instructions for use within /root/start_vnc.sh output.')
	util.get_shutit_modules().add(obj)
	ShutItModule.register(vnc)

<commit_msg>dynamic for ubuntu at least<commit_after>#The MIT License (MIT)
#
#Copyright (C) 2014 OpenBet Limited
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of
#this software and associated documentation files (the "Software"), to deal in
#the Software without restriction, including without limitation the rights to
#use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
#of the Software, and to permit persons to whom the Software is furnished to do
#so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all
#copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#ITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
#THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#SOFTWARE.

from shutit_module import ShutItModule
import util
import pexpect

#From: https://groups.google.com/forum/#!topic/docker-user/D0n-lURDn0o
# Expose port 5901 and 6080
class vnc(ShutItModule):

	def check_ready(self,shutit):
		# Only apt-based systems are supported support atm
		return shutit.cfg['container']['install_type'] == 'apt'

	def is_installed(self,shutit):
		return shutit.file_exists('/root/start_vnc.sh')

	def build(self,shutit):
		# TODO: distr-independence
		shutit.set_default_expect(shutit.cfg['expect_prompts']['root_prompt'])
		shutit.send_and_expect("""echo "deb http://archive.ubuntu.com/ubuntu $(grep CODENAME /etc/lsb-release | sed 's/DISTRIB_CODENAME=\(.*\)/\1/g') main universe multiverse" > /etc/apt/sources.list""")
		shutit.add_line_to_file("""deb http://archive.ubuntu.com/ubuntu/ $(grep CODENAME /etc/lsb-release | sed 's/DISTRIB_CODENAME=\(.*\)/\1/g')-updates main restricted','/etc/apt/sources.list""")
		if shutit.cfg['container']['install_type'] == 'apt':
			shutit.send_and_expect('apt-get update -qq',timeout=10000)
		shutit.send_and_expect('apt-get upgrade -y')
		shutit.install('gnome-terminal')
		shutit.install('openjdk-6-jre')
		shutit.install('xserver-xorg')
		shutit.install('vnc4server')
		shutit.install('novnc')
		shutit.install('apt-utils')
		# apt-utils?
		if shutit.cfg['container']['install_type'] == 'apt':
			send = 'apt-get install -qq -y --no-install-recommends ubuntu-desktop > /tmp/ubuntu-desktop && rm -f /tmp/ubuntu-desktop'
		while True:
			res = shutit.send_and_expect(send,['Unpacking','Setting up',shutit.cfg['expect_prompts']['root_prompt']],timeout=9999,check_exit=False)
			if res == 2:
				break
			elif res == 0 or res == 1:
				send = ''
		send = 'vncserver'
		while True:
			res = shutit.send_and_expect(send,['assword','erify',shutit.cfg['expect_prompts']['root_prompt']],check_exit=False,fail_on_empty_before=False,record_command=False)
			if res == 0 or res == 1:
				send = shutit.cfg['shutit.tk.vnc.vnc']['password']
			elif res == 2:
				break
		shutit.add_line_to_file('# start vnc','/root/start_vnc.sh')
		shutit.add_line_to_file('rm -rf /tmp/.X*','/root/start_vnc.sh')
		shutit.add_line_to_file("""vncserver << END
""" + shutit.cfg['shutit.tk.vnc.vnc']['password'] + """
""" + shutit.cfg['shutit.tk.vnc.vnc']['password'] + """
END""",'/root/start_vnc.sh',shutit.cfg['expect_prompts']['root_prompt'])
		shutit.add_line_to_file('echo "Did you expose ports 5901 and 6080?"','/root/start_vnc.sh',shutit.cfg['expect_prompts']['root_prompt'],match_regexp='echo .Did you expose ports 5901 and 6080..')
		shutit.add_line_to_file('echo "If so, then vncviewer localhost:1 should work."','/root/start_vnc.sh',shutit.cfg['expect_prompts']['root_prompt'],match_regexp='echo .If so, then vncviewer localhost:1 should work..')
		shutit.add_line_to_file('# stop vnc','/root/stop_vnc.sh',shutit.cfg['expect_prompts']['root_prompt'])
		shutit.add_line_to_file("""ps -ef | grep Xvnc4 | grep -v grep | awk '{print $2}' | xargs kill""",'/root/stop_vnc.sh',shutit.cfg['expect_prompts']['root_prompt'])
		shutit.add_line_to_file('sleep 10','/root/stop_vnc.sh',shutit.cfg['expect_prompts']['root_prompt'])
		shutit.add_line_to_file('rm -rf /tmp/.X*-lock','/root/stop_vnc.sh',shutit.cfg['expect_prompts']['root_prompt'])
		shutit.send_and_expect('chmod +x /root/start_vnc.sh')
		shutit.send_and_expect('chmod +x /root/stop_vnc.sh')
		return True

	def start(self,shutit):
		shutit.send_and_expect('/root/start_vnc.sh',check_exit=False)
		return True

	def stop(self,shutit):
		shutit.send_and_expect('/root/stop_vnc.sh',check_exit=False)
		return True

	def get_config(self,shutit):
		cfg = shutit.cfg
		cp = cfg['config_parser']
		cfg['shutit.tk.vnc.vnc']['password'] = cp.get('shutit.tk.vnc.vnc','password')
		return True

if not util.module_exists('shutit.tk.vnc.vnc'):
	obj = vnc('shutit.tk.vnc.vnc',0.322,'VNC ShutIt module. Contains instructions for use within /root/start_vnc.sh output.')
	util.get_shutit_modules().add(obj)
	ShutItModule.register(vnc)

<|endoftext|>


<commit_before>import imaplib
import time

# Can't use time.time() values, as they are O/S specific

timevalues = [(2033, 5, 18, 3, 33, 20, 2, 138, 0), '"18-May-2033 13:33:20 +1000"']

for t in timevalues:
    t1 = imaplib.Time2Internaldate(t)
    t2 = imaplib.Time2Internaldate(imaplib.Internaldate2tuple('INTERNALDATE ' + t1))
    if t1 <> t2:
        print 'incorrect result when converting', `t`
<commit_msg>remove redundant import<commit_after>import imaplib

# Can't use time.time() values, as they are O/S specific

timevalues = [(2033, 5, 18, 3, 33, 20, 2, 138, 0), '"18-May-2033 13:33:20 +1000"']

for t in timevalues:
    t1 = imaplib.Time2Internaldate(t)
    t2 = imaplib.Time2Internaldate(imaplib.Internaldate2tuple('INTERNALDATE ' + t1))
    if t1 <> t2:
        print 'incorrect result when converting', `t`
<|endoftext|>