1 |
I've modified the patch as per Brian's suggestions: |
2 |
- the modules return True on success and False on failure. |
3 |
- TaskHandler.run_tasks() returns a list of all the module return |
4 |
codes, then emaint_main() exits. |
5 |
- the portage/pym/portage/tests/emerge/test_simple.py change wasn't |
6 |
present in the first version of the patch because of a subtle bug that |
7 |
I introduced: rval was None when the variable PORT_LOGDIR wasn't set, |
8 |
it was different from os.EX_OK so TaskHandler.run_tasks() was exiting |
9 |
with sys.exit(None), which is zero - success. |
10 |
|
11 |
Below is the modified commit message and the diff. |
12 |
|
13 |
Module functions currently return a message to emaint after invocation. |
14 |
Emaint prints this message then exits normally (with a success return |
15 |
code) even if the module encountered an error. This patch aims to |
16 |
change this by having each module public function return a tuple of |
17 |
(returncode, message), where returncode is boolean True if the function |
18 |
was successful or False otherwise. Emaint will inspect the return codes |
19 |
and exit unsuccessfully if necessary. |
20 |
--- |
21 |
pym/portage/emaint/main.py | 12 +++++++++--- |
22 |
pym/portage/emaint/modules/binhost/binhost.py | 6 ++++-- |
23 |
pym/portage/emaint/modules/config/config.py | 6 ++++-- |
24 |
pym/portage/emaint/modules/logs/logs.py | 9 +++++---- |
25 |
pym/portage/emaint/modules/merges/merges.py | 18 +++++++++++------- |
26 |
pym/portage/emaint/modules/move/move.py | 9 +++++++-- |
27 |
pym/portage/emaint/modules/resume/resume.py | 4 +++- |
28 |
pym/portage/emaint/modules/sync/sync.py | 20 +++++++++++++------- |
29 |
pym/portage/emaint/modules/world/world.py | 8 ++++++-- |
30 |
pym/portage/tests/emerge/test_simple.py | 1 + |
31 |
10 files changed, 63 insertions(+), 30 deletions(-) |
32 |
|
33 |
diff --git a/pym/portage/emaint/main.py b/pym/portage/emaint/main.py |
34 |
index 65e3545..f448d6b 100644 |
35 |
--- a/pym/portage/emaint/main.py |
36 |
+++ b/pym/portage/emaint/main.py |
37 |
@@ -115,6 +115,7 @@ class TaskHandler(object): |
38 |
"""Runs the module tasks""" |
39 |
if tasks is None or func is None: |
40 |
return |
41 |
+ returncodes = [] |
42 |
for task in tasks: |
43 |
inst = task() |
44 |
show_progress = self.show_progress_bar and self.isatty |
45 |
@@ -135,14 +136,17 @@ class TaskHandler(object): |
46 |
# them for other tasks if there is more to do. |
47 |
'options': options.copy() |
48 |
} |
49 |
- result = getattr(inst, func)(**kwargs) |
50 |
+ returncode, msgs = getattr(inst, func)(**kwargs) |
51 |
+ returncodes.append(returncode) |
52 |
if show_progress: |
53 |
# make sure the final progress is displayed |
54 |
self.progress_bar.display() |
55 |
print() |
56 |
self.progress_bar.stop() |
57 |
if self.callback: |
58 |
- self.callback(result) |
59 |
+ self.callback(msgs) |
60 |
+ |
61 |
+ return returncodes |
62 |
|
63 |
|
64 |
def print_results(results): |
65 |
@@ -237,4 +241,6 @@ def emaint_main(myargv): |
66 |
task_opts = options.__dict__ |
67 |
task_opts['return-messages'] = True |
68 |
taskmaster = TaskHandler(callback=print_results, module_output=sys.stdout) |
69 |
- taskmaster.run_tasks(tasks, func, status, options=task_opts) |
70 |
+ returncodes = taskmaster.run_tasks(tasks, func, status, options=task_opts) |
71 |
+ |
72 |
+ sys.exit(False in returncodes) |
73 |
diff --git a/pym/portage/emaint/modules/binhost/binhost.py |
74 |
b/pym/portage/emaint/modules/binhost/binhost.py |
75 |
index cf1213e..527b02f 100644 |
76 |
--- a/pym/portage/emaint/modules/binhost/binhost.py |
77 |
+++ b/pym/portage/emaint/modules/binhost/binhost.py |
78 |
@@ -86,7 +86,9 @@ class BinhostHandler(object): |
79 |
stale = set(metadata).difference(cpv_all) |
80 |
for cpv in stale: |
81 |
errors.append("'%s' is not in the repository" % cpv) |
82 |
- return errors |
83 |
+ if errors: |
84 |
+ return (False, errors) |
85 |
+ return (True, None) |
86 |
|
87 |
def fix(self, **kwargs): |
88 |
onProgress = kwargs.get('onProgress', None) |
89 |
@@ -177,4 +179,4 @@ class BinhostHandler(object): |
90 |
if maxval == 0: |
91 |
maxval = 1 |
92 |
onProgress(maxval, maxval) |
93 |
- return None |
94 |
+ return (True, None) |
95 |
diff --git a/pym/portage/emaint/modules/config/config.py |
96 |
b/pym/portage/emaint/modules/config/config.py |
97 |
index dad024b..a05a3c2 100644 |
98 |
--- a/pym/portage/emaint/modules/config/config.py |
99 |
+++ b/pym/portage/emaint/modules/config/config.py |
100 |
@@ -36,7 +36,8 @@ class CleanConfig(object): |
101 |
if onProgress: |
102 |
onProgress(maxval, i+1) |
103 |
i += 1 |
104 |
- return self._format_output(messages) |
105 |
+ msgs = self._format_output(messages) |
106 |
+ return (True, msgs) |
107 |
|
108 |
def fix(self, **kwargs): |
109 |
onProgress = kwargs.get('onProgress', None) |
110 |
@@ -65,7 +66,8 @@ class CleanConfig(object): |
111 |
i += 1 |
112 |
if modified: |
113 |
writedict(configs, self.target) |
114 |
- return self._format_output(messages, True) |
115 |
+ msgs = self._format_output(messages, True) |
116 |
+ return (True, msgs) |
117 |
|
118 |
def _format_output(self, messages=[], cleaned=False): |
119 |
output = [] |
120 |
diff --git a/pym/portage/emaint/modules/logs/logs.py |
121 |
b/pym/portage/emaint/modules/logs/logs.py |
122 |
index fe65cf5..028084a 100644 |
123 |
--- a/pym/portage/emaint/modules/logs/logs.py |
124 |
+++ b/pym/portage/emaint/modules/logs/logs.py |
125 |
@@ -40,7 +40,6 @@ class CleanLogs(object): |
126 |
'NUM': int: number of days |
127 |
'pretend': boolean |
128 |
""" |
129 |
- messages = [] |
130 |
num_of_days = None |
131 |
pretend = False |
132 |
if kwargs: |
133 |
@@ -70,10 +69,12 @@ class CleanLogs(object): |
134 |
clean_cmd.remove("-delete") |
135 |
|
136 |
if not clean_cmd: |
137 |
- return [] |
138 |
+ return (True, None) |
139 |
rval = self._clean_logs(clean_cmd, settings) |
140 |
- messages += self._convert_errors(rval) |
141 |
- return messages |
142 |
+ errors = self._convert_errors(rval) |
143 |
+ if errors: |
144 |
+ return (False, errors) |
145 |
+ return (True, None) |
146 |
|
147 |
|
148 |
@staticmethod |
149 |
diff --git a/pym/portage/emaint/modules/merges/merges.py |
150 |
b/pym/portage/emaint/modules/merges/merges.py |
151 |
index 8f677c2..416a725 100644 |
152 |
--- a/pym/portage/emaint/modules/merges/merges.py |
153 |
+++ b/pym/portage/emaint/modules/merges/merges.py |
154 |
@@ -239,7 +239,9 @@ class MergesHandler(object): |
155 |
for pkg, mtime in failed_pkgs.items(): |
156 |
mtime_str = time.ctime(int(mtime)) |
157 |
errors.append("'%s' failed to merge on '%s'" % (pkg, mtime_str)) |
158 |
- return errors |
159 |
+ if errors: |
160 |
+ return (False, errors) |
161 |
+ return (True, None) |
162 |
|
163 |
|
164 |
def fix(self, **kwargs): |
165 |
@@ -247,13 +249,13 @@ class MergesHandler(object): |
166 |
module_output = kwargs.get('module_output', None) |
167 |
failed_pkgs = self._failed_pkgs() |
168 |
if not failed_pkgs: |
169 |
- return ['No failed merges found.'] |
170 |
+ return (True, ['No failed merges found.']) |
171 |
|
172 |
pkg_invalid_entries = set() |
173 |
pkg_atoms = set() |
174 |
self._get_pkg_atoms(failed_pkgs, pkg_atoms, pkg_invalid_entries) |
175 |
if pkg_invalid_entries: |
176 |
- return pkg_invalid_entries |
177 |
+ return (False, pkg_invalid_entries) |
178 |
|
179 |
try: |
180 |
self._tracking_file.save(failed_pkgs) |
181 |
@@ -261,7 +263,7 @@ class MergesHandler(object): |
182 |
errors = ['Unable to save failed merges to tracking file: %s\n' |
183 |
% str(ex)] |
184 |
errors.append(', '.join(sorted(failed_pkgs))) |
185 |
- return errors |
186 |
+ return (False, errors) |
187 |
self._remove_failed_dirs(failed_pkgs) |
188 |
results = self._emerge_pkg_atoms(module_output, pkg_atoms) |
189 |
# list any new failed merges |
190 |
@@ -276,12 +278,14 @@ class MergesHandler(object): |
191 |
if not vardb.cpv_exists(pkg_name): |
192 |
still_failed_pkgs[pkg] = mtime |
193 |
self._tracking_file.save(still_failed_pkgs) |
194 |
- return results |
195 |
+ if still_failed_pkgs: |
196 |
+ return (False, results) |
197 |
+ return (True, results) |
198 |
|
199 |
|
200 |
def purge(self, **kwargs): |
201 |
"""Attempt to remove previously saved tracking file.""" |
202 |
if not self._tracking_file.exists(): |
203 |
- return ['Tracking file not found.'] |
204 |
+ return (True, ['Tracking file not found.']) |
205 |
self._tracking_file.purge() |
206 |
- return ['Removed tracking file.'] |
207 |
+ return (True, ['Removed tracking file.']) |
208 |
diff --git a/pym/portage/emaint/modules/move/move.py |
209 |
b/pym/portage/emaint/modules/move/move.py |
210 |
index 41ca167..1c2636d 100644 |
211 |
--- a/pym/portage/emaint/modules/move/move.py |
212 |
+++ b/pym/portage/emaint/modules/move/move.py |
213 |
@@ -123,7 +123,10 @@ class MoveHandler(object): |
214 |
errors.append("'%s' has outdated metadata" % cpv) |
215 |
if onProgress: |
216 |
onProgress(maxval, i+1) |
217 |
- return errors |
218 |
+ |
219 |
+ if errors: |
220 |
+ return (False, errors) |
221 |
+ return (True, None) |
222 |
|
223 |
def fix(self, **kwargs): |
224 |
onProgress = kwargs.get('onProgress', None) |
225 |
@@ -156,7 +159,9 @@ class MoveHandler(object): |
226 |
# Searching for updates in all the metadata is relatively slow, so this |
227 |
# is where the progress bar comes out of indeterminate mode. |
228 |
self._tree.dbapi.update_ents(allupdates, onProgress=onProgress) |
229 |
- return errors |
230 |
+ if errors: |
231 |
+ return (False, errors) |
232 |
+ return (True, None) |
233 |
|
234 |
class MoveInstalled(MoveHandler): |
235 |
|
236 |
diff --git a/pym/portage/emaint/modules/resume/resume.py |
237 |
b/pym/portage/emaint/modules/resume/resume.py |
238 |
index 1bada52..1d14275 100644 |
239 |
--- a/pym/portage/emaint/modules/resume/resume.py |
240 |
+++ b/pym/portage/emaint/modules/resume/resume.py |
241 |
@@ -2,6 +2,7 @@ |
242 |
# Distributed under the terms of the GNU General Public License v2 |
243 |
|
244 |
import portage |
245 |
+import os |
246 |
|
247 |
|
248 |
class CleanResume(object): |
249 |
@@ -37,7 +38,7 @@ class CleanResume(object): |
250 |
finally: |
251 |
if onProgress: |
252 |
onProgress(maxval, i+1) |
253 |
- return messages |
254 |
+ return (True, messages) |
255 |
|
256 |
def fix(self, **kwargs): |
257 |
onProgress = kwargs.get('onProgress', None) |
258 |
@@ -56,3 +57,4 @@ class CleanResume(object): |
259 |
onProgress(maxval, i+1) |
260 |
if delete_count: |
261 |
mtimedb.commit() |
262 |
+ return (True, None) |
263 |
diff --git a/pym/portage/emaint/modules/sync/sync.py |
264 |
b/pym/portage/emaint/modules/sync/sync.py |
265 |
index 15d63e2..d867699 100644 |
266 |
--- a/pym/portage/emaint/modules/sync/sync.py |
267 |
+++ b/pym/portage/emaint/modules/sync/sync.py |
268 |
@@ -127,8 +127,8 @@ class SyncRepos(object): |
269 |
% (bold(", ".join(repos))) + "\n ...returning" |
270 |
] |
271 |
if return_messages: |
272 |
- return msgs |
273 |
- return |
274 |
+ return (False, msgs) |
275 |
+ return (False, None) |
276 |
return self._sync(selected, return_messages, |
277 |
emaint_opts=options) |
278 |
|
279 |
@@ -211,8 +211,8 @@ class SyncRepos(object): |
280 |
msgs.append("Emaint sync, nothing to sync... returning") |
281 |
if return_messages: |
282 |
msgs.extend(self.rmessage([('None', os.EX_OK)], 'sync')) |
283 |
- return msgs |
284 |
- return |
285 |
+ return (True, msgs) |
286 |
+ return (True, None) |
287 |
# Portage needs to ensure a sane umask for the files it creates. |
288 |
os.umask(0o22) |
289 |
|
290 |
@@ -232,9 +232,14 @@ class SyncRepos(object): |
291 |
sync_scheduler.wait() |
292 |
retvals = sync_scheduler.retvals |
293 |
msgs.extend(sync_scheduler.msgs) |
294 |
+ returncode = True |
295 |
|
296 |
if retvals: |
297 |
msgs.extend(self.rmessage(retvals, 'sync')) |
298 |
+ for repo, returncode in retvals: |
299 |
+ if returncode != os.EX_OK: |
300 |
+ returncode = False |
301 |
+ break |
302 |
else: |
303 |
msgs.extend(self.rmessage([('None', os.EX_OK)], 'sync')) |
304 |
|
305 |
@@ -244,6 +249,8 @@ class SyncRepos(object): |
306 |
rcode = sync_manager.perform_post_sync_hook('') |
307 |
if rcode: |
308 |
msgs.extend(self.rmessage([('None', rcode)], 'post-sync')) |
309 |
+ if rcode != os.EX_OK: |
310 |
+ returncode = False |
311 |
|
312 |
# Reload the whole config. |
313 |
portage._sync_mode = False |
314 |
@@ -254,8 +261,8 @@ class SyncRepos(object): |
315 |
self.emerge_config.opts) |
316 |
|
317 |
if return_messages: |
318 |
- return msgs |
319 |
- return |
320 |
+ return (returncode, msgs) |
321 |
+ return (returncode, None) |
322 |
|
323 |
|
324 |
def _do_pkg_moves(self): |
325 |
@@ -355,7 +362,6 @@ class SyncScheduler(AsyncScheduler): |
326 |
# that hooks will be called in a backward-compatible manner |
327 |
# even if all sync tasks have failed. |
328 |
hooks_enabled = True |
329 |
- returncode = task.returncode |
330 |
if task.returncode == os.EX_OK: |
331 |
returncode, message, updatecache_flg, hooks_enabled = task.result |
332 |
if message: |
333 |
diff --git a/pym/portage/emaint/modules/world/world.py |
334 |
b/pym/portage/emaint/modules/world/world.py |
335 |
index 2c9dbff..ebc3adc 100644 |
336 |
--- a/pym/portage/emaint/modules/world/world.py |
337 |
+++ b/pym/portage/emaint/modules/world/world.py |
338 |
@@ -65,7 +65,9 @@ class WorldHandler(object): |
339 |
errors += ["'%s' is not installed" % x for x in self.not_installed] |
340 |
else: |
341 |
errors.append(self.world_file + " could not be opened for reading") |
342 |
- return errors |
343 |
+ if errors: |
344 |
+ return (False, errors) |
345 |
+ return (True, None) |
346 |
|
347 |
def fix(self, **kwargs): |
348 |
onProgress = kwargs.get('onProgress', None) |
349 |
@@ -83,7 +85,9 @@ class WorldHandler(object): |
350 |
except portage.exception.PortageException: |
351 |
errors.append("%s could not be opened for writing" % \ |
352 |
self.world_file) |
353 |
- return errors |
354 |
+ if errors: |
355 |
+ return (False, errors) |
356 |
+ return (True, None) |
357 |
finally: |
358 |
world_set.unlock() |
359 |
|
360 |
diff --git a/pym/portage/tests/emerge/test_simple.py |
361 |
b/pym/portage/tests/emerge/test_simple.py |
362 |
index b1a2af5..5930f6c 100644 |
363 |
--- a/pym/portage/tests/emerge/test_simple.py |
364 |
+++ b/pym/portage/tests/emerge/test_simple.py |
365 |
@@ -382,6 +382,7 @@ pkg_preinst() { |
366 |
"PORTAGE_PYTHON" : portage_python, |
367 |
"PORTAGE_REPOSITORIES" : settings.repositories.config_string(), |
368 |
"PORTAGE_TMPDIR" : portage_tmpdir, |
369 |
+ "PORT_LOGDIR" : portage_tmpdir, |
370 |
"PYTHONDONTWRITEBYTECODE" : os.environ.get("PYTHONDONTWRITEBYTECODE", ""), |
371 |
"PYTHONPATH" : pythonpath, |
372 |
"__PORTAGE_TEST_PATH_OVERRIDE" : fake_bin, |
373 |
-- |
374 |
2.10.2 |
375 |
|
376 |
On 1/17/17, Alexandru Elisei <alexandru.elisei@×××××.com> wrote: |
377 |
>> |
378 |
>> Please move this sys.exit to the main(). So this should just return |
379 |
>> a list of the rcodes to match the tasks it was passed. |
380 |
>> TaskHandler can be used easily via api, so we don't want it to exit out |
381 |
>> of someone else's code. That will also keep main() as the primary cli |
382 |
>> interface. There process the list of rcodes and exit appropriately. |
383 |
> |
384 |
> |
385 |
> This was an oversight on my part, I will make the necessary changes. |
386 |
> |
387 |
> Alexandru, I tend to not prefer using numbers to indicate success/fail. |
388 |
>> They can be confusing at times. In this case 1 means fail, 0 means |
389 |
>> success. Which is the opposite of True/False. It would be different |
390 |
>> if there were multiple return code values. Then they would be |
391 |
>> meaningful. |
392 |
> |
393 |
> |
394 |
> Sure, I understand your point, we are only interested in success of |
395 |
> failure of the modules, so having True or False as return values makes |
396 |
> sense. I got the idea to use 1 as a failure return code because that's how |
397 |
> the portage sync modules work (for example: |
398 |
> https://gitweb.gentoo.org/proj/portage.git/tree/pym/portage/sync/modules/git/git.py#n46). |
399 |
> That's also why I chose to return (returncode, message) and not (message, |
400 |
> returncode). |
401 |
> |
402 |
> |
403 |
> |
404 |
> On Tue, Jan 17, 2017 at 8:23 PM, Brian Dolbec <dolsen@g.o> wrote: |
405 |
> |
406 |
>> On Tue, 17 Jan 2017 19:48:16 +0200 |
407 |
>> Alexandru Elisei <alexandru.elisei@×××××.com> wrote: |
408 |
>> |
409 |
>> > Currently module functions return a message to emaint after |
410 |
>> > invocation. Emaint prints this message then exits normally (with a |
411 |
>> > success return code) even if the module encountered an error. This |
412 |
>> > patch aims to change this by having each module public function |
413 |
>> > return a tuple of (returncode, message). Emaint will inspect the |
414 |
>> > return code and will exit unsuccessfully if necessary. |
415 |
>> > --- |
416 |
>> > pym/portage/emaint/main.py | 11 +++++++++-- |
417 |
>> > pym/portage/emaint/modules/binhost/binhost.py | 6 ++++-- |
418 |
>> > pym/portage/emaint/modules/config/config.py | 8 ++++++-- |
419 |
>> > pym/portage/emaint/modules/logs/logs.py | 9 +++++---- |
420 |
>> > pym/portage/emaint/modules/merges/merges.py | 18 +++++++++++------- |
421 |
>> > pym/portage/emaint/modules/move/move.py | 9 +++++++-- |
422 |
>> > pym/portage/emaint/modules/resume/resume.py | 4 +++- |
423 |
>> > pym/portage/emaint/modules/sync/sync.py | 19 |
424 |
>> > +++++++++++++------ pym/portage/emaint/modules/world/world.py | |
425 |
>> > 8 ++++++-- 9 files changed, 64 insertions(+), 28 deletions(-) |
426 |
>> > |
427 |
>> > diff --git a/pym/portage/emaint/main.py b/pym/portage/emaint/main.py |
428 |
>> > index 65e3545..ef4383a 100644 |
429 |
>> > --- a/pym/portage/emaint/main.py |
430 |
>> > +++ b/pym/portage/emaint/main.py |
431 |
>> > @@ -115,6 +115,7 @@ class TaskHandler(object): |
432 |
>> > """Runs the module tasks""" |
433 |
>> > if tasks is None or func is None: |
434 |
>> > return |
435 |
>> > + returncode = os.EX_OK |
436 |
>> > for task in tasks: |
437 |
>> > inst = task() |
438 |
>> > show_progress = self.show_progress_bar and |
439 |
>> > self.isatty @@ -135,14 +136,20 @@ class TaskHandler(object): |
440 |
>> > # them for other tasks if there is |
441 |
>> > more to do. 'options': options.copy() |
442 |
>> > } |
443 |
>> > - result = getattr(inst, func)(**kwargs) |
444 |
>> > + rcode, msgs = getattr(inst, func)(**kwargs) |
445 |
>> > if show_progress: |
446 |
>> > # make sure the final progress is |
447 |
>> > displayed self.progress_bar.display() |
448 |
>> > print() |
449 |
>> > self.progress_bar.stop() |
450 |
>> > if self.callback: |
451 |
>> > - self.callback(result) |
452 |
>> > + self.callback(msgs) |
453 |
>> > + # Keep the last error code when using the |
454 |
>> > 'all' command. |
455 |
>> > + if rcode != os.EX_OK: |
456 |
>> > + returncode = rcode |
457 |
>> > + |
458 |
>> > + if returncode != os.EX_OK: |
459 |
>> > + sys.exit(returncode) |
460 |
>> > |
461 |
>> |
462 |
>> |
463 |
>> Please move this sys.exit to the main(). So this should just return |
464 |
>> a list of the rcodes to match the tasks it was passed. |
465 |
>> TaskHandler can be used easily via api, so we don't want it to exit out |
466 |
>> of someone else's code. That will also keep main() as the primary cli |
467 |
>> interface. There process the list of rcodes and exit appropriately. |
468 |
>> |
469 |
>> |
470 |
>> |
471 |
>> > |
472 |
>> > def print_results(results): |
473 |
>> > diff --git a/pym/portage/emaint/modules/binhost/binhost.py |
474 |
>> > b/pym/portage/emaint/modules/binhost/binhost.py |
475 |
>> > index cf1213e..8cf3da6 100644 |
476 |
>> > --- a/pym/portage/emaint/modules/binhost/binhost.py |
477 |
>> > +++ b/pym/portage/emaint/modules/binhost/binhost.py |
478 |
>> > @@ -86,7 +86,9 @@ class BinhostHandler(object): |
479 |
>> > stale = set(metadata).difference(cpv_all) |
480 |
>> > for cpv in stale: |
481 |
>> > errors.append("'%s' is not in the |
482 |
>> > repository" % cpv) |
483 |
>> > - return errors |
484 |
>> > + if errors: |
485 |
>> > + return (1, errors) |
486 |
>> > + return (os.EX_OK, None) |
487 |
>> > |
488 |
>> |
489 |
>> Alexandru, I tend to not prefer using numbers to indicate success/fail. |
490 |
>> They can be confusing at times. In this case 1 means fail, 0 means |
491 |
>> success. Which is the opposite of True/False. It would be different |
492 |
>> if there were multiple return code values. Then they would be |
493 |
>> meaningful. I want to keep these modules pythonic in the sense that |
494 |
>> they return standard True/False for API use. In this case returning |
495 |
>> True/False would also make it easy to process the list of rcodes |
496 |
>> returned to main() |
497 |
>> |
498 |
>> sys.exit(False in rcodes) |
499 |
>> |
500 |
>> which will reverse the True/False to 0/1 automatically for us and make |
501 |
>> it super simple to process a variable length list success/fail results. |
502 |
>> |
503 |
>> -- |
504 |
>> Brian Dolbec <dolsen> |
505 |
>> |
506 |
>> |
507 |
>> |
508 |
> |