Back to index

apport  2.4
Public Member Functions | Public Attributes
test_ui.TestSuiteUserInterface Class Reference
Inheritance diagram for test_ui.TestSuiteUserInterface:
Inheritance graph
[legend]
Collaboration diagram for test_ui.TestSuiteUserInterface:
Collaboration graph
[legend]

List of all members.

Public Member Functions

def __init__
def clear_msg
def ui_present_report_details
def ui_info_message
def ui_error_message
def ui_start_info_collection_progress
def ui_pulse_info_collection_progress
def ui_stop_info_collection_progress
def ui_start_upload_progress
def ui_set_upload_progress
def ui_stop_upload_progress
def open_url
def ui_question_yesno
def ui_question_choice
def ui_question_file
def run_crashes
def run_crash
def finish_hang
def run_hang
def wait_for_pid
def kill_segv
def run_report_bug
def run_update_report
def run_symptoms
def run_symptom
def run_argv
def parse_argv_update
def parse_argv
def format_filesize
def get_complete_size
def get_reduced_size
def can_examine_locally
def restart
def examine
def collect_info
def file_report
def load_report
def check_unreportable
def get_desktop_entry
def handle_duplicate
def add_extra_tags
def ui_shutdown
def ui_run_terminal
def ui_question_userpass

Public Attributes

 crashdb_conf
 crashdb
 ic_progress_active
 ic_progress_pulses
 upload_progress_active
 upload_progress_pulses
 present_package_error_response
 present_kernel_error_response
 present_details_response
 question_yesno_response
 question_choice_response
 question_file_response
 opened_url
 present_details_shown
 msg_title
 msg_text
 msg_severity
 msg_choices
 gettext_domain
 report
 report_file
 cur_package
 args

Detailed Description

Concrete apport.ui.UserInterface suitable for automatic testing

Definition at line 20 of file test_ui.py.


Constructor & Destructor Documentation

Initialize program state and parse command line options.

Reimplemented from apport.ui.UserInterface.

Definition at line 23 of file test_ui.py.

00023 
00024     def __init__(self):
00025         # use our dummy crashdb
00026         self.crashdb_conf = tempfile.NamedTemporaryFile()
00027         self.crashdb_conf.write(b'''default = 'testsuite'
00028 databases = {
00029     'testsuite': {
00030         'impl': 'memory',
00031         'bug_pattern_url': None,
00032     }
00033 }
00034 ''')
00035         self.crashdb_conf.flush()
00036 
00037         os.environ['APPORT_CRASHDB_CONF'] = self.crashdb_conf.name
00038 
00039         apport.ui.UserInterface.__init__(self)
00040 
00041         self.crashdb = apport.crashdb_impl.memory.CrashDatabase(
00042             None, {'dummy_data': 1, 'dupdb_url': ''})
00043 
00044         # state of progress dialogs
00045         self.ic_progress_active = False
00046         self.ic_progress_pulses = 0  # count the pulses
00047         self.upload_progress_active = False
00048         self.upload_progress_pulses = 0
00049 
00050         # these store the choices the ui_present_* calls do
00051         self.present_package_error_response = None
00052         self.present_kernel_error_response = None
00053         self.present_details_response = None
00054         self.question_yesno_response = None
00055         self.question_choice_response = None
00056         self.question_file_response = None
00057 
00058         self.opened_url = None
00059         self.present_details_shown = False
00060 
00061         self.clear_msg()


Member Function Documentation

def apport.ui.UserInterface.add_extra_tags (   self) [inherited]
Add extra tags to report specified with --tags on CLI.

Definition at line 1256 of file ui.py.

01256 
01257     def add_extra_tags(self):
01258         '''Add extra tags to report specified with --tags on CLI.'''
01259 
01260         assert self.report
01261         if self.options.tag:
01262             tags = self.report.get('Tags', '')
01263             if tags:
01264                 tags += ' '
01265             self.report['Tags'] = tags + ' '.join(self.options.tag)

Here is the caller graph for this function:

def apport.ui.UserInterface.can_examine_locally (   self) [inherited]
Check whether to offer the "Examine locally" button.

This will be true if the report has a core dump, apport-retrace is
installed and a terminal is available (see ui_run_terminal()).

Definition at line 828 of file ui.py.

