Gentoo Archives: gentoo-portage-dev

From: Alexandru Elisei <alexandru.elisei@×××××.com>
To: gentoo-portage-dev@l.g.o
Subject: [gentoo-portage-dev] Re: [PATCH v2] emaint: exit with non-zero status code when module fails (bug 567478)
Date: Wed, 18 Jan 2017 16:05:12
Message-Id: CAB-4s4noW9y=6zLoftZWv8BHmuuSjV_10pPpjrwTYsGgboorkA@mail.gmail.com
In Reply to: [gentoo-portage-dev] [PATCH] emaint: exit with non-zero status code when module fails (bug 567478) by Alexandru Elisei
1 Module functions currently return a message to emaint after invocation.
2 Emaint prints this message then exits normally (with a success return
3 code) even if the module encountered an error. This patch aims to
4 change this by having each module public function return a tuple of
5 (returncode, message), where returncode is boolean True if the function
6 was successful or False otherwise. Emaint will inspect the return codes
7 and exit unsuccessfully if necessary.
8 #
9 # I've modified the patch as per Brian's suggestions:
10 # - the modules return True on success and False on failure.
11 # - TaskHandler.run_tasks() returns a list of all the module return
12 # codes, then emaint_main() exits.
13 # - the portage/pym/portage/tests/emerge/test_simple.py change wasn't
14 # present in the first version of the patch because of a subtle bug that
15 # I introduced: rval was None when the variable PORT_LOGDIR wasn't set,
16 # it was different from os.EX_OK so TaskHandler.run_tasks() was exiting
17 # with sys.exit(None), which is zero - success.
18 ---
19 pym/portage/emaint/main.py | 12 +++++++++---
20 pym/portage/emaint/modules/binhost/binhost.py | 6 ++++--
21 pym/portage/emaint/modules/config/config.py | 6 ++++--
22 pym/portage/emaint/modules/logs/logs.py | 9 +++++----
23 pym/portage/emaint/modules/merges/merges.py | 18 +++++++++++-------
24 pym/portage/emaint/modules/move/move.py | 9 +++++++--
25 pym/portage/emaint/modules/resume/resume.py | 4 +++-
26 pym/portage/emaint/modules/sync/sync.py | 20 +++++++++++++-------
27 pym/portage/emaint/modules/world/world.py | 8 ++++++--
28 pym/portage/tests/emerge/test_simple.py | 1 +
29 10 files changed, 63 insertions(+), 30 deletions(-)
30
31 diff --git a/pym/portage/emaint/main.py b/pym/portage/emaint/main.py
32 index 65e3545..f448d6b 100644
33 --- a/pym/portage/emaint/main.py
34 +++ b/pym/portage/emaint/main.py
35 @@ -115,6 +115,7 @@ class TaskHandler(object):
36 """Runs the module tasks"""
37 if tasks is None or func is None:
38 return
39 + returncodes = []
40 for task in tasks:
41 inst = task()
42 show_progress = self.show_progress_bar and self.isatty
43 @@ -135,14 +136,17 @@ class TaskHandler(object):
44 # them for other tasks if there is more to do.
45 'options': options.copy()
46 }
47 - result = getattr(inst, func)(**kwargs)
48 + returncode, msgs = getattr(inst, func)(**kwargs)
49 + returncodes.append(returncode)
50 if show_progress:
51 # make sure the final progress is displayed
52 self.progress_bar.display()
53 print()
54 self.progress_bar.stop()
55 if self.callback:
56 - self.callback(result)
57 + self.callback(msgs)
58 +
59 + return returncodes
60
61
62 def print_results(results):
63 @@ -237,4 +241,6 @@ def emaint_main(myargv):
64 task_opts = options.__dict__
65 task_opts['return-messages'] = True
66 taskmaster = TaskHandler(callback=print_results, module_output=sys.stdout)
67 - taskmaster.run_tasks(tasks, func, status, options=task_opts)
68 + returncodes = taskmaster.run_tasks(tasks, func, status, options=task_opts)
69 +
70 + sys.exit(False in returncodes)
71 diff --git a/pym/portage/emaint/modules/binhost/binhost.py
72 b/pym/portage/emaint/modules/binhost/binhost.py
73 index cf1213e..527b02f 100644
74 --- a/pym/portage/emaint/modules/binhost/binhost.py
75 +++ b/pym/portage/emaint/modules/binhost/binhost.py
76 @@ -86,7 +86,9 @@ class BinhostHandler(object):
77 stale = set(metadata).difference(cpv_all)
78 for cpv in stale:
79 errors.append("'%s' is not in the repository" % cpv)
80 - return errors
81 + if errors:
82 + return (False, errors)
83 + return (True, None)
84
85 def fix(self, **kwargs):
86 onProgress = kwargs.get('onProgress', None)
87 @@ -177,4 +179,4 @@ class BinhostHandler(object):
88 if maxval == 0:
89 maxval = 1
90 onProgress(maxval, maxval)
91 - return None
92 + return (True, None)
93 diff --git a/pym/portage/emaint/modules/config/config.py
94 b/pym/portage/emaint/modules/config/config.py
95 index dad024b..a05a3c2 100644
96 --- a/pym/portage/emaint/modules/config/config.py
97 +++ b/pym/portage/emaint/modules/config/config.py
98 @@ -36,7 +36,8 @@ class CleanConfig(object):
99 if onProgress:
100 onProgress(maxval, i+1)
101 i += 1
102 - return self._format_output(messages)
103 + msgs = self._format_output(messages)
104 + return (True, msgs)
105
106 def fix(self, **kwargs):
107 onProgress = kwargs.get('onProgress', None)
108 @@ -65,7 +66,8 @@ class CleanConfig(object):
109 i += 1
110 if modified:
111 writedict(configs, self.target)
112 - return self._format_output(messages, True)
113 + msgs = self._format_output(messages, True)
114 + return (True, msgs)
115
116 def _format_output(self, messages=[], cleaned=False):
117 output = []
118 diff --git a/pym/portage/emaint/modules/logs/logs.py
119 b/pym/portage/emaint/modules/logs/logs.py
120 index fe65cf5..028084a 100644
121 --- a/pym/portage/emaint/modules/logs/logs.py
122 +++ b/pym/portage/emaint/modules/logs/logs.py
123 @@ -40,7 +40,6 @@ class CleanLogs(object):
124 'NUM': int: number of days
125 'pretend': boolean
126 """
127 - messages = []
128 num_of_days = None
129 pretend = False
130 if kwargs:
131 @@ -70,10 +69,12 @@ class CleanLogs(object):
132 clean_cmd.remove("-delete")
133
134 if not clean_cmd:
135 - return []
136 + return (True, None)
137 rval = self._clean_logs(clean_cmd, settings)
138 - messages += self._convert_errors(rval)
139 - return messages
140 + errors = self._convert_errors(rval)
141 + if errors:
142 + return (False, errors)
143 + return (True, None)
144
145
146 @staticmethod
147 diff --git a/pym/portage/emaint/modules/merges/merges.py
148 b/pym/portage/emaint/modules/merges/merges.py
149 index 8f677c2..416a725 100644
150 --- a/pym/portage/emaint/modules/merges/merges.py
151 +++ b/pym/portage/emaint/modules/merges/merges.py
152 @@ -239,7 +239,9 @@ class MergesHandler(object):
153 for pkg, mtime in failed_pkgs.items():
154 mtime_str = time.ctime(int(mtime))
155 errors.append("'%s' failed to merge on '%s'" % (pkg, mtime_str))
156 - return errors
157 + if errors:
158 + return (False, errors)
159 + return (True, None)
160
161
162 def fix(self, **kwargs):
163 @@ -247,13 +249,13 @@ class MergesHandler(object):
164 module_output = kwargs.get('module_output', None)
165 failed_pkgs = self._failed_pkgs()
166 if not failed_pkgs:
167 - return ['No failed merges found.']
168 + return (True, ['No failed merges found.'])
169
170 pkg_invalid_entries = set()
171 pkg_atoms = set()
172 self._get_pkg_atoms(failed_pkgs, pkg_atoms, pkg_invalid_entries)
173 if pkg_invalid_entries:
174 - return pkg_invalid_entries
175 + return (False, pkg_invalid_entries)
176
177 try:
178 self._tracking_file.save(failed_pkgs)
179 @@ -261,7 +263,7 @@ class MergesHandler(object):
180 errors = ['Unable to save failed merges to tracking file: %s\n'
181 % str(ex)]
182 errors.append(', '.join(sorted(failed_pkgs)))
183 - return errors
184 + return (False, errors)
185 self._remove_failed_dirs(failed_pkgs)
186 results = self._emerge_pkg_atoms(module_output, pkg_atoms)
187 # list any new failed merges
188 @@ -276,12 +278,14 @@ class MergesHandler(object):
189 if not vardb.cpv_exists(pkg_name):
190 still_failed_pkgs[pkg] = mtime
191 self._tracking_file.save(still_failed_pkgs)
192 - return results
193 + if still_failed_pkgs:
194 + return (False, results)
195 + return (True, results)
196
197
198 def purge(self, **kwargs):
199 """Attempt to remove previously saved tracking file."""
200 if not self._tracking_file.exists():
201 - return ['Tracking file not found.']
202 + return (True, ['Tracking file not found.'])
203 self._tracking_file.purge()
204 - return ['Removed tracking file.']
205 + return (True, ['Removed tracking file.'])
206 diff --git a/pym/portage/emaint/modules/move/move.py
207 b/pym/portage/emaint/modules/move/move.py
208 index 41ca167..1c2636d 100644
209 --- a/pym/portage/emaint/modules/move/move.py
210 +++ b/pym/portage/emaint/modules/move/move.py
211 @@ -123,7 +123,10 @@ class MoveHandler(object):
212 errors.append("'%s' has outdated metadata" % cpv)
213 if onProgress:
214 onProgress(maxval, i+1)
215 - return errors
216 +
217 + if errors:
218 + return (False, errors)
219 + return (True, None)
220
221 def fix(self, **kwargs):
222 onProgress = kwargs.get('onProgress', None)
223 @@ -156,7 +159,9 @@ class MoveHandler(object):
224 # Searching for updates in all the metadata is relatively slow, so this
225 # is where the progress bar comes out of indeterminate mode.
226 self._tree.dbapi.update_ents(allupdates, onProgress=onProgress)
227 - return errors
228 + if errors:
229 + return (False, errors)
230 + return (True, None)
231
232 class MoveInstalled(MoveHandler):
233
234 diff --git a/pym/portage/emaint/modules/resume/resume.py
235 b/pym/portage/emaint/modules/resume/resume.py
236 index 1bada52..1d14275 100644
237 --- a/pym/portage/emaint/modules/resume/resume.py
238 +++ b/pym/portage/emaint/modules/resume/resume.py
239 @@ -2,6 +2,7 @@
240 # Distributed under the terms of the GNU General Public License v2
241
242 import portage
243 +import os
244
245
246 class CleanResume(object):
247 @@ -37,7 +38,7 @@ class CleanResume(object):
248 finally:
249 if onProgress:
250 onProgress(maxval, i+1)
251 - return messages
252 + return (True, messages)
253
254 def fix(self, **kwargs):
255 onProgress = kwargs.get('onProgress', None)
256 @@ -56,3 +57,4 @@ class CleanResume(object):
257 onProgress(maxval, i+1)
258 if delete_count:
259 mtimedb.commit()
260 + return (True, None)
261 diff --git a/pym/portage/emaint/modules/sync/sync.py
262 b/pym/portage/emaint/modules/sync/sync.py
263 index 15d63e2..d867699 100644
264 --- a/pym/portage/emaint/modules/sync/sync.py
265 +++ b/pym/portage/emaint/modules/sync/sync.py
266 @@ -127,8 +127,8 @@ class SyncRepos(object):
267 % (bold(", ".join(repos))) + "\n ...returning"
268 ]
269 if return_messages:
270 - return msgs
271 - return
272 + return (False, msgs)
273 + return (False, None)
274 return self._sync(selected, return_messages,
275 emaint_opts=options)
276
277 @@ -211,8 +211,8 @@ class SyncRepos(object):
278 msgs.append("Emaint sync, nothing to sync... returning")
279 if return_messages:
280 msgs.extend(self.rmessage([('None', os.EX_OK)], 'sync'))
281 - return msgs
282 - return
283 + return (True, msgs)
284 + return (True, None)
285 # Portage needs to ensure a sane umask for the files it creates.
286 os.umask(0o22)
287
288 @@ -232,9 +232,14 @@ class SyncRepos(object):
289 sync_scheduler.wait()
290 retvals = sync_scheduler.retvals
291 msgs.extend(sync_scheduler.msgs)
292 + returncode = True
293
294 if retvals:
295 msgs.extend(self.rmessage(retvals, 'sync'))
296 + for repo, returncode in retvals:
297 + if returncode != os.EX_OK:
298 + returncode = False
299 + break
300 else:
301 msgs.extend(self.rmessage([('None', os.EX_OK)], 'sync'))
302
303 @@ -244,6 +249,8 @@ class SyncRepos(object):
304 rcode = sync_manager.perform_post_sync_hook('')
305 if rcode:
306 msgs.extend(self.rmessage([('None', rcode)], 'post-sync'))
307 + if rcode != os.EX_OK:
308 + returncode = False
309
310 # Reload the whole config.
311 portage._sync_mode = False
312 @@ -254,8 +261,8 @@ class SyncRepos(object):
313 self.emerge_config.opts)
314
315 if return_messages:
316 - return msgs
317 - return
318 + return (returncode, msgs)
319 + return (returncode, None)
320
321
322 def _do_pkg_moves(self):
323 @@ -355,7 +362,6 @@ class SyncScheduler(AsyncScheduler):
324 # that hooks will be called in a backward-compatible manner
325 # even if all sync tasks have failed.
326 hooks_enabled = True
327 - returncode = task.returncode
328 if task.returncode == os.EX_OK:
329 returncode, message, updatecache_flg, hooks_enabled = task.result
330 if message:
331 diff --git a/pym/portage/emaint/modules/world/world.py
332 b/pym/portage/emaint/modules/world/world.py
333 index 2c9dbff..ebc3adc 100644
334 --- a/pym/portage/emaint/modules/world/world.py
335 +++ b/pym/portage/emaint/modules/world/world.py
336 @@ -65,7 +65,9 @@ class WorldHandler(object):
337 errors += ["'%s' is not installed" % x for x in self.not_installed]
338 else:
339 errors.append(self.world_file + " could not be opened for reading")
340 - return errors
341 + if errors:
342 + return (False, errors)
343 + return (True, None)
344
345 def fix(self, **kwargs):
346 onProgress = kwargs.get('onProgress', None)
347 @@ -83,7 +85,9 @@ class WorldHandler(object):
348 except portage.exception.PortageException:
349 errors.append("%s could not be opened for writing" % \
350 self.world_file)
351 - return errors
352 + if errors:
353 + return (False, errors)
354 + return (True, None)
355 finally:
356 world_set.unlock()
357
358 diff --git a/pym/portage/tests/emerge/test_simple.py
359 b/pym/portage/tests/emerge/test_simple.py
360 index b1a2af5..5930f6c 100644
361 --- a/pym/portage/tests/emerge/test_simple.py
362 +++ b/pym/portage/tests/emerge/test_simple.py
363 @@ -382,6 +382,7 @@ pkg_preinst() {
364 "PORTAGE_PYTHON" : portage_python,
365 "PORTAGE_REPOSITORIES" : settings.repositories.config_string(),
366 "PORTAGE_TMPDIR" : portage_tmpdir,
367 + "PORT_LOGDIR" : portage_tmpdir,
368 "PYTHONDONTWRITEBYTECODE" : os.environ.get("PYTHONDONTWRITEBYTECODE", ""),
369 "PYTHONPATH" : pythonpath,
370 "__PORTAGE_TEST_PATH_OVERRIDE" : fake_bin,
371 --
372 2.10.2
373
374 On 1/17/17, Alexandru Elisei <alexandru.elisei@×××××.com> wrote:
375 > Currently module functions return a message to emaint after invocation.
376 > Emaint prints this message then exits normally (with a success return
377 > code) even if the module encountered an error. This patch aims to
378 > change this by having each module public function return a tuple of
379 > (returncode, message). Emaint will inspect the return code and will
380 > exit unsuccessfully if necessary.
381 > ---
382 > pym/portage/emaint/main.py | 11 +++++++++--
383 > pym/portage/emaint/modules/binhost/binhost.py | 6 ++++--
384 > pym/portage/emaint/modules/config/config.py | 8 ++++++--
385 > pym/portage/emaint/modules/logs/logs.py | 9 +++++----
386 > pym/portage/emaint/modules/merges/merges.py | 18 +++++++++++-------
387 > pym/portage/emaint/modules/move/move.py | 9 +++++++--
388 > pym/portage/emaint/modules/resume/resume.py | 4 +++-
389 > pym/portage/emaint/modules/sync/sync.py | 19 +++++++++++++------
390 > pym/portage/emaint/modules/world/world.py | 8 ++++++--
391 > 9 files changed, 64 insertions(+), 28 deletions(-)
392 >
393 > diff --git a/pym/portage/emaint/main.py b/pym/portage/emaint/main.py
394 > index 65e3545..ef4383a 100644
395 > --- a/pym/portage/emaint/main.py
396 > +++ b/pym/portage/emaint/main.py
397 > @@ -115,6 +115,7 @@ class TaskHandler(object):
398 > """Runs the module tasks"""
399 > if tasks is None or func is None:
400 > return
401 > + returncode = os.EX_OK
402 > for task in tasks:
403 > inst = task()
404 > show_progress = self.show_progress_bar and self.isatty
405 > @@ -135,14 +136,20 @@ class TaskHandler(object):
406 > # them for other tasks if there is more to do.
407 > 'options': options.copy()
408 > }
409 > - result = getattr(inst, func)(**kwargs)
410 > + rcode, msgs = getattr(inst, func)(**kwargs)
411 > if show_progress:
412 > # make sure the final progress is displayed
413 > self.progress_bar.display()
414 > print()
415 > self.progress_bar.stop()
416 > if self.callback:
417 > - self.callback(result)
418 > + self.callback(msgs)
419 > + # Keep the last error code when using the 'all' command.
420 > + if rcode != os.EX_OK:
421 > + returncode = rcode
422 > +
423 > + if returncode != os.EX_OK:
424 > + sys.exit(returncode)
425 >
426 >
427 > def print_results(results):
428 > diff --git a/pym/portage/emaint/modules/binhost/binhost.py
429 > b/pym/portage/emaint/modules/binhost/binhost.py
430 > index cf1213e..8cf3da6 100644
431 > --- a/pym/portage/emaint/modules/binhost/binhost.py
432 > +++ b/pym/portage/emaint/modules/binhost/binhost.py
433 > @@ -86,7 +86,9 @@ class BinhostHandler(object):
434 > stale = set(metadata).difference(cpv_all)
435 > for cpv in stale:
436 > errors.append("'%s' is not in the repository" % cpv)
437 > - return errors
438 > + if errors:
439 > + return (1, errors)
440 > + return (os.EX_OK, None)
441 >
442 > def fix(self, **kwargs):
443 > onProgress = kwargs.get('onProgress', None)
444 > @@ -177,4 +179,4 @@ class BinhostHandler(object):
445 > if maxval == 0:
446 > maxval = 1
447 > onProgress(maxval, maxval)
448 > - return None
449 > + return (os.EX_OK, None)
450 > diff --git a/pym/portage/emaint/modules/config/config.py
451 > b/pym/portage/emaint/modules/config/config.py
452 > index dad024b..a4a58d9 100644
453 > --- a/pym/portage/emaint/modules/config/config.py
454 > +++ b/pym/portage/emaint/modules/config/config.py
455 > @@ -36,7 +36,10 @@ class CleanConfig(object):
456 > if onProgress:
457 > onProgress(maxval, i+1)
458 > i += 1
459 > - return self._format_output(messages)
460 > + msgs = self._format_output(messages)
461 > + if msgs:
462 > + return (1, msgs)
463 > + return (os.EX_OK, None)
464 >
465 > def fix(self, **kwargs):
466 > onProgress = kwargs.get('onProgress', None)
467 > @@ -65,7 +68,8 @@ class CleanConfig(object):
468 > i += 1
469 > if modified:
470 > writedict(configs, self.target)
471 > - return self._format_output(messages, True)
472 > + msgs = self._format_output(messages, True)
473 > + return (os.EX_OK, msgs)
474 >
475 > def _format_output(self, messages=[], cleaned=False):
476 > output = []
477 > diff --git a/pym/portage/emaint/modules/logs/logs.py
478 > b/pym/portage/emaint/modules/logs/logs.py
479 > index fe65cf5..8c638d7 100644
480 > --- a/pym/portage/emaint/modules/logs/logs.py
481 > +++ b/pym/portage/emaint/modules/logs/logs.py
482 > @@ -40,7 +40,6 @@ class CleanLogs(object):
483 > 'NUM': int: number of days
484 > 'pretend': boolean
485 > """
486 > - messages = []
487 > num_of_days = None
488 > pretend = False
489 > if kwargs:
490 > @@ -70,10 +69,12 @@ class CleanLogs(object):
491 > clean_cmd.remove("-delete")
492 >
493 > if not clean_cmd:
494 > - return []
495 > + return (os.EX_OK, None)
496 > rval = self._clean_logs(clean_cmd, settings)
497 > - messages += self._convert_errors(rval)
498 > - return messages
499 > + errors = self._convert_errors(rval)
500 > + if errors:
501 > + return (rval, errors)
502 > + return (os.EX_OK, None)
503 >
504 >
505 > @staticmethod
506 > diff --git a/pym/portage/emaint/modules/merges/merges.py
507 > b/pym/portage/emaint/modules/merges/merges.py
508 > index 8f677c2..9a87d87 100644
509 > --- a/pym/portage/emaint/modules/merges/merges.py
510 > +++ b/pym/portage/emaint/modules/merges/merges.py
511 > @@ -239,7 +239,9 @@ class MergesHandler(object):
512 > for pkg, mtime in failed_pkgs.items():
513 > mtime_str = time.ctime(int(mtime))
514 > errors.append("'%s' failed to merge on '%s'" % (pkg, mtime_str))
515 > - return errors
516 > + if errors:
517 > + return (1, errors)
518 > + return (os.EX_OK, None)
519 >
520 >
521 > def fix(self, **kwargs):
522 > @@ -247,13 +249,13 @@ class MergesHandler(object):
523 > module_output = kwargs.get('module_output', None)
524 > failed_pkgs = self._failed_pkgs()
525 > if not failed_pkgs:
526 > - return ['No failed merges found.']
527 > + return (os.EX_OK, ['No failed merges found.'])
528 >
529 > pkg_invalid_entries = set()
530 > pkg_atoms = set()
531 > self._get_pkg_atoms(failed_pkgs, pkg_atoms, pkg_invalid_entries)
532 > if pkg_invalid_entries:
533 > - return pkg_invalid_entries
534 > + return (1, pkg_invalid_entries)
535 >
536 > try:
537 > self._tracking_file.save(failed_pkgs)
538 > @@ -261,7 +263,7 @@ class MergesHandler(object):
539 > errors = ['Unable to save failed merges to tracking file: %s\n'
540 > % str(ex)]
541 > errors.append(', '.join(sorted(failed_pkgs)))
542 > - return errors
543 > + return (1, errors)
544 > self._remove_failed_dirs(failed_pkgs)
545 > results = self._emerge_pkg_atoms(module_output, pkg_atoms)
546 > # list any new failed merges
547 > @@ -276,12 +278,14 @@ class MergesHandler(object):
548 > if not vardb.cpv_exists(pkg_name):
549 > still_failed_pkgs[pkg] = mtime
550 > self._tracking_file.save(still_failed_pkgs)
551 > - return results
552 > + if still_failed_pkgs:
553 > + return (1, results)
554 > + return (os.EX_OK, results)
555 >
556 >
557 > def purge(self, **kwargs):
558 > """Attempt to remove previously saved tracking file."""
559 > if not self._tracking_file.exists():
560 > - return ['Tracking file not found.']
561 > + return (os.EX_OK, ['Tracking file not found.'])
562 > self._tracking_file.purge()
563 > - return ['Removed tracking file.']
564 > + return (os.EX_OK, ['Removed tracking file.'])
565 > diff --git a/pym/portage/emaint/modules/move/move.py
566 > b/pym/portage/emaint/modules/move/move.py
567 > index 41ca167..1a566c3 100644
568 > --- a/pym/portage/emaint/modules/move/move.py
569 > +++ b/pym/portage/emaint/modules/move/move.py
570 > @@ -123,7 +123,10 @@ class MoveHandler(object):
571 > errors.append("'%s' has outdated metadata" % cpv)
572 > if onProgress:
573 > onProgress(maxval, i+1)
574 > - return errors
575 > +
576 > + if errors:
577 > + return (1, errors)
578 > + return (os.EX_OK, None)
579 >
580 > def fix(self, **kwargs):
581 > onProgress = kwargs.get('onProgress', None)
582 > @@ -156,7 +159,9 @@ class MoveHandler(object):
583 > # Searching for updates in all the metadata is relatively slow, so this
584 > # is where the progress bar comes out of indeterminate mode.
585 > self._tree.dbapi.update_ents(allupdates, onProgress=onProgress)
586 > - return errors
587 > + if errors:
588 > + return(1, errors)
589 > + return (os.EX_OK, None)
590 >
591 > class MoveInstalled(MoveHandler):
592 >
593 > diff --git a/pym/portage/emaint/modules/resume/resume.py
594 > b/pym/portage/emaint/modules/resume/resume.py
595 > index 1bada52..648e8c1 100644
596 > --- a/pym/portage/emaint/modules/resume/resume.py
597 > +++ b/pym/portage/emaint/modules/resume/resume.py
598 > @@ -2,6 +2,7 @@
599 > # Distributed under the terms of the GNU General Public License v2
600 >
601 > import portage
602 > +import os
603 >
604 >
605 > class CleanResume(object):
606 > @@ -37,7 +38,7 @@ class CleanResume(object):
607 > finally:
608 > if onProgress:
609 > onProgress(maxval, i+1)
610 > - return messages
611 > + return (os.EX_OK, messages)
612 >
613 > def fix(self, **kwargs):
614 > onProgress = kwargs.get('onProgress', None)
615 > @@ -56,3 +57,4 @@ class CleanResume(object):
616 > onProgress(maxval, i+1)
617 > if delete_count:
618 > mtimedb.commit()
619 > + return (os.EX_OK, None)
620 > diff --git a/pym/portage/emaint/modules/sync/sync.py
621 > b/pym/portage/emaint/modules/sync/sync.py
622 > index 15d63e2..03a684b 100644
623 > --- a/pym/portage/emaint/modules/sync/sync.py
624 > +++ b/pym/portage/emaint/modules/sync/sync.py
625 > @@ -127,8 +127,8 @@ class SyncRepos(object):
626 > % (bold(", ".join(repos))) + "\n ...returning"
627 > ]
628 > if return_messages:
629 > - return msgs
630 > - return
631 > + return (1, msgs)
632 > + return (1, None)
633 > return self._sync(selected, return_messages,
634 > emaint_opts=options)
635 >
636 > @@ -211,8 +211,8 @@ class SyncRepos(object):
637 > msgs.append("Emaint sync, nothing to sync... returning")
638 > if return_messages:
639 > msgs.extend(self.rmessage([('None', os.EX_OK)], 'sync'))
640 > - return msgs
641 > - return
642 > + return (os.EX_OK, msgs)
643 > + return (os.EX_OK, None)
644 > # Portage needs to ensure a sane umask for the files it creates.
645 > os.umask(0o22)
646 >
647 > @@ -232,6 +232,7 @@ class SyncRepos(object):
648 > sync_scheduler.wait()
649 > retvals = sync_scheduler.retvals
650 > msgs.extend(sync_scheduler.msgs)
651 > + returncode = sync_scheduler.returncode
652 >
653 > if retvals:
654 > msgs.extend(self.rmessage(retvals, 'sync'))
655 > @@ -244,6 +245,8 @@ class SyncRepos(object):
656 > rcode = sync_manager.perform_post_sync_hook('')
657 > if rcode:
658 > msgs.extend(self.rmessage([('None', rcode)], 'post-sync'))
659 > + if rcode != os.EX_OK:
660 > + returncode = rcode
661 >
662 > # Reload the whole config.
663 > portage._sync_mode = False
664 > @@ -254,8 +257,8 @@ class SyncRepos(object):
665 > self.emerge_config.opts)
666 >
667 > if return_messages:
668 > - return msgs
669 > - return
670 > + return (returncode, msgs)
671 > + return (returncode, None)
672 >
673 >
674 > def _do_pkg_moves(self):
675 > @@ -323,6 +326,7 @@ class SyncScheduler(AsyncScheduler):
676 > self._init_graph()
677 > self.retvals = []
678 > self.msgs = []
679 > + self.returncode = os.EX_OK
680 >
681 > def _init_graph(self):
682 > '''
683 > @@ -360,6 +364,9 @@ class SyncScheduler(AsyncScheduler):
684 > returncode, message, updatecache_flg, hooks_enabled = task.result
685 > if message:
686 > self.msgs.append(message)
687 > + else:
688 > + # Keep the last unsuccessful sync return code.
689 > + self.returncode = returncode
690 > repo = task.kwargs['repo'].name
691 > self._running_repos.remove(repo)
692 > self.retvals.append((repo, returncode))
693 > diff --git a/pym/portage/emaint/modules/world/world.py
694 > b/pym/portage/emaint/modules/world/world.py
695 > index 2c9dbff..d8a4e69 100644
696 > --- a/pym/portage/emaint/modules/world/world.py
697 > +++ b/pym/portage/emaint/modules/world/world.py
698 > @@ -65,7 +65,9 @@ class WorldHandler(object):
699 > errors += ["'%s' is not installed" % x for x in self.not_installed]
700 > else:
701 > errors.append(self.world_file + " could not be opened for reading")
702 > - return errors
703 > + if errors:
704 > + return (1, errors)
705 > + return (os.EX_OK, None)
706 >
707 > def fix(self, **kwargs):
708 > onProgress = kwargs.get('onProgress', None)
709 > @@ -83,7 +85,9 @@ class WorldHandler(object):
710 > except portage.exception.PortageException:
711 > errors.append("%s could not be opened for writing" % \
712 > self.world_file)
713 > - return errors
714 > + if errors:
715 > + return (1, errors)
716 > + return (os.EX_OK, None)
717 > finally:
718 > world_set.unlock()
719 >
720 > --
721 > 2.10.2
722 >