Gentoo Archives: gentoo-portage-dev

From: Brian Dolbec <dolsen@g.o>
To: gentoo-portage-dev@l.g.o
Subject: Re: [gentoo-portage-dev] Re: [PATCH V3] emaint: exit with non-zero status code when module fails (bug 567478)
Date: Thu, 19 Jan 2017 20:49:05
Message-Id: 20170119124900.723ad357.dolsen@gentoo.org
In Reply to: [gentoo-portage-dev] Re: [PATCH V3] emaint: exit with non-zero status code when module fails (bug 567478) by Alexandru Elisei
1 On Wed, 18 Jan 2017 19:15:54 +0200
2 Alexandru Elisei <alexandru.elisei@×××××.com> wrote:
3
4 > Module functions currently return a message to emaint after
5 > invocation. Emaint prints this message then exits normally (with a
6 > success return code) even if the module encountered an error. This
7 > patch aims to change this by having each module public function
8 > return a tuple of (returncode, message), where returncode is boolean
9 > True if the function was successful or False otherwise. Emaint will
10 > inspect the return codes and exit unsuccessfully if necessary.
11 >
12 > The variable PORT_LOGDIR was added to the test environment to prevent
13 > CleanLogs.clean() from failing when the variable isn't set or not a
14 > directory.
15 > ---
16 > pym/portage/emaint/main.py | 12 +++++++++---
17 > pym/portage/emaint/modules/binhost/binhost.py | 6 ++++--
18 > pym/portage/emaint/modules/config/config.py | 6 ++++--
19 > pym/portage/emaint/modules/logs/logs.py | 9 +++++----
20 > pym/portage/emaint/modules/merges/merges.py | 18 +++++++++++-------
21 > pym/portage/emaint/modules/move/move.py | 9 +++++++--
22 > pym/portage/emaint/modules/resume/resume.py | 3 ++-
23 > pym/portage/emaint/modules/sync/sync.py | 20
24 > +++++++++++++------- pym/portage/emaint/modules/world/world.py |
25 > 8 ++++++-- pym/portage/tests/emerge/test_simple.py | 1 +
26 > 10 files changed, 62 insertions(+), 30 deletions(-)
27 >
28 > diff --git a/pym/portage/emaint/main.py b/pym/portage/emaint/main.py
29 > index 65e3545..f448d6b 100644
30 > --- a/pym/portage/emaint/main.py
31 > +++ b/pym/portage/emaint/main.py
32 > @@ -115,6 +115,7 @@ class TaskHandler(object):
33 > """Runs the module tasks"""
34 > if tasks is None or func is None:
35 > return
36 > + returncodes = []
37 > for task in tasks:
38 > inst = task()
39 > show_progress = self.show_progress_bar and
40 > self.isatty @@ -135,14 +136,17 @@ class TaskHandler(object):
41 > # them for other tasks if there is
42 > more to do. 'options': options.copy()
43 > }
44 > - result = getattr(inst, func)(**kwargs)
45 > + returncode, msgs = getattr(inst,
46 > func)(**kwargs)
47 > + returncodes.append(returncode)
48 > if show_progress:
49 > # make sure the final progress is
50 > displayed self.progress_bar.display()
51 > print()
52 > self.progress_bar.stop()
53 > if self.callback:
54 > - self.callback(result)
55 > + self.callback(msgs)
56 > +
57 > + return returncodes
58 >
59 >
60 > def print_results(results):
61 > @@ -237,4 +241,6 @@ def emaint_main(myargv):
62 > task_opts = options.__dict__
63 > task_opts['return-messages'] = True
64 > taskmaster = TaskHandler(callback=print_results,
65 > module_output=sys.stdout)
66 > - taskmaster.run_tasks(tasks, func, status, options=task_opts)
67 > + returncodes = taskmaster.run_tasks(tasks, func, status,
68 > options=task_opts) +
69 > + sys.exit(False in returncodes)
70 > diff --git a/pym/portage/emaint/modules/binhost/binhost.py
71 > b/pym/portage/emaint/modules/binhost/binhost.py
72 > index cf1213e..527b02f 100644
73 > --- a/pym/portage/emaint/modules/binhost/binhost.py
74 > +++ b/pym/portage/emaint/modules/binhost/binhost.py
75 > @@ -86,7 +86,9 @@ class BinhostHandler(object):
76 > stale = set(metadata).difference(cpv_all)
77 > for cpv in stale:
78 > errors.append("'%s' is not in the
79 > 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'"
156 > % (pkg, mtime_str))
157 > - return errors
158 > + if errors:
159 > + return (False, errors)
160 > + return (True, None)
161 >
162 >
163 > def fix(self, **kwargs):
164 > @@ -247,13 +249,13 @@ class MergesHandler(object):
165 > module_output = kwargs.get('module_output', None)
166 > failed_pkgs = self._failed_pkgs()
167 > if not failed_pkgs:
168 > - return ['No failed merges found.']
169 > + return (True, ['No failed merges found.'])
170 >
171 > pkg_invalid_entries = set()
172 > pkg_atoms = set()
173 > self._get_pkg_atoms(failed_pkgs, pkg_atoms,
174 > pkg_invalid_entries) if pkg_invalid_entries:
175 > - return pkg_invalid_entries
176 > + return (False, pkg_invalid_entries)
177 >
178 > try:
179 > self._tracking_file.save(failed_pkgs)
180 > @@ -261,7 +263,7 @@ class MergesHandler(object):
181 > errors = ['Unable to save failed merges to
182 > tracking file: %s\n' % str(ex)]
183 > errors.append(', '.join(sorted(failed_pkgs)))
184 > - return errors
185 > + return (False, errors)
186 > self._remove_failed_dirs(failed_pkgs)
187 > results = self._emerge_pkg_atoms(module_output,
188 > pkg_atoms) # list any new failed merges
189 > @@ -276,12 +278,14 @@ class MergesHandler(object):
190 > if not vardb.cpv_exists(pkg_name):
191 > still_failed_pkgs[pkg] = mtime
192 > self._tracking_file.save(still_failed_pkgs)
193 > - return results
194 > + if still_failed_pkgs:
195 > + return (False, results)
196 > + return (True, results)
197 >
198 >
199 > def purge(self, **kwargs):
200 > """Attempt to remove previously saved tracking
201 > file.""" if not self._tracking_file.exists():
202 > - return ['Tracking file not found.']
203 > + return (True, ['Tracking file not found.'])
204 > self._tracking_file.purge()
205 > - return ['Removed tracking file.']
206 > + return (True, ['Removed tracking file.'])
207 > diff --git a/pym/portage/emaint/modules/move/move.py
208 > b/pym/portage/emaint/modules/move/move.py
209 > index 41ca167..1c2636d 100644
210 > --- a/pym/portage/emaint/modules/move/move.py
211 > +++ b/pym/portage/emaint/modules/move/move.py
212 > @@ -123,7 +123,10 @@ class MoveHandler(object):
213 > errors.append("'%s' has outdated
214 > metadata" % cpv) if onProgress:
215 > onProgress(maxval, i+1)
216 > - return errors
217 > +
218 > + if errors:
219 > + return (False, errors)
220 > + return (True, None)
221 >
222 > def fix(self, **kwargs):
223 > onProgress = kwargs.get('onProgress', None)
224 > @@ -156,7 +159,9 @@ class MoveHandler(object):
225 > # Searching for updates in all the metadata is
226 > relatively slow, so this # is where the progress bar comes out of
227 > indeterminate mode. self._tree.dbapi.update_ents(allupdates,
228 > 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..0d65d41 100644
239 > --- a/pym/portage/emaint/modules/resume/resume.py
240 > +++ b/pym/portage/emaint/modules/resume/resume.py
241 > @@ -37,7 +37,7 @@ class CleanResume(object):
242 > finally:
243 > if onProgress:
244 > onProgress(maxval, i+1)
245 > - return messages
246 > + return (True, messages)
247 >
248 > def fix(self, **kwargs):
249 > onProgress = kwargs.get('onProgress', None)
250 > @@ -56,3 +56,4 @@ class CleanResume(object):
251 > onProgress(maxval, i+1)
252 > if delete_count:
253 > mtimedb.commit()
254 > + return (True, None)
255 > diff --git a/pym/portage/emaint/modules/sync/sync.py
256 > b/pym/portage/emaint/modules/sync/sync.py
257 > index 15d63e2..d867699 100644
258 > --- a/pym/portage/emaint/modules/sync/sync.py
259 > +++ b/pym/portage/emaint/modules/sync/sync.py
260 > @@ -127,8 +127,8 @@ class SyncRepos(object):
261 > % (bold(", ".join(repos))) +
262 > "\n ...returning" ]
263 > if return_messages:
264 > - return msgs
265 > - return
266 > + return (False, msgs)
267 > + return (False, None)
268 > return self._sync(selected, return_messages,
269 > emaint_opts=options)
270 >
271 > @@ -211,8 +211,8 @@ class SyncRepos(object):
272 > msgs.append("Emaint sync, nothing to sync...
273 > returning") if return_messages:
274 > msgs.extend(self.rmessage([('None',
275 > os.EX_OK)], 'sync'))
276 > - return msgs
277 > - return
278 > + return (True, msgs)
279 > + return (True, None)
280 > # Portage needs to ensure a sane umask for the files
281 > it creates. os.umask(0o22)
282 >
283 > @@ -232,9 +232,14 @@ class SyncRepos(object):
284 > sync_scheduler.wait()
285 > retvals = sync_scheduler.retvals
286 > msgs.extend(sync_scheduler.msgs)
287 > + returncode = True
288 >
289 > if retvals:
290 > msgs.extend(self.rmessage(retvals, 'sync'))
291 > + for repo, returncode in retvals:
292 > + if returncode != os.EX_OK:
293 > + returncode = False
294 > + break
295 > else:
296 > msgs.extend(self.rmessage([('None',
297 > os.EX_OK)], 'sync'))
298 >
299 > @@ -244,6 +249,8 @@ class SyncRepos(object):
300 > rcode =
301 > sync_manager.perform_post_sync_hook('') if rcode:
302 > msgs.extend(self.rmessage([('None',
303 > rcode)], 'post-sync'))
304 > + if rcode != os.EX_OK:
305 > + returncode = False
306 >
307 > # Reload the whole config.
308 > portage._sync_mode = False
309 > @@ -254,8 +261,8 @@ class SyncRepos(object):
310 > self.emerge_config.opts)
311 >
312 > if return_messages:
313 > - return msgs
314 > - return
315 > + return (returncode, msgs)
316 > + return (returncode, None)
317 >
318 >
319 > def _do_pkg_moves(self):
320 > @@ -355,7 +362,6 @@ class SyncScheduler(AsyncScheduler):
321 > # that hooks will be called in a backward-compatible
322 > manner # even if all sync tasks have failed.
323 > hooks_enabled = True
324 > - returncode = task.returncode
325 > if task.returncode == os.EX_OK:
326 > returncode, message, updatecache_flg,
327 > hooks_enabled = task.result if message:
328 > diff --git a/pym/portage/emaint/modules/world/world.py
329 > b/pym/portage/emaint/modules/world/world.py
330 > index 2c9dbff..ebc3adc 100644
331 > --- a/pym/portage/emaint/modules/world/world.py
332 > +++ b/pym/portage/emaint/modules/world/world.py
333 > @@ -65,7 +65,9 @@ class WorldHandler(object):
334 > errors += ["'%s' is not installed" % x for x
335 > in self.not_installed] else:
336 > errors.append(self.world_file + " could not
337 > be opened for reading")
338 > - return errors
339 > + if errors:
340 > + return (False, errors)
341 > + return (True, None)
342 >
343 > def fix(self, **kwargs):
344 > onProgress = kwargs.get('onProgress', None)
345 > @@ -83,7 +85,9 @@ class WorldHandler(object):
346 > except
347 > portage.exception.PortageException: errors.append("%s could not be
348 > opened for writing" % \ self.world_file)
349 > - return errors
350 > + if errors:
351 > + return (False, errors)
352 > + return (True, None)
353 > finally:
354 > world_set.unlock()
355 >
356 > diff --git a/pym/portage/tests/emerge/test_simple.py
357 > b/pym/portage/tests/emerge/test_simple.py
358 > index b1a2af5..5930f6c 100644
359 > --- a/pym/portage/tests/emerge/test_simple.py
360 > +++ b/pym/portage/tests/emerge/test_simple.py
361 > @@ -382,6 +382,7 @@ pkg_preinst() {
362 > "PORTAGE_PYTHON" : portage_python,
363 > "PORTAGE_REPOSITORIES" :
364 > settings.repositories.config_string(), "PORTAGE_TMPDIR" :
365 > portage_tmpdir,
366 > + "PORT_LOGDIR" : portage_tmpdir,
367 > "PYTHONDONTWRITEBYTECODE" :
368 > os.environ.get("PYTHONDONTWRITEBYTECODE", ""), "PYTHONPATH" :
369 > pythonpath, "__PORTAGE_TEST_PATH_OVERRIDE" : fake_bin,
370
371 merged :) thank you
372
373 --
374 Brian Dolbec <dolsen>