00828 
00829     def can_examine_locally(self):
00830         '''Check whether to offer the "Examine locally" button.
00831 
00832         This will be true if the report has a core dump, apport-retrace is
00833         installed and a terminal is available (see ui_run_terminal()).
00834         '''
00835         if not self.report or 'CoreDump' not in self.report:
00836             return False
00837 
00838         try:
00839             p = subprocess.Popen(['apport-retrace', '--help'],
00840                                  stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
00841             p.communicate()
00842             if p.returncode != 0:
00843                 return False
00844         except OSError:
00845             return False
00846 
00847         try:
00848             return self.ui_run_terminal(None)
00849         except NotImplementedError:
00850             return False

Here is the call graph for this function:

def apport.ui.UserInterface.check_unreportable (   self) [inherited]
Check if the current report is unreportable.

If so, display an info message and return True.

Definition at line 1186 of file ui.py.

01186 
01187     def check_unreportable(self):
01188         '''Check if the current report is unreportable.
01189 
01190         If so, display an info message and return True.
01191         '''
01192         if not self.crashdb.accepts(self.report):
01193             return False
01194         if 'UnreportableReason' in self.report:
01195             if type(self.report['UnreportableReason']) == bytes:
01196                 self.report['UnreportableReason'] = self.report['UnreportableReason'].decode('UTF-8')
01197             if 'Package' in self.report:
01198                 title = _('Problem in %s') % self.report['Package'].split()[0]
01199             else:
01200                 title = ''
01201             self.ui_info_message(title, _('The problem cannot be reported:\n\n%s') %
01202                                  self.report['UnreportableReason'])
01203             return True
01204         return False

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 62 of file test_ui.py.

00062 
00063     def clear_msg(self):
00064         # last message box
00065         self.msg_title = None
00066         self.msg_text = None
00067         self.msg_severity = None  # 'warning' or 'error'
00068         self.msg_choices = None

def apport.ui.UserInterface.collect_info (   self,
  symptom_script = None,
  ignore_uninstalled = False,
  on_finished = None 
) [inherited]
Collect additional information.

Call all the add_*_info() methods and display a progress dialog during
this.

In particular, this adds OS, package and gdb information and checks bug
patterns.

If a symptom script is given, this will be run first (used by
run_symptom()).

Definition at line 890 of file ui.py.

00890 
00891                      on_finished=None):
00892         '''Collect additional information.
00893 
00894         Call all the add_*_info() methods and display a progress dialog during
00895         this.
00896 
00897         In particular, this adds OS, package and gdb information and checks bug
00898         patterns.
00899 
00900         If a symptom script is given, this will be run first (used by
00901         run_symptom()).
00902         '''
00903         # check if binary changed since the crash happened
00904         if 'ExecutablePath' in self.report and 'ExecutableTimestamp' in self.report:
00905             orig_time = int(self.report['ExecutableTimestamp'])
00906             del self.report['ExecutableTimestamp']
00907             cur_time = int(os.stat(self.report['ExecutablePath']).st_mtime)
00908 
00909             if orig_time != cur_time:
00910                 self.report['UnreportableReason'] = (
00911                     _('The problem happened with the program %s which changed '
00912                       'since the crash occurred.') % self.report['ExecutablePath'])
00913                 return
00914 
00915         if not self.cur_package and 'ExecutablePath' not in self.report \
00916                 and not symptom_script:
00917             # this happens if we file a bug without specifying a PID or a
00918             # package
00919             self.report.add_os_info()
00920         else:
00921             # check if we already ran, skip if so
00922             if (self.report.get('ProblemType') == 'Crash' and 'Stacktrace' in self.report) or (self.report.get('ProblemType') != 'Crash' and 'Dependencies' in self.report):
00923                 if on_finished:
00924                     on_finished()
00925                 return
00926 
00927             # since this might take a while, create separate threads and
00928             # display a progress dialog.
00929             self.ui_start_info_collection_progress()
00930 
00931             hookui = HookUI(self)
00932 
00933             if 'Stacktrace' not in self.report:
00934                 # save original environment, in case hooks change it
00935                 orig_env = os.environ.copy()
00936                 icthread = apport.REThread.REThread(target=thread_collect_info,
00937                                                     name='thread_collect_info',
00938                                                     args=(self.report, self.report_file, self.cur_package,
00939                                                           hookui, symptom_script, ignore_uninstalled))
00940                 icthread.start()
00941                 while icthread.isAlive():
00942                     self.ui_pulse_info_collection_progress()
00943                     try:
00944                         hookui.process_event()
00945                     except KeyboardInterrupt:
00946                         sys.exit(1)
00947 
00948                 icthread.join()
00949 
00950                 # restore original environment
00951                 os.environ.clear()
00952                 os.environ.update(orig_env)
00953 
00954                 icthread.exc_raise()
00955 
00956             if 'CrashDB' in self.report:
00957                 self.crashdb = get_crashdb(None, self.report['CrashDB'])
00958 
00959             # check bug patterns
00960             if self.report['ProblemType'] == 'KernelCrash' or self.report['ProblemType'] == 'KernelOops' or 'Package' in self.report:
00961                 bpthread = apport.REThread.REThread(target=self.report.search_bug_patterns,
00962                                                     args=(self.crashdb.get_bugpattern_baseurl(),))
00963                 bpthread.start()
00964                 while bpthread.isAlive():
00965                     self.ui_pulse_info_collection_progress()
00966                     try:
00967                         bpthread.join(0.1)
00968                     except KeyboardInterrupt:
00969                         sys.exit(1)
00970                 bpthread.exc_raise()
00971                 if bpthread.return_value():
00972                     self.report['KnownReport'] = bpthread.return_value()
00973 
00974             # check crash database if problem is known
00975             if self.report['ProblemType'] != 'Bug':
00976                 known_thread = apport.REThread.REThread(target=self.crashdb.known,
00977                                                         args=(self.report,))
00978                 known_thread.start()
00979                 while known_thread.isAlive():
00980                     self.ui_pulse_info_collection_progress()
00981                     try:
00982                         known_thread.join(0.1)
00983                     except KeyboardInterrupt:
00984                         sys.exit(1)
00985                 known_thread.exc_raise()
00986                 val = known_thread.return_value()
00987                 if val is not None:
00988                     if val is True:
00989                         self.report['KnownReport'] = '1'
00990                     else:
00991                         self.report['KnownReport'] = val
00992 
00993             # anonymize; needs to happen after duplicate checking, otherwise we
00994             # might damage the stack trace
00995             anonymize_thread = apport.REThread.REThread(target=self.report.anonymize)
00996             anonymize_thread.start()
00997             while anonymize_thread.isAlive():
00998                 self.ui_pulse_info_collection_progress()
00999                 try:
01000                     anonymize_thread.join(0.1)
01001                 except KeyboardInterrupt:
01002                     sys.exit(1)
01003             anonymize_thread.exc_raise()
01004 
01005             self.ui_stop_info_collection_progress()
01006 
01007             # check that we were able to determine package names
01008             if ('SourcePackage' not in self.report or
01009                 (not self.report['ProblemType'].startswith('Kernel')
01010                  and 'Package' not in self.report)):
01011                 self.ui_error_message(_('Invalid problem report'),
01012                                       _('Could not determine the package or source package name.'))
01013                 # TODO This is not called consistently, is it really needed?
01014                 self.ui_shutdown()
01015                 sys.exit(1)
01016 
01017         if on_finished:
01018             on_finished()

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.ui.UserInterface.examine (   self) [inherited]
Locally examine crash report.

Definition at line 861 of file ui.py.

00861 
00862     def examine(self):
00863         '''Locally examine crash report.'''
00864 
00865         response = self.ui_question_choice(
00866             _('This will launch apport-retrace in a terminal window to examine the crash.'),
00867             [_('Run gdb session'),
00868              _('Run gdb session without downloading debug symbols'),
00869              #TRANSLATORS: %s contains the crash report file name
00870              _('Update %s with fully symbolic stack trace') % self.report_file,
00871             ],
00872             False)
00873 
00874         if response is None:
00875             return
00876 
00877         retrace_with_download = 'apport-retrace -S system -C %s -v ' % os.path.expanduser(
00878             '~/.cache/apport/retrace')
00879         retrace_no_download = 'apport-retrace '
00880         filearg = "'" + self.report_file.replace("'", "'\\''") + "'"
00881 
00882         cmds = {
00883             0: retrace_with_download + '--gdb ' + filearg,
00884             1: retrace_no_download + '--gdb ' + filearg,
00885             2: retrace_with_download + '--output ' + filearg + ' ' + filearg,
00886         }
00887 
00888         self.ui_run_terminal(cmds[response[0]])

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.ui.UserInterface.file_report (   self) [inherited]
Upload the current report and guide the user to the reporting web page.

Definition at line 1063 of file ui.py.

