Merge lp:~cr3/checkbox/warning_prompt into lp:checkbox

Proposed by Marc Tardif
Status: Merged
Approved by: Sylvain Pineau
Approved revision: 1781
Merged at revision: 1785
Proposed branch: lp:~cr3/checkbox/warning_prompt
Merge into: lp:checkbox
Diff against target: 395 lines (+168/-85)
3 files modified
checkbox/user_interface.py (+98/-70)
plugins/jobs_info.py (+35/-15)
plugins/warning_prompt.py (+35/-0)
To merge this branch: bzr merge lp:~cr3/checkbox/warning_prompt
Reviewer Review Type Date Requested Status
Sylvain Pineau (community) Approve
Review via email: mp+130193@code.launchpad.net

Commit message

Merged warning_prompt plugin for whitelist validation warnings by cr3.

Description of the change

Added warning_prompt plugin to prevent the reactor from stopping on some warnings when run non-interactively.

To post a comment you must log in.
Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :

There's a lot of pep8 fixes but i'm happy with the skeleton of show_warning() in base checkbox/user_interface.py.

Thanks

review: Approve
Revision history for this message
Marc Tardif (cr3) wrote :

Thanks, you can now work on having a nice user interface for Qt specifically at your leisure by overriding the show_warning method in the qt interface class.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'checkbox/user_interface.py'
--- checkbox/user_interface.py 2012-07-23 20:50:14 +0000
+++ checkbox/user_interface.py 2012-10-17 19:06:24 +0000
@@ -29,10 +29,20 @@
2929
30from checkbox.contrib.REThread import REThread30from checkbox.contrib.REThread import REThread
3131
32from checkbox.lib.environ import add_variable, get_variable, remove_variable32from checkbox.lib.environ import (
33 add_variable,
34 get_variable,
35 remove_variable,
36 )
3337
34from checkbox.job import (FAIL, PASS, UNINITIATED,38from checkbox.job import (
35 UNRESOLVED, UNSUPPORTED, UNTESTED)39 FAIL,
40 PASS,
41 UNINITIATED,
42 UNRESOLVED,
43 UNSUPPORTED,
44 UNTESTED,
45 )
36from checkbox.reactor import StopAllException46from checkbox.reactor import StopAllException
3747
3848
@@ -83,13 +93,22 @@
83 logging.info(text)93 logging.info(text)
84 return default94 return default
8595
86 def show_error(self, primary_text,96 def show_error(
87 secondary_text=None, detailed_text=None):97 self, primary_text, secondary_text=None, detailed_text=None):
88 text = filter(None, [primary_text, secondary_text, detailed_text])98 text = filter(None, [primary_text, secondary_text, detailed_text])
89 text = '\n'.join(text)99 text = '\n'.join(text)
90 logging.error(text)100 logging.error(text)
91 raise StopAllException("Error: %s" % text)101 raise StopAllException("Error: %s" % text)
92102
103 def show_warning(
104 self, primary_text, secondary_text=None, detailed_text=None):
105 try:
106 self.show_error(primary_text, secondary_text, detailed_text)
107 except StopAllException:
108 # The only difference with show_error for now is that warnings
109 # don't stop the reactor.
110 pass
111
93 def show_progress(self, message, function, *args, **kwargs):112 def show_progress(self, message, function, *args, **kwargs):
94 self.show_progress_start(message)113 self.show_progress_start(message)
95114
@@ -138,8 +157,8 @@
138157
139 Display an error dialog if everything fails."""158 Display an error dialog if everything fails."""
140159
141 # If we are called through sudo, determine the real user id and run the160 # If we are called through sudo, determine the real user id and
142 # browser with it to get the user's web browser settings.161 # run the browser with it to get the user's web browser settings.
143 try:162 try:
144 uid = int(get_variable("SUDO_UID"))163 uid = int(get_variable("SUDO_UID"))
145 gid = int(get_variable("SUDO_GID"))164 gid = int(get_variable("SUDO_GID"))
@@ -149,67 +168,78 @@
149 gid = None168 gid = None
150 sudo_prefix = []169 sudo_prefix = []
151170
152 # figure out appropriate web browser171 # if ksmserver is running, try kfmclient
153 try:172 try:
154 # if ksmserver is running, try kfmclient173 if (os.getenv("DISPLAY") and
155 try:174 subprocess.call(
156 if os.getenv("DISPLAY") and \175 ["pgrep", "-x", "-u", str(uid), "ksmserver"],
157 subprocess.call(["pgrep", "-x", "-u", str(uid), "ksmserver"],176 stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0):
158 stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0:177 subprocess.Popen(sudo_prefix + ["kfmclient", "openURL", url])
159 subprocess.Popen(sudo_prefix + ["kfmclient", "openURL", url])178 return
160 return179 except OSError:
161 except OSError:180 pass
162 pass181
163182 # if gnome-session is running, try gnome-open; special-case
164 # if gnome-session is running, try gnome-open; special-case firefox183 # firefox (and more generally, mozilla browsers) and epiphany
165 # (and more generally, mozilla browsers) and epiphany to open a new window184 # to open a new window with respectively -new-window and
166 # with respectively -new-window and --new-window; special-case chromium-browser185 # --new-window; special-case chromium-browser to allow file://
167 # to allow file:// URLs as needed by the checkbox report.186 # URLs as needed by the checkbox report.
168 try:187 try:
169 if os.getenv("DISPLAY") and \188 if (os.getenv("DISPLAY") and
170 subprocess.call(["pgrep", "-x", "-u", str(uid), "gnome-panel|gconfd-2"],189 subprocess.call(
171 stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0:190 ["pgrep", "-x", "-u", str(uid), "gnome-panel|gconfd-2"],
172 from gi.repository import Gio191 stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0):
173192 from gi.repository import Gio
174 preferred_xml_app = Gio.app_info_get_default_for_type("application/xml",False)193
175 if preferred_xml_app:194 preferred_xml_app = Gio.app_info_get_default_for_type(
176 preferred_browser = preferred_xml_app.get_executable()195 "application/xml", False)
177 browser = re.match("((firefox|seamonkey|flock)[^\s]*)", preferred_browser)196 if preferred_xml_app:
178 if browser:197 preferred_browser = preferred_xml_app.get_executable()
179 subprocess.Popen(sudo_prefix + [browser.group(0), "-new-window", url])198 browser = re.match(
180 return199 "((firefox|seamonkey|flock)[^\s]*)",
181200 preferred_browser)
182 browser = re.match("(epiphany[^\s]*)", preferred_browser)201 if browser:
183 if browser:202 subprocess.Popen(
184 subprocess.Popen(sudo_prefix + [browser.group(0), "--new-window", url])203 sudo_prefix +
185 return204 [browser.group(0), "-new-window", url])
186 205 return
187 browser = re.match("(chromium-browser[^\s]*)", preferred_browser)206
188 if browser:207 browser = re.match("(epiphany[^\s]*)", preferred_browser)
189 subprocess.Popen(sudo_prefix + [browser.group(0), "--allow-file-access-from-files", url])208 if browser:
190 return209 subprocess.Popen(
191210 sudo_prefix +
192 subprocess.Popen(sudo_prefix + [preferred_browser % url], shell=True)211 [browser.group(0), "--new-window", url])
193 return212 return
194213
195 subprocess.Popen(sudo_prefix + ["gnome-open", url])214 browser = re.match(
196 return215 "(chromium-browser[^\s]*)", preferred_browser)
197 except OSError:216 if browser:
198 pass217 subprocess.Popen(
199218 sudo_prefix +
200 # fall back to webbrowser219 [browser.group(0),
201 if uid and gid:220 "--allow-file-access-from-files", url])
202 os.setgroups([gid])221 return
203 os.setgid(gid)222
204 os.setuid(uid)223 subprocess.Popen(
205 remove_variable("SUDO_USER")224 sudo_prefix +
206 add_variable("HOME", pwd.getpwuid(uid).pw_dir)225 [preferred_browser % url], shell=True)
207226 return
208 webbrowser.open(url, new=True, autoraise=True)227
209 return228 subprocess.Popen(sudo_prefix + ["gnome-open", url])
210229 return
211 except Exception as e:230 except OSError:
212 pass231 pass
232
233 # fall back to webbrowser
234 if uid and gid:
235 os.setgroups([gid])
236 os.setgid(gid)
237 os.setuid(uid)
238 remove_variable("SUDO_USER")
239 add_variable("HOME", pwd.getpwuid(uid).pw_dir)
240
241 webbrowser.open(url, new=True, autoraise=True)
242 return
213243
214 def show_report(self, text, results):244 def show_report(self, text, results):
215 """245 """
@@ -225,5 +255,3 @@
225 about each job.255 about each job.
226 """256 """
227 pass257 pass
228
229
230258
=== modified file 'plugins/jobs_info.py'
--- plugins/jobs_info.py 2012-06-26 14:07:30 +0000
+++ plugins/jobs_info.py 2012-10-17 19:06:24 +0000
@@ -16,18 +16,29 @@
16# You should have received a copy of the GNU General Public License16# You should have received a copy of the GNU General Public License
17# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.17# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
18#18#
19import os, sys, re19import os
20import re
21import sys
22
20import difflib23import difflib
21import gettext24import gettext
22import logging25import logging
2326
24from collections import defaultdict27from collections import defaultdict
28from gettext import gettext as _
2529
26from checkbox.lib.resolver import Resolver30from checkbox.lib.resolver import Resolver
2731
28from checkbox.arguments import coerce_arguments32from checkbox.arguments import coerce_arguments
29from checkbox.properties import Float, Int, List, Map, Path, String
30from checkbox.plugin import Plugin33from checkbox.plugin import Plugin
34from checkbox.properties import (
35 Float,
36 Int,
37 List,
38 Map,
39 Path,
40 String,
41 )
3142
3243
33job_schema = Map({44job_schema = Map({
@@ -76,14 +87,17 @@
76 def register(self, manager):87 def register(self, manager):
77 super(JobsInfo, self).register(manager)88 super(JobsInfo, self).register(manager)
7889
79 self.whitelist_patterns = self.get_patterns(self.whitelist, self.whitelist_file)90 self.whitelist_patterns = self.get_patterns(
80 self.blacklist_patterns = self.get_patterns(self.blacklist, self.blacklist_file)91 self.whitelist, self.whitelist_file)
92 self.blacklist_patterns = self.get_patterns(
93 self.blacklist, self.blacklist_file)
81 self.selected_jobs = defaultdict(list)94 self.selected_jobs = defaultdict(list)
8295
83 self._manager.reactor.call_on("prompt-begin", self.prompt_begin)96 self._manager.reactor.call_on("prompt-begin", self.prompt_begin)
84 self._manager.reactor.call_on("gather", self.gather)97 self._manager.reactor.call_on("gather", self.gather)
85 if logging.getLogger().getEffectiveLevel() <= logging.DEBUG:98 if logging.getLogger().getEffectiveLevel() <= logging.DEBUG:
86 self._manager.reactor.call_on("prompt-gather", self.post_gather, 90)99 self._manager.reactor.call_on(
100 "prompt-gather", self.post_gather, 90)
87 self._manager.reactor.call_on("report-job", self.report_job, -100)101 self._manager.reactor.call_on("report-job", self.report_job, -100)
88102
89 def prompt_begin(self, interface):103 def prompt_begin(self, interface):
@@ -92,7 +106,8 @@
92 to display errors106 to display errors
93 """107 """
94 self.interface = interface108 self.interface = interface
95 self.unused_patterns = self.whitelist_patterns + self.blacklist_patterns109 self.unused_patterns = (
110 self.whitelist_patterns + self.blacklist_patterns)
96111
97 def check_ordered_messages(self, messages):112 def check_ordered_messages(self, messages):
98 """Return whether the list of messages are ordered or not."""113 """Return whether the list of messages are ordered or not."""
@@ -113,8 +128,8 @@
113 try:128 try:
114 file = open(filename)129 file = open(filename)
115 except IOError as e:130 except IOError as e:
116 error_message=(gettext.gettext("Failed to open file '%s': %s") %131 error_message = (_("Failed to open file '%s': %s")
117 (filename, e.strerror))132 % (filename, e.strerror))
118 logging.critical(error_message)133 logging.critical(error_message)
119 sys.stderr.write("%s\n" % error_message)134 sys.stderr.write("%s\n" % error_message)
120 sys.exit(os.EX_NOINPUT)135 sys.exit(os.EX_NOINPUT)
@@ -148,7 +163,9 @@
148 def report_message(message):163 def report_message(message):
149 if self.whitelist_patterns:164 if self.whitelist_patterns:
150 name = message["name"]165 name = message["name"]
151 if not [name for p in self.whitelist_patterns if p.match(name)]:166 names = [name for p in self.whitelist_patterns
167 if p.match(name)]
168 if not names:
152 return169 return
153170
154 messages.append(message)171 messages.append(message)
@@ -156,7 +173,8 @@
156 # Set domain and message event handler173 # Set domain and message event handler
157 old_domain = gettext.textdomain()174 old_domain = gettext.textdomain()
158 gettext.textdomain(self.domain)175 gettext.textdomain(self.domain)
159 event_id = self._manager.reactor.call_on("report-message", report_message, 100)176 event_id = self._manager.reactor.call_on(
177 "report-message", report_message, 100)
160178
161 for directory in self.directories:179 for directory in self.directories:
162 self._manager.reactor.fire("message-directory", directory)180 self._manager.reactor.fire("message-directory", directory)
@@ -182,7 +200,8 @@
182 messages = sorted(messages, key=key_function)200 messages = sorted(messages, key=key_function)
183201
184 if not self.check_ordered_messages(messages):202 if not self.check_ordered_messages(messages):
185 old_message_names = [message["name"] + "\n" for message in messages]203 old_message_names = [
204 message["name"] + "\n" for message in messages]
186 resolver = Resolver(key_func=lambda m: m["name"])205 resolver = Resolver(key_func=lambda m: m["name"])
187 for message in messages:206 for message in messages:
188 resolver.add(207 resolver.add(
@@ -192,7 +211,8 @@
192 # Check if messages are already topologically ordered211 # Check if messages are already topologically ordered
193 if (self.whitelist_patterns and212 if (self.whitelist_patterns and
194 logging.getLogger().getEffectiveLevel() <= logging.DEBUG):213 logging.getLogger().getEffectiveLevel() <= logging.DEBUG):
195 new_message_names = [message["name"] + "\n" for message in messages]214 new_message_names = [
215 message["name"] + "\n" for message in messages]
196 detailed_text = "".join(216 detailed_text = "".join(
197 difflib.unified_diff(217 difflib.unified_diff(
198 old_message_names,218 old_message_names,
@@ -200,7 +220,7 @@
200 "old whitelist",220 "old whitelist",
201 "new whitelist"))221 "new whitelist"))
202 self._manager.reactor.fire(222 self._manager.reactor.fire(
203 "prompt-error",223 "prompt-warning",
204 self.interface,224 self.interface,
205 "Whitelist not topologically ordered",225 "Whitelist not topologically ordered",
206 "Jobs will be reordered to fix broken dependencies",226 "Jobs will be reordered to fix broken dependencies",
@@ -231,7 +251,7 @@
231 'Please make sure that the patterns you used are up-to-date\n'251 'Please make sure that the patterns you used are up-to-date\n'
232 .format('\n'.join(['- {0}'.format(tc)252 .format('\n'.join(['- {0}'.format(tc)
233 for tc in orphan_test_cases])))253 for tc in orphan_test_cases])))
234 self._manager.reactor.fire('prompt-error', self.interface,254 self._manager.reactor.fire('prompt-warning', self.interface,
235 'Orphan test cases detected',255 'Orphan test cases detected',
236 "Some test cases aren't included "256 "Some test cases aren't included "
237 'in any test suite',257 'in any test suite',
@@ -244,7 +264,7 @@
244 "Please make sure that the patterns you used are up-to-date\n"264 "Please make sure that the patterns you used are up-to-date\n"
245 .format('\n'.join(['- {0}'.format(p.pattern[1:-1])265 .format('\n'.join(['- {0}'.format(p.pattern[1:-1])
246 for p in self.unused_patterns])))266 for p in self.unused_patterns])))
247 self._manager.reactor.fire('prompt-error', self.interface,267 self._manager.reactor.fire('prompt-warning', self.interface,
248 'Unused patterns',268 'Unused patterns',
249 'Please make sure that the patterns '269 'Please make sure that the patterns '
250 'you used are up-to-date',270 'you used are up-to-date',
251271
=== added file 'plugins/warning_prompt.py'
--- plugins/warning_prompt.py 1970-01-01 00:00:00 +0000
+++ plugins/warning_prompt.py 2012-10-17 19:06:24 +0000
@@ -0,0 +1,35 @@
1#
2# This file is part of Checkbox.
3#
4# Copyright 2012 Canonical Ltd.
5#
6# Checkbox is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# Checkbox is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
18#
19from checkbox.plugin import Plugin
20
21
22class WarningPrompt(Plugin):
23
24 def register(self, manager):
25 super(WarningPrompt, self).register(manager)
26
27 self._manager.reactor.call_on("prompt-warning",
28 self.prompt_warning)
29
30 def prompt_warning(self, interface,
31 primary_text, secondary_text=None, detailed_text=None):
32 interface.show_warning(primary_text, secondary_text, detailed_text)
33
34
35factory = WarningPrompt

Subscribers

People subscribed via source and target branches