Gentoo Archives: gentoo-portage-dev

From: Alexandru Elisei <alexandru.elisei@×××××.com>
To: gentoo-portage-dev@l.g.o
Subject: [gentoo-portage-dev] [PATCH] emaint: exit with non-zero status code when module fails (bug 567478)
Date: Tue, 17 Jan 2017 17:48:19
Message-Id: CAB-4s4kcFUHpng4WgW9orvLuN-L=3AbgFAv1Oy+VTw5w_hWtAQ@mail.gmail.com
1 Currently module functions 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). Emaint will inspect the return code and will
6 exit unsuccessfully if necessary.
7 ---
8 pym/portage/emaint/main.py | 11 +++++++++--
9 pym/portage/emaint/modules/binhost/binhost.py | 6 ++++--
10 pym/portage/emaint/modules/config/config.py | 8 ++++++--
11 pym/portage/emaint/modules/logs/logs.py | 9 +++++----
12 pym/portage/emaint/modules/merges/merges.py | 18 +++++++++++-------
13 pym/portage/emaint/modules/move/move.py | 9 +++++++--
14 pym/portage/emaint/modules/resume/resume.py | 4 +++-
15 pym/portage/emaint/modules/sync/sync.py | 19 +++++++++++++------
16 pym/portage/emaint/modules/world/world.py | 8 ++++++--
17 9 files changed, 64 insertions(+), 28 deletions(-)
18
19 diff --git a/pym/portage/emaint/main.py b/pym/portage/emaint/main.py
20 index 65e3545..ef4383a 100644
21 --- a/pym/portage/emaint/main.py
22 +++ b/pym/portage/emaint/main.py
23 @@ -115,6 +115,7 @@ class TaskHandler(object):
24 """Runs the module tasks"""
25 if tasks is None or func is None:
26 return
27 + returncode = os.EX_OK
28 for task in tasks:
29 inst = task()
30 show_progress = self.show_progress_bar and self.isatty
31 @@ -135,14 +136,20 @@ class TaskHandler(object):
32 # them for other tasks if there is more to do.
33 'options': options.copy()
34 }
35 - result = getattr(inst, func)(**kwargs)
36 + rcode, msgs = getattr(inst, func)(**kwargs)
37 if show_progress:
38 # make sure the final progress is displayed
39 self.progress_bar.display()
40 print()
41 self.progress_bar.stop()
42 if self.callback:
43 - self.callback(result)
44 + self.callback(msgs)
45 + # Keep the last error code when using the 'all' command.
46 + if rcode != os.EX_OK:
47 + returncode = rcode
48 +
49 + if returncode != os.EX_OK:
50 + sys.exit(returncode)
51
52
53 def print_results(results):
54 diff --git a/pym/portage/emaint/modules/binhost/binhost.py
55 b/pym/portage/emaint/modules/binhost/binhost.py
56 index cf1213e..8cf3da6 100644
57 --- a/pym/portage/emaint/modules/binhost/binhost.py
58 +++ b/pym/portage/emaint/modules/binhost/binhost.py
59 @@ -86,7 +86,9 @@ class BinhostHandler(object):
60 stale = set(metadata).difference(cpv_all)
61 for cpv in stale:
62 errors.append("'%s' is not in the repository" % cpv)
63 - return errors
64 + if errors:
65 + return (1, errors)
66 + return (os.EX_OK, None)
67
68 def fix(self, **kwargs):
69 onProgress = kwargs.get('onProgress', None)
70 @@ -177,4 +179,4 @@ class BinhostHandler(object):
71 if maxval == 0:
72 maxval = 1
73 onProgress(maxval, maxval)
74 - return None
75 + return (os.EX_OK, None)
76 diff --git a/pym/portage/emaint/modules/config/config.py
77 b/pym/portage/emaint/modules/config/config.py
78 index dad024b..a4a58d9 100644
79 --- a/pym/portage/emaint/modules/config/config.py
80 +++ b/pym/portage/emaint/modules/config/config.py
81 @@ -36,7 +36,10 @@ class CleanConfig(object):
82 if onProgress:
83 onProgress(maxval, i+1)
84 i += 1
85 - return self._format_output(messages)
86 + msgs = self._format_output(messages)
87 + if msgs:
88 + return (1, msgs)
89 + return (os.EX_OK, None)
90
91 def fix(self, **kwargs):
92 onProgress = kwargs.get('onProgress', None)
93 @@ -65,7 +68,8 @@ class CleanConfig(object):
94 i += 1
95 if modified:
96 writedict(configs, self.target)
97 - return self._format_output(messages, True)
98 + msgs = self._format_output(messages, True)
99 + return (os.EX_OK, msgs)
100
101 def _format_output(self, messages=[], cleaned=False):
102 output = []
103 diff --git a/pym/portage/emaint/modules/logs/logs.py
104 b/pym/portage/emaint/modules/logs/logs.py
105 index fe65cf5..8c638d7 100644
106 --- a/pym/portage/emaint/modules/logs/logs.py
107 +++ b/pym/portage/emaint/modules/logs/logs.py
108 @@ -40,7 +40,6 @@ class CleanLogs(object):
109 'NUM': int: number of days
110 'pretend': boolean
111 """
112 - messages = []
113 num_of_days = None
114 pretend = False
115 if kwargs:
116 @@ -70,10 +69,12 @@ class CleanLogs(object):
117 clean_cmd.remove("-delete")
118
119 if not clean_cmd:
120 - return []
121 + return (os.EX_OK, None)
122 rval = self._clean_logs(clean_cmd, settings)
123 - messages += self._convert_errors(rval)
124 - return messages
125 + errors = self._convert_errors(rval)
126 + if errors:
127 + return (rval, errors)
128 + return (os.EX_OK, None)
129
130
131 @staticmethod
132 diff --git a/pym/portage/emaint/modules/merges/merges.py
133 b/pym/portage/emaint/modules/merges/merges.py
134 index 8f677c2..9a87d87 100644
135 --- a/pym/portage/emaint/modules/merges/merges.py
136 +++ b/pym/portage/emaint/modules/merges/merges.py
137 @@ -239,7 +239,9 @@ class MergesHandler(object):
138 for pkg, mtime in failed_pkgs.items():
139 mtime_str = time.ctime(int(mtime))
140 errors.append("'%s' failed to merge on '%s'" % (pkg, mtime_str))
141 - return errors
142 + if errors:
143 + return (1, errors)
144 + return (os.EX_OK, None)
145
146
147 def fix(self, **kwargs):
148 @@ -247,13 +249,13 @@ class MergesHandler(object):
149 module_output = kwargs.get('module_output', None)
150 failed_pkgs = self._failed_pkgs()
151 if not failed_pkgs:
152 - return ['No failed merges found.']
153 + return (os.EX_OK, ['No failed merges found.'])
154
155 pkg_invalid_entries = set()
156 pkg_atoms = set()
157 self._get_pkg_atoms(failed_pkgs, pkg_atoms, pkg_invalid_entries)
158 if pkg_invalid_entries:
159 - return pkg_invalid_entries
160 + return (1, pkg_invalid_entries)
161
162 try:
163 self._tracking_file.save(failed_pkgs)
164 @@ -261,7 +263,7 @@ class MergesHandler(object):
165 errors = ['Unable to save failed merges to tracking file: %s\n'
166 % str(ex)]
167 errors.append(', '.join(sorted(failed_pkgs)))
168 - return errors
169 + return (1, errors)
170 self._remove_failed_dirs(failed_pkgs)
171 results = self._emerge_pkg_atoms(module_output, pkg_atoms)
172 # list any new failed merges
173 @@ -276,12 +278,14 @@ class MergesHandler(object):
174 if not vardb.cpv_exists(pkg_name):
175 still_failed_pkgs[pkg] = mtime
176 self._tracking_file.save(still_failed_pkgs)
177 - return results
178 + if still_failed_pkgs:
179 + return (1, results)
180 + return (os.EX_OK, results)
181
182
183 def purge(self, **kwargs):
184 """Attempt to remove previously saved tracking file."""
185 if not self._tracking_file.exists():
186 - return ['Tracking file not found.']
187 + return (os.EX_OK, ['Tracking file not found.'])
188 self._tracking_file.purge()
189 - return ['Removed tracking file.']
190 + return (os.EX_OK, ['Removed tracking file.'])
191 diff --git a/pym/portage/emaint/modules/move/move.py
192 b/pym/portage/emaint/modules/move/move.py
193 index 41ca167..1a566c3 100644
194 --- a/pym/portage/emaint/modules/move/move.py
195 +++ b/pym/portage/emaint/modules/move/move.py
196 @@ -123,7 +123,10 @@ class MoveHandler(object):
197 errors.append("'%s' has outdated metadata" % cpv)
198 if onProgress:
199 onProgress(maxval, i+1)
200 - return errors
201 +
202 + if errors:
203 + return (1, errors)
204 + return (os.EX_OK, None)
205
206 def fix(self, **kwargs):
207 onProgress = kwargs.get('onProgress', None)
208 @@ -156,7 +159,9 @@ class MoveHandler(object):
209 # Searching for updates in all the metadata is relatively slow, so this
210 # is where the progress bar comes out of indeterminate mode.
211 self._tree.dbapi.update_ents(allupdates, onProgress=onProgress)
212 - return errors
213 + if errors:
214 + return(1, errors)
215 + return (os.EX_OK, None)
216
217 class MoveInstalled(MoveHandler):
218
219 diff --git a/pym/portage/emaint/modules/resume/resume.py
220 b/pym/portage/emaint/modules/resume/resume.py
221 index 1bada52..648e8c1 100644
222 --- a/pym/portage/emaint/modules/resume/resume.py
223 +++ b/pym/portage/emaint/modules/resume/resume.py
224 @@ -2,6 +2,7 @@
225 # Distributed under the terms of the GNU General Public License v2
226
227 import portage
228 +import os
229
230
231 class CleanResume(object):
232 @@ -37,7 +38,7 @@ class CleanResume(object):
233 finally:
234 if onProgress:
235 onProgress(maxval, i+1)
236 - return messages
237 + return (os.EX_OK, messages)
238
239 def fix(self, **kwargs):
240 onProgress = kwargs.get('onProgress', None)
241 @@ -56,3 +57,4 @@ class CleanResume(object):
242 onProgress(maxval, i+1)
243 if delete_count:
244 mtimedb.commit()
245 + return (os.EX_OK, None)
246 diff --git a/pym/portage/emaint/modules/sync/sync.py
247 b/pym/portage/emaint/modules/sync/sync.py
248 index 15d63e2..03a684b 100644
249 --- a/pym/portage/emaint/modules/sync/sync.py
250 +++ b/pym/portage/emaint/modules/sync/sync.py
251 @@ -127,8 +127,8 @@ class SyncRepos(object):
252 % (bold(", ".join(repos))) + "\n ...returning"
253 ]
254 if return_messages:
255 - return msgs
256 - return
257 + return (1, msgs)
258 + return (1, None)
259 return self._sync(selected, return_messages,
260 emaint_opts=options)
261
262 @@ -211,8 +211,8 @@ class SyncRepos(object):
263 msgs.append("Emaint sync, nothing to sync... returning")
264 if return_messages:
265 msgs.extend(self.rmessage([('None', os.EX_OK)], 'sync'))
266 - return msgs
267 - return
268 + return (os.EX_OK, msgs)
269 + return (os.EX_OK, None)
270 # Portage needs to ensure a sane umask for the files it creates.
271 os.umask(0o22)
272
273 @@ -232,6 +232,7 @@ class SyncRepos(object):
274 sync_scheduler.wait()
275 retvals = sync_scheduler.retvals
276 msgs.extend(sync_scheduler.msgs)
277 + returncode = sync_scheduler.returncode
278
279 if retvals:
280 msgs.extend(self.rmessage(retvals, 'sync'))
281 @@ -244,6 +245,8 @@ class SyncRepos(object):
282 rcode = sync_manager.perform_post_sync_hook('')
283 if rcode:
284 msgs.extend(self.rmessage([('None', rcode)], 'post-sync'))
285 + if rcode != os.EX_OK:
286 + returncode = rcode
287
288 # Reload the whole config.
289 portage._sync_mode = False
290 @@ -254,8 +257,8 @@ class SyncRepos(object):
291 self.emerge_config.opts)
292
293 if return_messages:
294 - return msgs
295 - return
296 + return (returncode, msgs)
297 + return (returncode, None)
298
299
300 def _do_pkg_moves(self):
301 @@ -323,6 +326,7 @@ class SyncScheduler(AsyncScheduler):
302 self._init_graph()
303 self.retvals = []
304 self.msgs = []
305 + self.returncode = os.EX_OK
306
307 def _init_graph(self):
308 '''
309 @@ -360,6 +364,9 @@ class SyncScheduler(AsyncScheduler):
310 returncode, message, updatecache_flg, hooks_enabled = task.result
311 if message:
312 self.msgs.append(message)
313 + else:
314 + # Keep the last unsuccessful sync return code.
315 + self.returncode = returncode
316 repo = task.kwargs['repo'].name
317 self._running_repos.remove(repo)
318 self.retvals.append((repo, returncode))
319 diff --git a/pym/portage/emaint/modules/world/world.py
320 b/pym/portage/emaint/modules/world/world.py
321 index 2c9dbff..d8a4e69 100644
322 --- a/pym/portage/emaint/modules/world/world.py
323 +++ b/pym/portage/emaint/modules/world/world.py
324 @@ -65,7 +65,9 @@ class WorldHandler(object):
325 errors += ["'%s' is not installed" % x for x in self.not_installed]
326 else:
327 errors.append(self.world_file + " could not be opened for reading")
328 - return errors
329 + if errors:
330 + return (1, errors)
331 + return (os.EX_OK, None)
332
333 def fix(self, **kwargs):
334 onProgress = kwargs.get('onProgress', None)
335 @@ -83,7 +85,9 @@ class WorldHandler(object):
336 except portage.exception.PortageException:
337 errors.append("%s could not be opened for writing" % \
338 self.world_file)
339 - return errors
340 + if errors:
341 + return (1, errors)
342 + return (os.EX_OK, None)
343 finally:
344 world_set.unlock()
345
346 --
347 2.10.2

Replies