01063 
01064     def file_report(self):
01065         '''Upload the current report and guide the user to the reporting web page.'''
01066         # FIXME: This behaviour is not really correct, but necessary as
01067         # long as we only support a single crashdb and have whoopsie
01068         # hardcoded. Once we have multiple crash dbs, we need to check
01069         # accepts() earlier, and not even present the data if none of
01070         # the DBs wants the report. See LP#957177 for details.
01071         if not self.crashdb.accepts(self.report):
01072             return
01073         # drop PackageArchitecture if equal to Architecture
01074         if self.report.get('PackageArchitecture') == self.report.get('Architecture'):
01075             try:
01076                 del self.report['PackageArchitecture']
01077             except KeyError:
01078                 pass
01079 
01080         # StacktraceAddressSignature is redundant and does not need to clutter
01081         # the database
01082         try:
01083             del self.report['StacktraceAddressSignature']
01084         except KeyError:
01085             pass
01086 
01087         global __upload_progress
01088         __upload_progress = None
01089 
01090         def progress_callback(sent, total):
01091             global __upload_progress
01092             __upload_progress = float(sent) / total
01093 
01094         self.ui_start_upload_progress()
01095         upthread = apport.REThread.REThread(target=self.crashdb.upload,
01096                                             args=(self.report, progress_callback))
01097         upthread.start()
01098         while upthread.isAlive():
01099             self.ui_set_upload_progress(__upload_progress)
01100             try:
01101                 upthread.join(0.1)
01102                 upthread.exc_raise()
01103             except KeyboardInterrupt:
01104                 sys.exit(1)
01105             except NeedsCredentials as e:
01106                 message = _('Please enter your account information for the '
01107                             '%s bug tracking system')
01108                 data = self.ui_question_userpass(message % excstr(e))
01109                 if data is not None:
01110                     user, password = data
01111                     self.crashdb.set_credentials(user, password)
01112                     upthread = apport.REThread.REThread(target=self.crashdb.upload,
01113                                                         args=(self.report, progress_callback))
01114                     upthread.start()
01115             except (TypeError, SyntaxError, ValueError):
01116                 raise
01117             except Exception as e:
01118                 self.ui_error_message(_('Network problem'),
01119                                       '%s\n\n%s' % (
01120                                           _('Cannot connect to crash database, please check your Internet connection.'),
01121                                           excstr(e)))
01122                 return
01123 
01124         upthread.exc_raise()
01125         ticket = upthread.return_value()
01126         self.ui_stop_upload_progress()
01127 
01128         url = self.crashdb.get_comment_url(self.report, ticket)
01129         if url:
01130             self.open_url(url)

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.ui.UserInterface.finish_hang (   self,
  f 
) [inherited]
Finish processing a hanging application after the core pipe handler
has handed the report back.

This will signal to whoopsie that the report needs to be uploaded.

Definition at line 318 of file ui.py.

00318 
00319     def finish_hang(self, f):
00320         '''Finish processing a hanging application after the core pipe handler
00321         has handed the report back.
00322 
00323         This will signal to whoopsie that the report needs to be uploaded.
00324         '''
00325         apport.fileutils.mark_report_upload(f)
00326         apport.fileutils.mark_report_seen(f)

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.ui.UserInterface.format_filesize (   self,
  size 
) [inherited]
Format the given integer as humanly readable and i18n'ed file size.

Definition at line 788 of file ui.py.

00788 
00789     def format_filesize(self, size):
00790         '''Format the given integer as humanly readable and i18n'ed file size.'''
00791 
00792         if size < 1000000:
00793             return locale.format('%.1f', size / 1000.) + ' KB'
00794         if size < 1000000000:
00795             return locale.format('%.1f', size / 1000000.) + ' MB'
00796         return locale.format('%.1f', size / float(1000000000)) + ' GB'

def apport.ui.UserInterface.get_complete_size (   self) [inherited]
Return the size of the complete report.

Definition at line 797 of file ui.py.

00797 
00798     def get_complete_size(self):
00799         '''Return the size of the complete report.'''
00800 
00801         # report wasn't loaded, so count manually
00802         size = 0
00803         for k in self.report:
00804             if self.report[k]:
00805                 try:
00806                     # if we have a compressed value, take its size, but take
00807                     # base64 overhead into account
00808                     size += len(self.report[k].gzipvalue) * 8 / 6
00809                 except AttributeError:
00810                     size += len(self.report[k])
00811         return size

def apport.ui.UserInterface.get_desktop_entry (   self) [inherited]
Return a .desktop info dictionary for the current report.

Return None if report cannot be associated to a .desktop file.

Definition at line 1205 of file ui.py.

01205 
01206     def get_desktop_entry(self):
01207         '''Return a .desktop info dictionary for the current report.
01208 
01209         Return None if report cannot be associated to a .desktop file.
01210         '''
01211         if 'DesktopFile' in self.report and os.path.exists(self.report['DesktopFile']):
01212             desktop_file = self.report['DesktopFile']
01213         else:
01214             try:
01215                 desktop_file = apport.fileutils.find_package_desktopfile(self.cur_package)
01216             except ValueError:
01217                 return None
01218 
01219         if not desktop_file:
01220             return None
01221 
01222         cp = ConfigParser(interpolation=None)
01223         cp.read(desktop_file, encoding='UTF-8')
01224         if not cp.has_section('Desktop Entry'):
01225             return None
01226         result = dict(cp.items('Desktop Entry'))
01227         if 'name' not in result:
01228             return None
01229         return result

Here is the call graph for this function:

def apport.ui.UserInterface.get_reduced_size (   self) [inherited]
Return the size of the reduced report.

Definition at line 812 of file ui.py.

00812 
00813     def get_reduced_size(self):
00814         '''Return the size of the reduced report.'''
00815 
00816         size = 0
00817         for k in self.report:
00818             if k != 'CoreDump':
00819                 if self.report[k]:
00820                     try:
00821                         # if we have a compressed value, take its size, but take
00822                         # base64 overhead into account
00823                         size += len(self.report[k].gzipvalue) * 8 / 6
00824                     except AttributeError:
00825                         size += len(self.report[k])
00826 
00827         return size

def apport.ui.UserInterface.handle_duplicate (   self) [inherited]
Check if current report matches a bug pattern.

If so, tell the user about it, open the existing bug in a browser, and
return True.

Definition at line 1230 of file ui.py.

01230 
01231     def handle_duplicate(self):
01232         '''Check if current report matches a bug pattern.
01233 
01234         If so, tell the user about it, open the existing bug in a browser, and
01235         return True.
01236         '''
01237         if not self.crashdb.accepts(self.report):
01238             return False
01239         if 'KnownReport' not in self.report:
01240             return False
01241 
01242         # if we have an URL, open it; otherwise this is just a marker that we
01243         # know about it
01244         if self.report['KnownReport'].startswith('http'):
01245             self.ui_info_message(_('Problem already known'),
01246                                  _('This problem was already reported in the bug report displayed \
01247 in the web browser. Please check if you can add any further information that \
01248 might be helpful for the developers.'))
01249 
01250             self.open_url(self.report['KnownReport'])
01251         else:
01252             self.ui_info_message(_('Problem already known'),
01253                                  _('This problem was already reported to developers. Thank you!'))
01254 
01255         return True

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.ui.UserInterface.kill_segv (   self,
  pid 
) [inherited]

Definition at line 372 of file ui.py.

00372 
00373     def kill_segv(self, pid):
00374         os.kill(int(pid), signal.SIGSEGV)

def apport.ui.UserInterface.load_report (   self,
  path 
) [inherited]
Load report from given path and do some consistency checks.

This might issue an error message and return False if the report cannot
be processed, otherwise self.report is initialized and True is
returned.

Definition at line 1131 of file ui.py.

