Home | History | Annotate | Download | only in system
      1 # Copyright (c) 2009, Google Inc. All rights reserved.
      2 #
      3 # Redistribution and use in source and binary forms, with or without
      4 # modification, are permitted provided that the following conditions are
      5 # met:
      6 #
      7 #     * Redistributions of source code must retain the above copyright
      8 # notice, this list of conditions and the following disclaimer.
      9 #     * Redistributions in binary form must reproduce the above
     10 # copyright notice, this list of conditions and the following disclaimer
     11 # in the documentation and/or other materials provided with the
     12 # distribution.
     13 #     * Neither the name of Google Inc. nor the names of its
     14 # contributors may be used to endorse or promote products derived from
     15 # this software without specific prior written permission.
     16 #
     17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28 
     29 import getpass
     30 import logging
     31 import os
     32 import platform
     33 import re
     34 import shlex
     35 import subprocess
     36 import sys
     37 import webbrowser
     38 
     39 from webkitpy.common.system.executive import Executive
     40 from webkitpy.common.system.platforminfo import PlatformInfo
     41 
     42 
     43 _log = logging.getLogger(__name__)
     44 
     45 
     46 try:
     47     import readline
     48 except ImportError:
     49     if sys.platform != "win32":
     50         # There is no readline module for win32, not much to do except cry.
     51         _log.warn("Unable to import readline.")
     52 
     53 
     54 class User(object):
     55     DEFAULT_NO = 'n'
     56     DEFAULT_YES = 'y'
     57 
     58     def __init__(self, platforminfo=None):
     59         # We cannot get the PlatformInfo object from a SystemHost because
     60         # User is part of SystemHost itself.
     61         self._platforminfo = platforminfo or PlatformInfo(sys, platform, Executive())
     62 
     63     # FIXME: These are @classmethods because bugzilla.py doesn't have a Tool object (thus no User instance).
     64     @classmethod
     65     def prompt(cls, message, repeat=1, raw_input=raw_input):
     66         response = None
     67         while (repeat and not response):
     68             repeat -= 1
     69             response = raw_input(message)
     70         return response
     71 
     72     @classmethod
     73     def prompt_password(cls, message, repeat=1):
     74         return cls.prompt(message, repeat=repeat, raw_input=getpass.getpass)
     75 
     76     @classmethod
     77     def prompt_with_multiple_lists(cls, list_title, subtitles, lists, can_choose_multiple=False, raw_input=raw_input):
     78         item_index = 0
     79         cumulated_list = []
     80         print list_title
     81         for i in range(len(subtitles)):
     82             print "\n" + subtitles[i]
     83             for item in lists[i]:
     84                 item_index += 1
     85                 print "%2d. %s" % (item_index, item)
     86             cumulated_list += lists[i]
     87         return cls._wait_on_list_response(cumulated_list, can_choose_multiple, raw_input)
     88 
     89     @classmethod
     90     def _wait_on_list_response(cls, list_items, can_choose_multiple, raw_input):
     91         while True:
     92             if can_choose_multiple:
     93                 response = cls.prompt("Enter one or more numbers (comma-separated) or ranges (e.g. 3-7), or \"all\": ", raw_input=raw_input)
     94                 if not response.strip() or response == "all":
     95                     return list_items
     96 
     97                 try:
     98                     indices = []
     99                     for value in re.split("\s*,\s*", response):
    100                         parts = value.split('-')
    101                         if len(parts) == 2:
    102                             indices += range(int(parts[0]) - 1, int(parts[1]))
    103                         else:
    104                             indices.append(int(value) - 1)
    105                 except ValueError, err:
    106                     continue
    107 
    108                 return [list_items[i] for i in indices]
    109             else:
    110                 try:
    111                     result = int(cls.prompt("Enter a number: ", raw_input=raw_input)) - 1
    112                 except ValueError, err:
    113                     continue
    114                 return list_items[result]
    115 
    116     @classmethod
    117     def prompt_with_list(cls, list_title, list_items, can_choose_multiple=False, raw_input=raw_input):
    118         print list_title
    119         i = 0
    120         for item in list_items:
    121             i += 1
    122             print "%2d. %s" % (i, item)
    123         return cls._wait_on_list_response(list_items, can_choose_multiple, raw_input)
    124 
    125     def edit(self, files):
    126         editor = os.environ.get("EDITOR") or "vi"
    127         args = shlex.split(editor)
    128         # Note: Not thread safe: http://bugs.python.org/issue2320
    129         subprocess.call(args + files)
    130 
    131     def _warn_if_application_is_xcode(self, edit_application):
    132         if "Xcode" in edit_application:
    133             print "Instead of using Xcode.app, consider using EDITOR=\"xed --wait\"."
    134 
    135     def edit_changelog(self, files):
    136         edit_application = os.environ.get("CHANGE_LOG_EDIT_APPLICATION")
    137         if edit_application and self._platforminfo.is_mac():
    138             # On Mac we support editing ChangeLogs using an application.
    139             args = shlex.split(edit_application)
    140             print "Using editor in the CHANGE_LOG_EDIT_APPLICATION environment variable."
    141             print "Please quit the editor application when done editing."
    142             self._warn_if_application_is_xcode(edit_application)
    143             subprocess.call(["open", "-W", "-n", "-a"] + args + files)
    144             return
    145         self.edit(files)
    146 
    147     def page(self, message):
    148         pager = os.environ.get("PAGER") or "less"
    149         try:
    150             # Note: Not thread safe: http://bugs.python.org/issue2320
    151             child_process = subprocess.Popen([pager], stdin=subprocess.PIPE)
    152             child_process.communicate(input=message)
    153         except IOError, e:
    154             pass
    155 
    156     def confirm(self, message=None, default=DEFAULT_YES, raw_input=raw_input):
    157         if not message:
    158             message = "Continue?"
    159         choice = {'y': 'Y/n', 'n': 'y/N'}[default]
    160         response = raw_input("%s [%s]: " % (message, choice))
    161         if not response:
    162             response = default
    163         return response.lower() == 'y'
    164 
    165     def can_open_url(self):
    166         try:
    167             webbrowser.get()
    168             return True
    169         except webbrowser.Error, e:
    170             return False
    171 
    172     def open_url(self, url):
    173         if not self.can_open_url():
    174             _log.warn("Failed to open %s" % url)
    175         webbrowser.open(url)
    176