01131 
01132     def load_report(self, path):
01133         '''Load report from given path and do some consistency checks.
01134 
01135         This might issue an error message and return False if the report cannot
01136         be processed, otherwise self.report is initialized and True is
01137         returned.
01138         '''
01139         try:
01140             self.report = apport.Report()
01141             with open(path, 'rb') as f:
01142                 self.report.load(f, binary='compressed')
01143             if 'ProblemType' not in self.report:
01144                 raise ValueError('Report does not contain "ProblemType" field')
01145         except MemoryError:
01146             self.report = None
01147             self.ui_error_message(_('Memory exhaustion'),
01148                                   _('Your system does not have enough memory to process this crash report.'))
01149             return False
01150         except IOError as e:
01151             self.report = None
01152             self.ui_error_message(_('Invalid problem report'), e.strerror)
01153             return False
01154         except (TypeError, ValueError, AssertionError, zlib.error) as e:
01155             self.report = None
01156             self.ui_error_message(_('Invalid problem report'),
01157                                   '%s\n\n%s' % (
01158                                       _('This problem report is damaged and cannot be processed.'),
01159                                       repr(e)))
01160             return False
01161 
01162         if 'Package' in self.report:
01163             self.cur_package = self.report['Package'].split()[0]
01164         else:
01165             self.cur_package = apport.fileutils.find_file_package(self.report.get('ExecutablePath', ''))
01166 
01167         # ensure that the crashed program is still installed:
01168         if self.report['ProblemType'] == 'Crash':
01169             exe_path = self.report.get('ExecutablePath', '')
01170             if not os.path.exists(exe_path):
01171                 msg = _('This problem report applies to a program which is not installed any more.')
01172                 if exe_path:
01173                     msg = '%s (%s)' % (msg, self.report['ExecutablePath'])
01174                 self.report = None
01175                 self.ui_info_message(_('Invalid problem report'), msg)
01176                 return False
01177 
01178             if 'InterpreterPath' in self.report:
01179                 if not os.path.exists(self.report['InterpreterPath']):
01180                     msg = _('This problem report applies to a program which is not installed any more.')
01181                     self.ui_info_message(_('Invalid problem report'), '%s (%s)'
01182                                          % (msg, self.report['InterpreterPath']))
01183                     return False
01184 
01185         return True

Here is the call graph for this function:

Here is the caller graph for this function:

def test_ui.TestSuiteUserInterface.open_url (   self,
  url 
)
Open the given URL in a new browser window.

Display an error dialog if everything fails.

Reimplemented from apport.ui.UserInterface.

Definition at line 105 of file test_ui.py.

00105 
00106     def open_url(self, url):
00107         self.opened_url = url

def apport.ui.UserInterface.parse_argv (   self) [inherited]
Parse command line options.

If a single argument is given without any options, this tries to "do
what I mean".

Definition at line 692 of file ui.py.

00692 
00693     def parse_argv(self):
00694         '''Parse command line options.
00695 
00696         If a single argument is given without any options, this tries to "do
00697         what I mean".
00698         '''
00699         # invoked in update mode?
00700         if len(sys.argv) > 0:
00701             if 'APPORT_INVOKED_AS' in os.environ:
00702                 sys.argv[0] = os.path.join(os.path.dirname(sys.argv[0]),
00703                                            os.path.basename(os.environ['APPORT_INVOKED_AS']))
00704             cmd = sys.argv[0]
00705             if cmd.endswith('-update-bug') or cmd.endswith('-collect'):
00706                 self.parse_argv_update()
00707                 return
00708 
00709         optparser = optparse.OptionParser(_('%prog [options] [symptom|pid|package|program path|.apport/.crash file]'))
00710         optparser.add_option('-f', '--file-bug', action='store_true',
00711                              dest='filebug', default=False,
00712                              help=_('Start in bug filing mode. Requires --package and an optional --pid, or just a --pid. If neither is given, display a list of known symptoms. (Implied if a single argument is given.)'))
00713         optparser.add_option('-w', '--window', action='store_true', default=False,
00714                              help=_('Click a window as a target for filing a problem report.'))
00715         optparser.add_option('-u', '--update-bug', type='int', dest='update_report',
00716                              help=_('Start in bug updating mode. Can take an optional --package.'))
00717         optparser.add_option('-s', '--symptom', metavar='SYMPTOM',
00718                              help=_('File a bug report about a symptom. (Implied if symptom name is given as only argument.)'))
00719         optparser.add_option('-p', '--package',
00720                              help=_('Specify package name in --file-bug mode. This is optional if a --pid is specified. (Implied if package name is given as only argument.)'))
00721         optparser.add_option('-P', '--pid', type='int',
00722                              help=_('Specify a running program in --file-bug mode. If this is specified, the bug report will contain more information.  (Implied if pid is given as only argument.)'))
00723         optparser.add_option('--hanging', action='store_true', default=False,
00724                              help=_('The provided pid is a hanging application.'))
00725         optparser.add_option('-c', '--crash-file', metavar='PATH',
00726                              help=_('Report the crash from given .apport or .crash file instead of the pending ones in %s. (Implied if file is given as only argument.)') % apport.fileutils.report_dir)
00727         optparser.add_option('--save', metavar='PATH',
00728                              help=_('In bug filing mode, save the collected information into a file instead of reporting it. This file can then be reported later on from a different machine.'))
00729         optparser.add_option('--tag', action='append', default=[],
00730                              help=_('Add an extra tag to the report. Can be specified multiple times.'))
00731         optparser.add_option('-v', '--version', action='store_true',
00732                              help=_('Print the Apport version number.'))
00733 
00734         if len(sys.argv) > 0 and cmd.endswith('-bug'):
00735             for o in ('-f', '-u', '-s', '-p', '-P', '-c'):
00736                 optparser.get_option(o).help = optparse.SUPPRESS_HELP
00737 
00738         (self.options, self.args) = optparser.parse_args()
00739 
00740         # "do what I mean" for zero or one arguments
00741         if len(sys.argv) == 0:
00742             return
00743 
00744         # no argument: default to "show pending crashes" except when called in
00745         # bug mode
00746         # NOTE: uses sys.argv, since self.args if empty for all the options,
00747         # e.g. "-v" or "-u $BUG"
00748         if len(sys.argv) == 1 and cmd.endswith('-bug'):
00749             self.options.filebug = True
00750             return
00751 
00752         # one argument: guess "file bug" mode by argument type
00753         if len(self.args) != 1:
00754             return
00755 
00756         # symptom?
00757         if os.path.exists(os.path.join(symptom_script_dir, self.args[0] + '.py')):
00758             self.options.filebug = True
00759             self.options.symptom = self.args[0]
00760             self.args = []
00761 
00762         # .crash/.apport file?
00763         elif self.args[0].endswith('.crash') or self.args[0].endswith('.apport'):
00764             self.options.crash_file = self.args[0]
00765             self.args = []
00766 
00767         # PID?
00768         elif self.args[0].isdigit():
00769             self.options.filebug = True
00770             self.options.pid = self.args[0]
00771             self.args = []
00772 
00773         # executable?
00774         elif '/' in self.args[0]:
00775             pkg = apport.packaging.get_file_package(self.args[0])
00776             if not pkg:
00777                 optparser.error('%s does not belong to a package.' % self.args[0])
00778                 sys.exit(1)
00779             self.args = []
00780             self.options.filebug = True
00781             self.options.package = pkg
00782 
00783         # otherwise: package name
00784         else:
00785             self.options.filebug = True
00786             self.options.package = self.args[0]
00787             self.args = []

Here is the call graph for this function:

def apport.ui.UserInterface.parse_argv_update (   self) [inherited]
Parse command line options when being invoked in update mode.

Return (options, args).

Definition at line 669 of file ui.py.

00669 
00670     def parse_argv_update(self):
00671         '''Parse command line options when being invoked in update mode.
00672 
00673         Return (options, args).
00674         '''
00675         optparser = optparse.OptionParser(_('%prog <report number>'))
00676         optparser.add_option('-p', '--package',
00677                              help=_('Specify package name.'))
00678         optparser.add_option('--tag', action='append', default=[],
00679                              help=_('Add an extra tag to the report. Can be specified multiple times.'))
00680         (self.options, self.args) = optparser.parse_args()
00681 
00682         if len(self.args) != 1 or not self.args[0].isdigit():
00683             optparser.error('You need to specify a report number to update')
00684             sys.exit(1)
00685 
00686         self.options.update_report = int(self.args[0])
00687         self.options.symptom = None
00688         self.options.filebug = False
00689         self.options.crash_file = None
00690         self.options.version = None
00691         self.args = []

Here is the caller graph for this function:

def apport.ui.UserInterface.restart (   self) [inherited]
Reopen the crashed application.

Definition at line 851 of file ui.py.

00851 
00852     def restart(self):
00853         '''Reopen the crashed application.'''
00854 
00855         assert 'ProcCmdline' in self.report
00856 
00857         if os.fork() == 0:
00858             os.setsid()
00859             os.execlp('sh', 'sh', '-c', self.report.get('RespawnCommand', self.report['ProcCmdline']))
00860             sys.exit(1)

Here is the caller graph for this function:

def apport.ui.UserInterface.run_argv (   self) [inherited]
Call appopriate run_* method according to command line arguments.

Return True if at least one report has been processed, and False
otherwise.

Definition at line 619 of file ui.py.

00619 
00620     def run_argv(self):
00621         '''Call appopriate run_* method according to command line arguments.
00622 
00623         Return True if at least one report has been processed, and False
00624         otherwise.
00625         '''
00626         if self.options.symptom:
00627             self.run_symptom()
00628             return True
00629         elif hasattr(self.options, 'pid') and self.options.hanging:
00630             self.run_hang(self.options.pid)
00631             return True
00632         elif self.options.filebug:
00633             return self.run_report_bug()
00634         elif self.options.update_report is not None:
00635             return self.run_update_report()
00636         elif self.options.version:
00637             print(__version__)
00638             return True
00639         elif self.options.crash_file:
00640             try:
00641                 self.run_crash(self.options.crash_file, False)
00642             except OSError as e:
00643                 self.ui_error_message(_('Invalid problem report'), excstr(e))
00644             return True
00645         elif self.options.window:
00646                 self.ui_info_message('', _('After closing this message '
00647                                            'please click on an application window to report a problem about it.'))
00648                 xprop = subprocess.Popen(['xprop', '_NET_WM_PID'],
00649                                          stdout=subprocess.PIPE, stderr=subprocess.PIPE)
00650                 (out, err) = xprop.communicate()
00651                 if xprop.returncode == 0:
00652                     try:
00653                         self.options.pid = int(out.split()[-1])
00654                     except ValueError:
00655                         self.ui_error_message(_('Cannot create report'),
00656                                               _('xprop failed to determine process ID of the window'))
00657                         return True
00658                     return self.run_report_bug()
00659                 else:
00660                     self.ui_error_message(_('Cannot create report'),
00661                                           _('xprop failed to determine process ID of the window') + '\n\n' + err)
00662                     return True
00663         else:
00664             return self.run_crashes()

Here is the call graph for this function:

def apport.ui.UserInterface.run_crash (   self,
  report_file,
  confirm = True 
) [inherited]
Present and report a particular crash.

If confirm is True, ask the user what to do about it, and offer to file
a bug for it.

If confirm is False, the user will not be asked, and the crash is
reported right away.

Definition at line 206 of file ui.py.

00206 
00207     def run_crash(self, report_file, confirm=True):
00208         '''Present and report a particular crash.
00209 
00210         If confirm is True, ask the user what to do about it, and offer to file
00211         a bug for it.
00212 
00213         If confirm is False, the user will not be asked, and the crash is
00214         reported right away.
00215         '''
00216         self.report_file = report_file
00217 
00218         try:
00219             try:
00220                 apport.fileutils.mark_report_seen(report_file)
00221             except OSError:
00222                 # not there any more? no problem, then it won't be regarded as
00223                 # "seen" any more anyway
00224                 pass
00225             if not self.report and not self.load_report(report_file):
00226                 return
00227 
00228             if 'Ignore' in self.report:
00229                 return
00230 
00231             # check for absent CoreDumps (removed if they exceed size limit)
00232             if self.report.get('ProblemType') == 'Crash' and 'Signal' in self.report and 'CoreDump' not in self.report and 'Stacktrace' not in self.report:
00233                 subject = os.path.basename(self.report.get('ExecutablePath', _('unknown program')))
00234                 heading = _('Sorry, the program "%s" closed unexpectedly') % subject
00235                 self.ui_error_message(
00236                     _('Problem in %s') % subject,
00237                     '%s\n\n%s' % (heading, _('Your computer does not have '
00238                     'enough free memory to automatically analyze the problem '
00239                     'and send a report to the developers.')))
00240                 return
00241 
00242             allowed_to_report = apport.fileutils.allowed_to_report()
00243             response = self.ui_present_report_details(allowed_to_report)
00244             if response['report'] or response['examine']:
00245                 try:
00246                     if 'Dependencies' not in self.report:
00247                         self.collect_info()
00248                 except (IOError, zlib.error) as e:
00249                     # can happen with broken core dumps
00250                     self.report = None
00251                     self.ui_error_message(
00252                         _('Invalid problem report'), '%s\n\n%s' % (
00253                             _('This problem report is damaged and cannot be processed.'),
00254                             repr(e)))
00255                     self.ui_shutdown()
00256                     return
00257                 except ValueError:  # package does not exist
00258                     self.ui_error_message(_('Invalid problem report'),
00259                                           _('The report belongs to a package that is not installed.'))
00260                     self.ui_shutdown()
00261                     return
00262                 except Exception as e:
00263                     apport.error(repr(e))
00264                     self.ui_error_message(_('Invalid problem report'),
00265                                           _('An error occurred while attempting to process this'
00266                                             ' problem report:') + '\n\n' + str(e))
00267                     self.ui_shutdown()
00268                     return
00269 
00270             if self.report is None:
00271                 # collect() does that on invalid reports
00272                 return
00273 
00274             if response['examine']:
00275                 self.examine()
00276                 return
00277             if response['restart']:
00278                 self.restart()
00279             if response['blacklist']:
00280                 self.report.mark_ignore()
00281             if not response['report']:
00282                 return
00283 
00284             apport.fileutils.mark_report_upload(report_file)
00285             # We check for duplicates and unreportable crashes here, rather
00286             # than before we show the dialog, as we want to submit these to the
00287             # crash database, but not Launchpad.
00288             if self.crashdb.accepts(self.report):
00289                 # FIXME: This behaviour is not really correct, but necessary as
00290                 # long as we only support a single crashdb and have whoopsie
00291                 # hardcoded. Once we have multiple crash dbs, we need to check
00292                 # accepts() earlier, and not even present the data if none of
00293                 # the DBs wants the report. See LP#957177 for details.
00294                 if self.handle_duplicate():
00295                     return
00296                 if self.check_unreportable():
00297                     return
00298                 self.file_report()
00299         except IOError as e:
00300             # fail gracefully if file is not readable for us
00301             if e.errno in (errno.EPERM, errno.EACCES):
00302                 self.ui_error_message(_('Invalid problem report'),
00303                                       _('You are not allowed to access this problem report.'))
00304                 sys.exit(1)
00305             elif e.errno == errno.ENOSPC:
00306                 self.ui_error_message(_('Error'),
00307                                       _('There is not enough disk space available to process this report.'))
00308                 sys.exit(1)
00309             else:
00310                 self.ui_error_message(_('Invalid problem report'), e.strerror)
00311                 sys.exit(1)
00312         except OSError as e:
00313             # fail gracefully on ENOMEM
00314             if e.errno == errno.ENOMEM:
00315                 apport.fatal('Out of memory, aborting')
00316             else:
00317                 raise

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.ui.UserInterface.run_crashes (   self) [inherited]
Present all currently pending crash reports.

Ask the user what to do about them, and offer to file bugs for them.

Return True if at least one crash report was processed, False
otherwise.

Definition at line 181 of file ui.py.

00181 
00182     def run_crashes(self):
00183         '''Present all currently pending crash reports.
00184 
00185         Ask the user what to do about them, and offer to file bugs for them.
00186 
00187         Return True if at least one crash report was processed, False
00188         otherwise.
00189         '''
00190         result = False
00191 
00192         if os.geteuid() == 0:
00193             reports = apport.fileutils.get_new_system_reports()
00194         else:
00195             reports = apport.fileutils.get_new_reports()
00196         for f in reports:
00197             if not self.load_report(f):
00198                 continue
00199             if self.report['ProblemType'] == 'Hang':
00200                 self.finish_hang(f)
00201             else:
00202                 self.run_crash(f)
00203             result = True
00204 
00205         return result

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.ui.UserInterface.run_hang (   self,
  pid 
) [inherited]
Report an application hanging.

This will first present a dialog containing the information it can
collect from the running application (everything but the trace) with
the option of terminating or restarting the application, optionally
reporting that this error occurred.

A SIGABRT will then be sent to the process and a series of
noninteractive processes will collect the remaining information and
mark the report for uploading.

Definition at line 327 of file ui.py.

00327 
00328     def run_hang(self, pid):
00329         '''Report an application hanging.
00330 
00331         This will first present a dialog containing the information it can
00332         collect from the running application (everything but the trace) with
00333         the option of terminating or restarting the application, optionally
00334         reporting that this error occurred.
00335 
00336         A SIGABRT will then be sent to the process and a series of
00337         noninteractive processes will collect the remaining information and
00338         mark the report for uploading.
00339         '''
00340         self.report = apport.Report('Hang')
00341         self.report.add_proc_info(pid)
00342         self.report.add_package_info()
00343         path = self.report.get('ExecutablePath', '')
00344         self.cur_package = apport.fileutils.find_file_package(path)
00345         self.report.add_os_info()
00346         allowed_to_report = apport.fileutils.allowed_to_report()
00347         response = self.ui_present_report_details(allowed_to_report,
00348                                                   modal_for=pid)
00349         if response['report']:
00350             apport.fileutils.mark_hanging_process(self.report, pid)
00351             os.kill(int(pid), signal.SIGABRT)
00352         else:
00353             os.kill(int(pid), signal.SIGKILL)
00354 
00355         if response['restart']:
00356             self.wait_for_pid(pid)
00357             self.restart()

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.ui.UserInterface.run_report_bug (   self,
  symptom_script = None 
) [inherited]
Report a bug.

If a pid is given on the command line, the report will contain runtime
debug information. Either a package or a pid must be specified; if none
is given, show a list of symptoms.

If a symptom script is given, this will be run first (used by
run_symptom()).

Definition at line 375 of file ui.py.

00375 
00376     def run_report_bug(self, symptom_script=None):
00377         '''Report a bug.
00378 
00379         If a pid is given on the command line, the report will contain runtime
00380         debug information. Either a package or a pid must be specified; if none
00381         is given, show a list of symptoms.
00382 
00383         If a symptom script is given, this will be run first (used by
00384         run_symptom()).
00385         '''
00386         if not self.options.package and not self.options.pid and \
00387                 not symptom_script:
00388             if self.run_symptoms():
00389                 return True
00390             else:
00391                 self.ui_error_message(_('No package specified'),
00392                                       _('You need to specify a package or a PID. See --help for more information.'))
00393             return False
00394 
00395         self.report = apport.Report('Bug')
00396 
00397         # if PID is given, add info
00398         if self.options.pid:
00399             try:
00400                 with open('/proc/%s/stat' % self.options.pid) as f:
00401                     stat = f.read().split()
00402                 flags = int(stat[8])
00403                 if flags & PF_KTHREAD:
00404                     # this PID is a kernel thread
00405                     self.options.package = 'linux'
00406                 else:
00407                     self.report.add_proc_info(self.options.pid)
00408             except (ValueError, IOError):
00409                 self.ui_error_message(_('Invalid PID'),
00410                                       _('The specified process ID does not belong to a program.'))
00411                 return False
00412             except OSError as e:
00413                 # silently ignore nonexisting PIDs; the user must not close the
00414                 # application prematurely
00415                 if e.errno == errno.ENOENT:
00416                     return False
00417                 elif e.errno == errno.EACCES:
00418                     self.ui_error_message(_('Permission denied'),
00419                                           _('The specified process does not belong to you. Please run this program as the process owner or as root.'))
00420                     return False
00421                 else:
00422                     raise
00423         else:
00424             self.report.add_proc_environ()
00425 
00426         if self.options.package:
00427             self.options.package = self.options.package.strip()
00428         # "Do what I mean" for filing against "linux"
00429         if self.options.package == 'linux':
00430             self.cur_package = apport.packaging.get_kernel_package()
00431         else:
00432             self.cur_package = self.options.package
00433 
00434         try:
00435             self.collect_info(symptom_script)
00436         except ValueError as e:
00437             if str(e) == 'package does not exist':
00438                 if not self.cur_package:
00439                     self.ui_error_message(_('Invalid problem report'),
00440                                           _('Symptom script %s did not determine an affected package') % symptom_script)
00441                 else:
00442                     self.ui_error_message(_('Invalid problem report'),
00443                                           _('Package %s does not exist') % self.cur_package)
00444                 return False
00445             else:
00446                 raise
00447 
00448         if self.check_unreportable():
00449             return
00450 
00451         self.add_extra_tags()
00452 
00453         if self.handle_duplicate():
00454             return True
00455 
00456         # not useful for bug reports, and has potentially sensitive information
00457         try:
00458             del self.report['ProcCmdline']
00459         except KeyError:
00460             pass
00461 
00462         if self.options.save:
00463             try:
00464                 with open(os.path.expanduser(self.options.save), 'wb') as f:
00465                     self.report.write(f)
00466             except (IOError, OSError) as e:
00467                 self.ui_error_message(_('Cannot create report'), excstr(e))
00468         else:
00469             # show what's being sent
00470             allowed_to_report = apport.fileutils.allowed_to_report()
00471             response = self.ui_present_report_details(allowed_to_report)
00472             if response['report']:
00473                 self.file_report()
00474 
00475         return True

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.ui.UserInterface.run_symptom (   self) [inherited]
Report a bug with a symptom script.

Definition at line 608 of file ui.py.

00608 
00609     def run_symptom(self):
00610         '''Report a bug with a symptom script.'''
00611 
00612         script = os.path.join(symptom_script_dir, self.options.symptom + '.py')
00613         if not os.path.exists(script):
00614             self.ui_error_message(_('Unknown symptom'),
00615                                   _('The symptom "%s" is not known.') % self.options.symptom)
00616             return
00617 
00618         self.run_report_bug(script)

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.ui.UserInterface.run_symptoms (   self) [inherited]
Report a bug from a list of available symptoms.

Return False if no symptoms are available.

Definition at line 559 of file ui.py.

00559 
00560     def run_symptoms(self):
00561         '''Report a bug from a list of available symptoms.
00562 
00563         Return False if no symptoms are available.
00564         '''
00565         scripts = glob.glob(os.path.join(symptom_script_dir, '*.py'))
00566 
00567         symptom_names = []
00568         symptom_descriptions = []
00569         for script in scripts:
00570             # scripts with an underscore can be used for private libraries
00571             if os.path.basename(script).startswith('_'):
00572                 continue
00573             symb = {}
00574             try:
00575                 with open(script) as f:
00576                     exec(compile(f.read(), script, 'exec'), symb)
00577             except:
00578                 apport.error('symptom script %s is invalid', script)
00579                 traceback.print_exc()
00580                 continue
00581             if 'run' not in symb:
00582                 apport.error('symptom script %s does not define run() function', script)
00583                 continue
00584             symptom_names.append(os.path.splitext(os.path.basename(script))[0])
00585             symptom_descriptions.append(symb.get('description', symptom_names[-1]))
00586 
00587         if not symptom_names:
00588             return False
00589 
00590         symptom_descriptions, symptom_names = \
00591             zip(*sorted(zip(symptom_descriptions, symptom_names)))
00592         symptom_descriptions = list(symptom_descriptions)
00593         symptom_names = list(symptom_names)
00594         symptom_names.append(None)
00595         symptom_descriptions.append('Other problem')
00596 
00597         ch = self.ui_question_choice(_('What kind of problem do you want to report?'),
00598                                      symptom_descriptions, False)
00599 
00600         if ch is not None:
00601             symptom = symptom_names[ch[0]]
00602             if symptom:
00603                 self.run_report_bug(os.path.join(symptom_script_dir, symptom + '.py'))
00604             else:
00605                 return False
00606 
00607         return True

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.ui.UserInterface.run_update_report (   self) [inherited]
Update an existing bug with locally collected information.

Definition at line 476 of file ui.py.

00476 
00477     def run_update_report(self):
00478         '''Update an existing bug with locally collected information.'''
00479 
00480         # avoid irrelevant noise
00481         if not self.crashdb.can_update(self.options.update_report):
00482             self.ui_error_message(_('Updating problem report'),
00483                                   _('You are not the reporter or subscriber of this '
00484                                     'problem report, or the report is a duplicate or already '
00485                                     'closed.\n\nPlease create a new report using "apport-bug".'))
00486             return False
00487 
00488         is_reporter = self.crashdb.is_reporter(self.options.update_report)
00489 
00490         if not is_reporter:
00491             r = self.ui_question_yesno(
00492                 _('You are not the reporter of this problem report. It '
00493                   'is much easier to mark a bug as a duplicate of another '
00494                   'than to move your comments and attachments to a new bug.\n\n'
00495                   'Subsequently, we recommend that you file a new bug report '
00496                   'using "apport-bug" and make a comment in this bug about '
00497                   'the one you file.\n\n'
00498                   'Do you really want to proceed?'))
00499             if not r:
00500                 return False
00501 
00502         # list of affected source packages
00503         self.report = apport.Report('Bug')
00504         if self.options.package:
00505             pkgs = [self.options.package.strip()]
00506         else:
00507             pkgs = self.crashdb.get_affected_packages(self.options.update_report)
00508 
00509         info_collected = False
00510         for p in pkgs:
00511             #print('Collecting apport information for source package %s...' % p)
00512             self.cur_package = p
00513             self.report['SourcePackage'] = p
00514             self.report['Package'] = p  # no way to find this out
00515 
00516             # we either must have the package installed or a source package hook
00517             # available to collect sensible information
00518             try:
00519                 apport.packaging.get_version(p)
00520             except ValueError:
00521                 if not os.path.exists(os.path.join(apport.report._hook_dir, 'source_%s.py' % p)):
00522                     print('Package %s not installed and no hook available, ignoring' % p)
00523                     continue
00524             self.collect_info(ignore_uninstalled=True)
00525             info_collected = True
00526 
00527         if not info_collected:
00528             self.ui_info_message(_('Updating problem report'),
00529                                  _('No additional information collected.'))
00530             return False
00531 
00532         self.report.add_user_info()
00533         self.report.add_proc_environ()
00534         self.add_extra_tags()
00535 
00536         # delete the uninteresting keys
00537         del self.report['ProblemType']
00538         del self.report['Date']
00539         try:
00540             del self.report['SourcePackage']
00541         except KeyError:
00542             pass
00543 
00544         if len(self.report) == 0:
00545             self.ui_info_message(_('Updating problem report'),
00546                                  _('No additional information collected.'))
00547             return False
00548 
00549         # show what's being sent
00550         allowed_to_report = apport.fileutils.allowed_to_report()
00551         response = self.ui_present_report_details(allowed_to_report)
00552         if response['report']:
00553             self.crashdb.update(self.options.update_report, self.report,
00554                                 'apport information', change_description=is_reporter,
00555                                 attachment_comment='apport information')
00556             return True
00557 
00558         return False

Here is the call graph for this function:

Here is the caller graph for this function:

def test_ui.TestSuiteUserInterface.ui_error_message (   self,
  title,
  text 
)
Show an error message box with given title and text.

Reimplemented from apport.ui.UserInterface.

Definition at line 78 of file test_ui.py.

00078 
00079     def ui_error_message(self, title, text):
00080         self.msg_title = title
00081         self.msg_text = text
00082         self.msg_severity = 'error'

def test_ui.TestSuiteUserInterface.ui_info_message (   self,
  title,
  text 
)
Show an information message box with given title and text.

Reimplemented from apport.ui.UserInterface.

Definition at line 73 of file test_ui.py.

00073 
00074     def ui_info_message(self, title, text):
00075         self.msg_title = title
00076         self.msg_text = text
00077         self.msg_severity = 'info'

def test_ui.TestSuiteUserInterface.ui_present_report_details (   self,
  allowed_to_report 
)
Show details of the bug report.

Return the action and options as a dictionary:

- Valid keys are: report the crash ('report'), restart
  the crashed application ('restart'), or blacklist further crashes
  ('blacklist').

Reimplemented from apport.ui.UserInterface.

Definition at line 69 of file test_ui.py.

00069 
00070     def ui_present_report_details(self, is_update):
00071         self.present_details_shown = True
00072         return self.present_details_response

Advance the data collection progress bar.

This function is called every 100 ms.

Reimplemented from apport.ui.UserInterface.

Definition at line 87 of file test_ui.py.

00087 
00088     def ui_pulse_info_collection_progress(self):
00089         assert self.ic_progress_active
00090         self.ic_progress_pulses += 1

def test_ui.TestSuiteUserInterface.ui_question_choice (   self,
  text,
  options,
  multiple 
)
Show an question with predefined choices.

options is a list of strings to present. If multiple is True, they
should be check boxes, if multiple is False they should be radio
buttons.

Return list of selected option indexes, or None if the user cancelled.
If multiple == False, the list will always have one element.

Reimplemented from apport.ui.UserInterface.

Definition at line 112 of file test_ui.py.

00112 
00113     def ui_question_choice(self, text, options, multiple):
00114         self.msg_text = text
00115         self.msg_choices = options
00116         return self.question_choice_response

Show a file selector dialog.

Return path if the user selected a file, or None if cancelled.

Reimplemented from apport.ui.UserInterface.

Definition at line 117 of file test_ui.py.

00117 
00118     def ui_question_file(self, text):
00119         self.msg_text = text
00120         return self.question_file_response
00121 

def apport.ui.UserInterface.ui_question_userpass (   self,
  message 
) [inherited]
Request username and password from user.

message is the text to be presented to the user when requesting for
username and password information.

Return a tuple (username, password), or None if cancelled.

Definition at line 1383 of file ui.py.

01383 
01384     def ui_question_userpass(self, message):
01385         '''Request username and password from user.
01386 
01387         message is the text to be presented to the user when requesting for
01388         username and password information.
01389 
01390         Return a tuple (username, password), or None if cancelled.
01391         '''
01392         raise NotImplementedError('this function must be overridden by subclasses')
01393 

Here is the caller graph for this function:

Show a yes/no question.

Return True if the user selected "Yes", False if selected "No" or
"None" on cancel/dialog closing.

Reimplemented from apport.ui.UserInterface.

Definition at line 108 of file test_ui.py.

00108 
00109     def ui_question_yesno(self, text):
00110         self.msg_text = text
00111         return self.question_yesno_response

def apport.ui.UserInterface.ui_run_terminal (   self,
  command 
) [inherited]
Run command in, or check for a terminal window.

If command is given, run command in a terminal window; raise an exception
if terminal cannot be opened.

If command is None, merely check if a terminal application is available
and can be launched.

Definition at line 1340 of file ui.py.

01340 
01341     def ui_run_terminal(self, command):
01342         '''Run command in, or check for a terminal window.
01343 
01344         If command is given, run command in a terminal window; raise an exception
01345         if terminal cannot be opened.
01346 
01347         If command is None, merely check if a terminal application is available
01348         and can be launched.
01349         '''
01350         raise NotImplementedError('this function must be overridden by subclasses')

Here is the caller graph for this function:

Update data upload progress bar.

Set the progress bar in the debug data upload progress window to the
given ratio (between 0 and 1, or None for indefinite progress).

This function is called every 100 ms.

Reimplemented from apport.ui.UserInterface.

Definition at line 98 of file test_ui.py.

00098 
00099     def ui_set_upload_progress(self, progress):
00100         assert self.upload_progress_active
00101         self.upload_progress_pulses += 1

def apport.ui.UserInterface.ui_shutdown (   self) [inherited]
Called right before terminating the program.

This can be used for for cleaning up.

Definition at line 1333 of file ui.py.

01333 
01334     def ui_shutdown(self):
01335         '''Called right before terminating the program.
01336 
01337         This can be used for for cleaning up.
01338         '''
01339         pass

Here is the caller graph for this function:

Open a indefinite progress bar for data collection.

This tells the user to wait while debug information is being
collected.

Reimplemented from apport.ui.UserInterface.

Definition at line 83 of file test_ui.py.

00083 
00084     def ui_start_info_collection_progress(self):
00085         self.ic_progress_pulses = 0
00086         self.ic_progress_active = True

Open progress bar for data upload.

This tells the user to wait while debug information is being uploaded.

Reimplemented from apport.ui.UserInterface.

Definition at line 94 of file test_ui.py.

00094 
00095     def ui_start_upload_progress(self):
00096         self.upload_progress_pulses = 0
00097         self.upload_progress_active = True

Close debug data collection progress window.

Reimplemented from apport.ui.UserInterface.

Definition at line 91 of file test_ui.py.

00091 
00092     def ui_stop_info_collection_progress(self):
00093         self.ic_progress_active = False

Close debug data upload progress window.

Reimplemented from apport.ui.UserInterface.

Definition at line 102 of file test_ui.py.

00102 
00103     def ui_stop_upload_progress(self):
00104         self.upload_progress_active = False

def apport.ui.UserInterface.wait_for_pid (   self,
  pid 
) [inherited]
waitpid() does not work for non-child processes. Query the process
state in a loop, waiting for "no such process."

Definition at line 358 of file ui.py.

00358 
00359     def wait_for_pid(self, pid):
00360         '''waitpid() does not work for non-child processes. Query the process
00361         state in a loop, waiting for "no such process."
00362         '''
00363         while True:
00364             try:
00365                 os.kill(int(pid), 0)
00366             except OSError as e:
00367                 if e.errno == errno.ESRCH:
00368                     break
00369                 else:
00370                     raise
00371             time.sleep(1)

Here is the caller graph for this function:


Member Data Documentation

Definition at line 690 of file ui.py.

Reimplemented from apport.ui.UserInterface.

Definition at line 40 of file test_ui.py.

Definition at line 25 of file test_ui.py.

Definition at line 164 of file ui.py.

Definition at line 161 of file ui.py.

Definition at line 44 of file test_ui.py.

Definition at line 45 of file test_ui.py.

Definition at line 67 of file test_ui.py.

Definition at line 66 of file test_ui.py.

Definition at line 65 of file test_ui.py.

Definition at line 64 of file test_ui.py.

Definition at line 57 of file test_ui.py.

Definition at line 52 of file test_ui.py.

Definition at line 58 of file test_ui.py.

Definition at line 51 of file test_ui.py.

Definition at line 50 of file test_ui.py.

Definition at line 54 of file test_ui.py.

Definition at line 55 of file test_ui.py.

Definition at line 53 of file test_ui.py.

Definition at line 162 of file ui.py.

Definition at line 163 of file ui.py.

Definition at line 46 of file test_ui.py.

Definition at line 47 of file test_ui.py.


The documentation for this class was generated from the following file: