Re: [gentoo-portage-dev] [PROPOSAL] Don't split user visible messages across multiple lines

2017-03-16 Thread Alexandru Elisei
On Thu, Mar 16, 2017 at 12:32 AM, Brian Dolbec  wrote:
> That could be pretty hard to do for all messages.
> Especially messages with embedded data
>
>   eg:  "%s is missing %s required use flag..." % ('sys-apps/foo', 'bar')

For that case we could use:

"%s is missing %s required use flag..." % \
% ('sys-apps/foo', 'bar')

> I know I don't always enforce the line length for a few characters,
> also when clarity is more important than line length.

I totally agree with that.

> We could also increase the max. line length to something like 120 or 130.

I think more people should chime in on that. I use vertical splits for
the screen when coding, and 120 characters is too long for me, but if
the preferred width ends up changing to 120 or 130 I can work with it.



[gentoo-portage-dev] [PATCH] emerge: sync given repos even if auto-sync is false

2017-02-20 Thread Alexandru Elisei
---
#
# This will be followed by a series of patches to ignore repos with sync-type
# unset, as per the man page, fail when sync-uri is not specified and an update
# to the emerge.1 man page to reflect the change in behavior.
#
 pym/_emerge/actions.py  |  8 +--
 pym/portage/emaint/modules/sync/sync.py | 40 -
 2 files changed, 10 insertions(+), 38 deletions(-)

diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py
index 71e362e..cc0269d 100644
--- a/pym/_emerge/actions.py
+++ b/pym/_emerge/actions.py
@@ -1999,9 +1999,13 @@ def action_sync(emerge_config, trees=DeprecationWarning,
action=action, args=[], trees=trees, opts=opts)
 
syncer = SyncRepos(emerge_config)
-
return_messages = "--quiet" not in emerge_config.opts
-   success, msgs = syncer.auto_sync(options={'return-messages': 
return_messages})
+   options = {'return-messages' : return_messages}
+   if emerge_config.args:
+   options['repo'] = emerge_config.args
+   success, msgs = syncer.repo(options=options)
+   else:
+   success, msgs = syncer.auto_sync(options=options)
if return_messages:
print_results(msgs)
 
diff --git a/pym/portage/emaint/modules/sync/sync.py 
b/pym/portage/emaint/modules/sync/sync.py
index 08a92a7..0aeccb3 100644
--- a/pym/portage/emaint/modules/sync/sync.py
+++ b/pym/portage/emaint/modules/sync/sync.py
@@ -148,43 +148,11 @@ class SyncRepos(object):
 
 
def _get_repos(self, auto_sync_only=True):
-   selected_repos = []
-   unknown_repo_names = []
-   missing_sync_type = []
-   if self.emerge_config.args:
-   for repo_name in self.emerge_config.args:
-   #print("_get_repos(): repo_name =", repo_name)
-   try:
-   repo = 
self.emerge_config.target_config.settings.repositories[repo_name]
-   except KeyError:
-   unknown_repo_names.append(repo_name)
-   else:
-   selected_repos.append(repo)
-   if repo.sync_type is None:
-   missing_sync_type.append(repo)
-
-   if unknown_repo_names:
-   writemsg_level("!!! %s\n" % _("Unknown repo(s): 
%s") %
-   " ".join(unknown_repo_names),
-   level=logging.ERROR, noiselevel=-1)
-
-   if missing_sync_type:
-   writemsg_level("!!! %s\n" %
-   _("Missing sync-type for repo(s): %s") %
-   " ".join(repo.name for repo in 
missing_sync_type),
-   level=logging.ERROR, noiselevel=-1)
-
-   if unknown_repo_names or missing_sync_type:
-   writemsg_level("Missing or unknown repos... 
returning",
-   level=logging.INFO, noiselevel=2)
-   return []
-
-   else:
-   
selected_repos.extend(self.emerge_config.target_config.settings.repositories)
-   #print("_get_repos(), selected =", selected_repos)
+   repos = self.emerge_config.target_config.settings.repositories
+   #print("_get_repos(), repos =", repos)
if auto_sync_only:
-   return self._filter_auto(selected_repos)
-   return selected_repos
+   return self._filter_auto(repos)
+   return repos
 
 
def _filter_auto(self, repos):
-- 
2.10.2




[gentoo-portage-dev] [PATCH V2] sync.py: extend the checks in _get_repos() and fail when necessary (bugs 567478, 576272, 601054)

2017-02-19 Thread Alexandru Elisei
The existence of the sync-type attribute is now being checked for all
repos, not just for the repos given as arguments to emerge --sync. A
message is returned when a repo without sync-type is found and sync
will fail.

Emerge will now fail if at least one of the repos given to emerge --sync
doesn't exist or has auto-sync disabled.

auto_sync() and all_repos() also fail when no valid repos are found, but
they return success when no auto-sync repos are defined, or no repos are
defined at all on the system.

This commit and commit f57ae38 'emerge: make emerge --sync print
messages from SyncRepos.auto_sync()' shoulg solve bugs 567478, 576282
and partly 601054.
---
 pym/portage/emaint/modules/sync/sync.py | 104 +++-
 1 file changed, 62 insertions(+), 42 deletions(-)

diff --git a/pym/portage/emaint/modules/sync/sync.py 
b/pym/portage/emaint/modules/sync/sync.py
index 08a92a7..4d66411 100644
--- a/pym/portage/emaint/modules/sync/sync.py
+++ b/pym/portage/emaint/modules/sync/sync.py
@@ -89,25 +89,45 @@ class SyncRepos(object):
def auto_sync(self, **kwargs):
'''Sync auto-sync enabled repos'''
options = kwargs.get('options', None)
-   selected = self._get_repos(True)
if options:
return_messages = options.get('return-messages', False)
else:
return_messages = False
-   return self._sync(selected, return_messages,
-   emaint_opts=options)
+   success, selected, msgs = self._get_repos(True)
+   if not success:
+   if return_messages:
+   msgs.append(red(" * ") + \
+   "Errors were encountered while getting 
repos... returning")
+   return (False, msgs)
+   return (False, None)
+   if not selected:
+   if return_messages:
+   msgs.append("Nothing to sync... returning")
+   return (True, msgs)
+   return (True, None)
+   return self._sync(selected, return_messages, 
emaint_opts=options)
 
 
def all_repos(self, **kwargs):
'''Sync all repos defined in repos.conf'''
-   selected = self._get_repos(auto_sync_only=False)
options = kwargs.get('options', None)
if options:
return_messages = options.get('return-messages', False)
else:
return_messages = False
-   return self._sync(selected, return_messages,
-   emaint_opts=options)
+   success, selected, msgs = self._get_repos(auto_sync_only=False)
+   if not success:
+   if return_messages:
+   msgs.append(red(" * ") + \
+   "Errors were encountered while getting 
repos... returning")
+   return (False, msgs)
+   return (False, None)
+   if not selected:
+   if return_messages:
+   msgs.append("Nothing to sync... returning")
+   return (True, msgs)
+   return (True, None)
+   return self._sync(selected, return_messages, 
emaint_opts=options)
 
 
def repo(self, **kwargs):
@@ -120,16 +140,17 @@ class SyncRepos(object):
return_messages = False
if isinstance(repos, _basestring):
repos = repos.split()
-   available = self._get_repos(auto_sync_only=False)
+   success, available, msgs = self._get_repos(auto_sync_only=False)
+   # Ignore errors from _get_repos(), we only want to know if the 
repo
+   # exists.
selected = self._match_repos(repos, available)
if not selected:
-   msgs = [red(" * ") + "The specified repos were not 
found: %s" %
-   (bold(", ".join(repos))) + "\n   ...returning"]
+   msgs.append(red(" * ") + "The specified repos are 
invalid or missing: %s" %
+   (bold(", ".join(repos))) + "\n   ...returning")
if return_messages:
return (False, msgs)
return (False, None)
-   return self._sync(selected, return_messages,
-   emaint_opts=options)
+   return self._sync(selected, return_messages, 
emaint_opts=options)
 
 
@staticmethod
@@ -148,10 +169,11 @@ class SyncRepos(object):
 
 
def _get_repos(self, auto_sync_only=True):
+   msgs = []
+   emerge_repos = []

[gentoo-portage-dev] [PATCH] sync.py: extend the checks in _get_repos() and fail when necessary (bugs 567478, 576272, 601054)

2017-02-13 Thread Alexandru Elisei
The existence of the sync-type attribute is now being checked for all
repos, not just for the repos given as arguments to emerge --sync. A
message is returned when a repo without sync-type is found and sync
will fail.

Emerge will now fail if at least one of the repos given to emerge --sync
doesn't exist or has auto-sync disabled.

auto_sync() and all_repos() also fail when no valid repos are found, but
they return success when no auto-sync repos are defined, or no repos are
defined at all on the system.

This commit and commit f57ae38 'emerge: make emerge --sync print
messages from SyncRepos.auto_sync()' shoulg solve bugs 567478, 576282
and partly 601054.
---
 pym/portage/emaint/modules/sync/sync.py | 104 +++-
 1 file changed, 62 insertions(+), 42 deletions(-)

diff --git a/pym/portage/emaint/modules/sync/sync.py
b/pym/portage/emaint/modules/sync/sync.py
index 08a92a7..4d66411 100644
--- a/pym/portage/emaint/modules/sync/sync.py
+++ b/pym/portage/emaint/modules/sync/sync.py
@@ -89,25 +89,45 @@ class SyncRepos(object):
def auto_sync(self, **kwargs):
'''Sync auto-sync enabled repos'''
options = kwargs.get('options', None)
-   selected = self._get_repos(True)
if options:
return_messages = options.get('return-messages', False)
else:
return_messages = False
-   return self._sync(selected, return_messages,
-   emaint_opts=options)
+   success, selected, msgs = self._get_repos(True)
+   if not success:
+   if return_messages:
+   msgs.append(red(" * ") + \
+   "Errors were encountered while getting 
repos... returning")
+   return (False, msgs)
+   return (False, None)
+   if not selected:
+   if return_messages:
+   msgs.append("Nothing to sync... returning")
+   return (True, msgs)
+   return (True, None)
+   return self._sync(selected, return_messages, 
emaint_opts=options)


def all_repos(self, **kwargs):
'''Sync all repos defined in repos.conf'''
-   selected = self._get_repos(auto_sync_only=False)
options = kwargs.get('options', None)
if options:
return_messages = options.get('return-messages', False)
else:
return_messages = False
-   return self._sync(selected, return_messages,
-   emaint_opts=options)
+   success, selected, msgs = self._get_repos(auto_sync_only=False)
+   if not success:
+   if return_messages:
+   msgs.append(red(" * ") + \
+   "Errors were encountered while getting 
repos... returning")
+   return (False, msgs)
+   return (False, None)
+   if not selected:
+   if return_messages:
+   msgs.append("Nothing to sync... returning")
+   return (True, msgs)
+   return (True, None)
+   return self._sync(selected, return_messages, 
emaint_opts=options)


def repo(self, **kwargs):
@@ -120,16 +140,17 @@ class SyncRepos(object):
return_messages = False
if isinstance(repos, _basestring):
repos = repos.split()
-   available = self._get_repos(auto_sync_only=False)
+   success, available, msgs = self._get_repos(auto_sync_only=False)
+   # Ignore errors from _get_repos(), we only want to know if the 
repo
+   # exists.
selected = self._match_repos(repos, available)
if not selected:
-   msgs = [red(" * ") + "The specified repos were not 
found: %s" %
-   (bold(", ".join(repos))) + "\n   ...returning"]
+   msgs.append(red(" * ") + "The specified repos are 
invalid or missing: %s" %
+   (bold(", ".join(repos))) + "\n   ...returning")
if return_messages:
return (False, msgs)
return (False, None)
-   return self._sync(selected, return_messages,
-   emaint_opts=options)
+   return self._sync(selected, return_messages, 
emaint_opts=options)


@staticmethod
@@ -148,10 +169,11 @@ class SyncRepos(object):


def _get_repos(self, auto_sync_only=True):
+   msgs = []
+   emerge_repos = []
selected_repos = 

[gentoo-portage-dev] [PATCH] emerge: make emerge --sync print messages from SyncRepos.auto_sync()

2017-02-04 Thread Alexandru Elisei
Emerge will only display messages if --quiet is not set.

The word 'emaint' has been removed from two messages to avoid confusion.
---
 pym/_emerge/actions.py  | 7 +--
 pym/portage/emaint/modules/sync/sync.py | 7 +++
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py
index ccb6a5d..71e362e 100644
--- a/pym/_emerge/actions.py
+++ b/pym/_emerge/actions.py
@@ -68,6 +68,7 @@ from portage._global_updates import _global_updates
 from portage.sync.old_tree_timestamp import old_tree_timestamp_warn
 from portage.localization import _
 from portage.metadata import action_metadata
+from portage.emaint.main import print_results

 from _emerge.clear_caches import clear_caches
 from _emerge.countdown import countdown
@@ -1999,8 +2000,10 @@ def action_sync(emerge_config, trees=DeprecationWarning,

syncer = SyncRepos(emerge_config)

-
-   success, msgs = syncer.auto_sync(options={'return-messages': False})
+   return_messages = "--quiet" not in emerge_config.opts
+   success, msgs = syncer.auto_sync(options={'return-messages': 
return_messages})
+   if return_messages:
+   print_results(msgs)

return os.EX_OK if success else 1

diff --git a/pym/portage/emaint/modules/sync/sync.py
b/pym/portage/emaint/modules/sync/sync.py
index 076297a..ca5461d 100644
--- a/pym/portage/emaint/modules/sync/sync.py
+++ b/pym/portage/emaint/modules/sync/sync.py
@@ -123,9 +123,8 @@ class SyncRepos(object):
available = self._get_repos(auto_sync_only=False)
selected = self._match_repos(repos, available)
if not selected:
-   msgs = [red(" * ") + "Emaint sync, The specified repos 
were not found: %s"
-   % (bold(", ".join(repos))) + "\n   ...returning"
-   ]
+   msgs = [red(" * ") + "The specified repos were not 
found: %s" %
+   (bold(", ".join(repos))) + "\n   ...returning"]
if return_messages:
return (False, msgs)
return (False, None)
@@ -208,7 +207,7 @@ class SyncRepos(object):
selected_repos = [repo for repo in selected_repos if 
repo.sync_type
is not None]
msgs = []
if not selected_repos:
-   msgs.append("Emaint sync, nothing to sync... returning")
+   msgs.append("Nothing to sync... returning")
if return_messages:
msgs.extend(self.rmessage([('None', os.EX_OK)], 
'sync'))
return (True, msgs)
-- 
2.10.2



[gentoo-portage-dev] [PATCH] sync.py: add warning when sync-type is not set

2017-01-31 Thread Alexandru Elisei
---
 pym/portage/emaint/modules/sync/sync.py | 12 +---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/pym/portage/emaint/modules/sync/sync.py
b/pym/portage/emaint/modules/sync/sync.py
index 076297a..b4d65e7 100644
--- a/pym/portage/emaint/modules/sync/sync.py
+++ b/pym/portage/emaint/modules/sync/sync.py
@@ -205,9 +205,15 @@ class SyncRepos(object):
k = "--" + k.replace("_", "-")
self.emerge_config.opts[k] = v

-   selected_repos = [repo for repo in selected_repos if 
repo.sync_type
is not None]
msgs = []
-   if not selected_repos:
+   valid_repos = []
+   for repo in selected_repos:
+   if repo.sync_type is None:
+   msgs.extend([warn(" * ") + "Missing sync-type 
for repo: " + \
+   repo.name +  ", skipping...\n"])
+   else:
+   valid_repos.append(repo)
+   if not valid_repos:
msgs.append("Emaint sync, nothing to sync... returning")
if return_messages:
msgs.extend(self.rmessage([('None', os.EX_OK)], 
'sync'))
@@ -223,7 +229,7 @@ class SyncRepos(object):
if 'parallel-fetch' in self.emerge_config.
target_config.settings.features else 1)
sync_scheduler = SyncScheduler(emerge_config=self.emerge_config,
-   selected_repos=selected_repos, 
sync_manager=sync_manager,
+   selected_repos=valid_repos, sync_manager=sync_manager,
max_jobs=max_jobs,
event_loop=global_event_loop() if 
portage._internal_caller else
EventLoop(main=False))
-- 
2.10.2



[gentoo-portage-dev] [PATCH] test_sync_local: add test for auto-sync set to 'no'

2017-01-29 Thread Alexandru Elisei
---
 pym/portage/tests/sync/test_sync_local.py | 12 ++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/pym/portage/tests/sync/test_sync_local.py
b/pym/portage/tests/sync/test_sync_local.py
index bec2e6a..1d38562 100644
--- a/pym/portage/tests/sync/test_sync_local.py
+++ b/pym/portage/tests/sync/test_sync_local.py
@@ -42,7 +42,7 @@ class SyncLocalTestCase(TestCase):
location = %(EPREFIX)s/var/repositories/test_repo
sync-type = %(sync-type)s
sync-uri = 
file:/%(EPREFIX)s/var/repositories/test_repo_sync
-   auto-sync = yes
+   auto-sync = %(auto-sync)s
%(repo_extra_keys)s
""")

@@ -87,9 +87,11 @@ class SyncLocalTestCase(TestCase):
committer_name = "Gentoo Dev"
committer_email = "gentoo-...@gentoo.org"

-   def repos_set_conf(sync_type, dflt_keys=None, xtra_keys=None):
+   def repos_set_conf(sync_type, dflt_keys=None, xtra_keys=None,
+   auto_sync="yes"):
env["PORTAGE_REPOSITORIES"] = repos_conf % {\
"EPREFIX": eprefix, "sync-type": sync_type,
+   "auto-sync": auto_sync,
"default_keys": "" if dflt_keys is None else 
dflt_keys,
"repo_extra_keys": "" if xtra_keys is None else 
xtra_keys}

@@ -100,6 +102,12 @@ class SyncLocalTestCase(TestCase):
os.unlink(os.path.join(metadata_dir, 'timestamp.chk'))

sync_cmds = (
+   (homedir, lambda: repos_set_conf("rsync", 
auto_sync="no")),
+   (homedir, cmds["emerge"] + ("--sync",)),
+   (homedir, lambda: self.assertFalse(os.path.exists(
+   os.path.join(repo.location, "dev-libs", "A")
+   ), "dev-libs/A found, expected missing")),
+   (homedir, lambda: repos_set_conf("rsync", 
auto_sync="yes")),
(homedir, cmds["emerge"] + ("--sync",)),
(homedir, lambda: self.assertTrue(os.path.exists(
os.path.join(repo.location, "dev-libs", "A")
-- 
2.10.2



[gentoo-portage-dev] [PATCH] man/portage.5: replace repos.conf sync-type 'websync' with 'webrsync'

2017-01-26 Thread Alexandru Elisei
---
 man/portage.5 | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/man/portage.5 b/man/portage.5
index 2cacafc..7d40b95 100644
--- a/man/portage.5
+++ b/man/portage.5
@@ -989,7 +989,7 @@ repository has changed since the previous sync operation.
 .B sync\-type
 Specifies type of synchronization performed by `emerge \-\-sync`.
 .br
-Valid non\-empty values: cvs, git, rsync, svn, websync (emerge-webrsync)
+Valid non\-empty values: cvs, git, rsync, svn, webrsync (emerge-webrsync)
 .br
 This attribute can be set to empty value to disable synchronization of given
 repository. Empty value is default.
-- 
2.10.2



[gentoo-portage-dev] Re: [PATCH V2] emerge: check correctly for --sync success (bug 606588)

2017-01-20 Thread Alexandru Elisei
The class SyncRepos will keep a list of (repo, returncode) tuples.
If sync is unsuccessful emerge will use this list to return the first
failed repository return code.
---
 pym/_emerge/actions.py  |  9 ++---
 pym/portage/emaint/modules/sync/sync.py | 11 ++-
 2 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py
index 6704afc..89cf501 100644
--- a/pym/_emerge/actions.py
+++ b/pym/_emerge/actions.py
@@ -2000,10 +2000,13 @@ def action_sync(emerge_config, trees=DeprecationWarning,
syncer = SyncRepos(emerge_config)


-   retvals = syncer.auto_sync(options={'return-messages': False})
+   success, msgs = syncer.auto_sync(options={'return-messages': False})

-   if retvals:
-   return retvals[0][1]
+   if not success:
+   retvals = syncer.retvals
+   for repo, retval in retvals:
+   if retval != os.EX_OK:
+   return retval
return os.EX_OK


diff --git a/pym/portage/emaint/modules/sync/sync.py
b/pym/portage/emaint/modules/sync/sync.py
index d867699..de150a1 100644
--- a/pym/portage/emaint/modules/sync/sync.py
+++ b/pym/portage/emaint/modules/sync/sync.py
@@ -84,6 +84,7 @@ class SyncRepos(object):
self.xterm_titles = "notitles" not in \
self.emerge_config.target_config.settings.features
emergelog(self.xterm_titles, " === sync")
+   self.retvals = []


def auto_sync(self, **kwargs):
@@ -230,14 +231,14 @@ class SyncRepos(object):

sync_scheduler.start()
sync_scheduler.wait()
-   retvals = sync_scheduler.retvals
+   self.retvals = sync_scheduler.retvals
msgs.extend(sync_scheduler.msgs)
returncode = True

-   if retvals:
-   msgs.extend(self.rmessage(retvals, 'sync'))
-   for repo, returncode in retvals:
-   if returncode != os.EX_OK:
+   if self.retvals:
+   msgs.extend(self.rmessage(self.retvals, 'sync'))
+   for repo, retval in self.retvals:
+   if retval != os.EX_OK:
returncode = False
break
else:
-- 
2.10.2



[gentoo-portage-dev] [PATCH] emerge: check correctly for --sync success (bug 606588)

2017-01-20 Thread Alexandru Elisei
---
 pym/_emerge/actions.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py
index 6704afc..5e8d5c5 100644
--- a/pym/_emerge/actions.py
+++ b/pym/_emerge/actions.py
@@ -2000,10 +2000,10 @@ def action_sync(emerge_config, trees=DeprecationWarning,
syncer = SyncRepos(emerge_config)


-   retvals = syncer.auto_sync(options={'return-messages': False})
+   success = syncer.auto_sync(options={'return-messages': False})

-   if retvals:
-   return retvals[0][1]
+   if not success:
+   return 1
return os.EX_OK


-- 
2.10.2



[gentoo-portage-dev] Re: [PATCH V2] emaint: add more meaningful error messages to the logs module

2017-01-19 Thread Alexandru Elisei
The logs module can fail for a variety of reasons: the PORT_LOGDIR
variable isn't set in make.conf or it doesn't point to a directory; the
PORT_LOGDIR_CLEAN command uses a binary which isn't present in the
system or the binary itself failed during execution. There is only one
generic error message for all these cases. The patch adds error messages
that better describe the reason for the failure.
---
#
# I've removed the check for rval being None because the only code path that
# made that possible has been changed to return error code 78.
#
 pym/portage/emaint/modules/logs/logs.py | 13 ++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/pym/portage/emaint/modules/logs/logs.py
b/pym/portage/emaint/modules/logs/logs.py
index 028084a..1b39d42 100644
--- a/pym/portage/emaint/modules/logs/logs.py
+++ b/pym/portage/emaint/modules/logs/logs.py
@@ -8,6 +8,11 @@ from portage.util import shlex_split, varexpand
 ## default clean command from make.globals
 ## PORT_LOGDIR_CLEAN = 'find "${PORT_LOGDIR}" -type f ! -name
"summary.log*" -mtime +7 -delete'

+ERROR_MESSAGES = {
+   78  : "PORT_LOGDIR variable not set or PORT_LOGDIR not a 
directory.",
+   127 : "PORT_LOGDIR_CLEAN command not found."
+}
+
 class CleanLogs(object):

short_desc = "Clean PORT_LOGDIR logs"
@@ -81,7 +86,7 @@ class CleanLogs(object):
def _clean_logs(clean_cmd, settings):
logdir = settings.get("PORT_LOGDIR")
if logdir is None or not os.path.isdir(logdir):
-   return
+   return 78

variables = {"PORT_LOGDIR" : logdir}
cmd = [varexpand(x, mydict=variables) for x in clean_cmd]
@@ -97,8 +102,10 @@ class CleanLogs(object):
def _convert_errors(rval):
msg = []
if rval != os.EX_OK:
-   msg.append("PORT_LOGDIR_CLEAN command returned %s"
-   % ("%d" % rval if rval else "None"))
+   if rval in ERROR_MESSAGES:
+   msg.append(ERROR_MESSAGES[rval])
+   else:
+   msg.append("PORT_LOGDIR_CLEAN command returned 
%s" % rval)
msg.append("See the make.conf(5) man page for "
"PORT_LOGDIR_CLEAN usage instructions.")
return msg
-- 
2.10.2



[gentoo-portage-dev] [PATCH] emaint: add more meaningful error messages to the logs module

2017-01-19 Thread Alexandru Elisei
The logs module can fail for a variety of reasons: the PORT_LOGDIR
variable isn't set in make.conf or it doesn't point to a directory; the
PORT_LOGDIR_CLEAN command uses a binary which isn't present in the
system or the binary itself failed during execution. There is only one
generic error message for all these cases. The patch adds error messages
that better describe the reason for the failure.
---
 pym/portage/emaint/modules/logs/logs.py | 14 +++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/pym/portage/emaint/modules/logs/logs.py
b/pym/portage/emaint/modules/logs/logs.py
index fe65cf5..7633741 100644
--- a/pym/portage/emaint/modules/logs/logs.py
+++ b/pym/portage/emaint/modules/logs/logs.py
@@ -8,6 +8,11 @@ from portage.util import shlex_split, varexpand
 ## default clean command from make.globals
 ## PORT_LOGDIR_CLEAN = 'find "${PORT_LOGDIR}" -type f ! -name
"summary.log*" -mtime +7 -delete'

+ERROR_MESSAGES = {
+   78  : "PORT_LOGDIR variable not set or PORT_LOGDIR not a 
directory.",
+   127 : "PORT_LOGDIR_CLEAN command not found."
+}
+
 class CleanLogs(object):

short_desc = "Clean PORT_LOGDIR logs"
@@ -80,7 +85,7 @@ class CleanLogs(object):
def _clean_logs(clean_cmd, settings):
logdir = settings.get("PORT_LOGDIR")
if logdir is None or not os.path.isdir(logdir):
-   return
+   return 78

variables = {"PORT_LOGDIR" : logdir}
cmd = [varexpand(x, mydict=variables) for x in clean_cmd]
@@ -96,8 +101,11 @@ class CleanLogs(object):
def _convert_errors(rval):
msg = []
if rval != os.EX_OK:
-   msg.append("PORT_LOGDIR_CLEAN command returned %s"
-   % ("%d" % rval if rval else "None"))
+   if rval in ERROR_MESSAGES:
+   msg.append(ERROR_MESSAGES[rval])
+   else:
+   msg.append("PORT_LOGDIR_CLEAN command returned 
%s"
+   % ("%d" % rval if rval else "None"))
msg.append("See the make.conf(5) man page for "
"PORT_LOGDIR_CLEAN usage instructions.")
return msg
-- 
2.10.2



[gentoo-portage-dev] Re: [PATCH V3] emaint: exit with non-zero status code when module fails (bug 567478)

2017-01-18 Thread Alexandru Elisei
   errors.append("%s could not be opened 
for writing" % \
self.world_file)
-   return errors
+   if errors:
+   return (False, errors)
+   return (True, None)
finally:
world_set.unlock()

diff --git a/pym/portage/tests/emerge/test_simple.py
b/pym/portage/tests/emerge/test_simple.py
index b1a2af5..5930f6c 100644
--- a/pym/portage/tests/emerge/test_simple.py
+++ b/pym/portage/tests/emerge/test_simple.py
@@ -382,6 +382,7 @@ pkg_preinst() {
"PORTAGE_PYTHON" : portage_python,
"PORTAGE_REPOSITORIES" : 
settings.repositories.config_string(),
"PORTAGE_TMPDIR" : portage_tmpdir,
+   "PORT_LOGDIR" : portage_tmpdir,
"PYTHONDONTWRITEBYTECODE" : 
os.environ.get("PYTHONDONTWRITEBYTECODE", ""),
"PYTHONPATH" : pythonpath,
"__PORTAGE_TEST_PATH_OVERRIDE" : fake_bin,
-- 
2.10.2

On 1/17/17, Alexandru Elisei <alexandru.eli...@gmail.com> wrote:
> Currently module functions return a message to emaint after invocation.
> Emaint prints this message then exits normally (with a success return
> code) even if the module encountered an error. This patch aims to
> change this by having each module public function return a tuple of
> (returncode, message). Emaint will inspect the return code and will
> exit unsuccessfully if necessary.
> ---
>  pym/portage/emaint/main.py| 11 +--
>  pym/portage/emaint/modules/binhost/binhost.py |  6 --
>  pym/portage/emaint/modules/config/config.py   |  8 ++--
>  pym/portage/emaint/modules/logs/logs.py   |  9 +
>  pym/portage/emaint/modules/merges/merges.py   | 18 +++---
>  pym/portage/emaint/modules/move/move.py   |  9 +++--
>  pym/portage/emaint/modules/resume/resume.py   |  4 +++-
>  pym/portage/emaint/modules/sync/sync.py   | 19 +--
>  pym/portage/emaint/modules/world/world.py |  8 ++--
>  9 files changed, 64 insertions(+), 28 deletions(-)
>
> diff --git a/pym/portage/emaint/main.py b/pym/portage/emaint/main.py
> index 65e3545..ef4383a 100644
> --- a/pym/portage/emaint/main.py
> +++ b/pym/portage/emaint/main.py
> @@ -115,6 +115,7 @@ class TaskHandler(object):
>   """Runs the module tasks"""
>   if tasks is None or func is None:
>   return
> + returncode = os.EX_OK
>   for task in tasks:
>   inst = task()
>   show_progress = self.show_progress_bar and self.isatty
> @@ -135,14 +136,20 @@ class TaskHandler(object):
>   # them for other tasks if there is more to do.
>   'options': options.copy()
>   }
> - result = getattr(inst, func)(**kwargs)
> + rcode, msgs = getattr(inst, func)(**kwargs)
>   if show_progress:
>   # make sure the final progress is displayed
>   self.progress_bar.display()
>   print()
>   self.progress_bar.stop()
>   if self.callback:
> - self.callback(result)
> + self.callback(msgs)
> + # Keep the last error code when using the 'all' command.
> + if rcode != os.EX_OK:
> + returncode = rcode
> +
> + if returncode != os.EX_OK:
> + sys.exit(returncode)
>
>
>  def print_results(results):
> diff --git a/pym/portage/emaint/modules/binhost/binhost.py
> b/pym/portage/emaint/modules/binhost/binhost.py
> index cf1213e..8cf3da6 100644
> --- a/pym/portage/emaint/modules/binhost/binhost.py
> +++ b/pym/portage/emaint/modules/binhost/binhost.py
> @@ -86,7 +86,9 @@ class BinhostHandler(object):
>   stale = set(metadata).difference(cpv_all)
>   for cpv in stale:
>   errors.append("'%s' is not in the repository" % cpv)
> - return errors
> + if errors:
> + return (1, errors)
> + return (os.EX_OK, None)
>
>   def fix(self,  **kwargs):
>   onProgress = kwargs.get('onProgress', None)
> @@ -177,4 +179,4 @@ class BinhostHandler(object):
> 

[gentoo-portage-dev] Re: [PATCH V2] emaint: exit with non-zero status code when module fails (bug 567478)

2017-01-18 Thread Alexandru Elisei
ed" % x for x in 
self.not_installed]
else:
errors.append(self.world_file + " could not be opened 
for reading")
-   return errors
+   if errors:
+   return (False, errors)
+   return (True, None)

def fix(self, **kwargs):
onProgress = kwargs.get('onProgress', None)
@@ -83,7 +85,9 @@ class WorldHandler(object):
except portage.exception.PortageException:
errors.append("%s could not be opened 
for writing" % \
self.world_file)
-   return errors
+   if errors:
+   return (False, errors)
+   return (True, None)
finally:
world_set.unlock()

diff --git a/pym/portage/tests/emerge/test_simple.py
b/pym/portage/tests/emerge/test_simple.py
index b1a2af5..5930f6c 100644
--- a/pym/portage/tests/emerge/test_simple.py
+++ b/pym/portage/tests/emerge/test_simple.py
@@ -382,6 +382,7 @@ pkg_preinst() {
"PORTAGE_PYTHON" : portage_python,
"PORTAGE_REPOSITORIES" : 
settings.repositories.config_string(),
"PORTAGE_TMPDIR" : portage_tmpdir,
+   "PORT_LOGDIR" : portage_tmpdir,
"PYTHONDONTWRITEBYTECODE" : 
os.environ.get("PYTHONDONTWRITEBYTECODE", ""),
"PYTHONPATH" : pythonpath,
"__PORTAGE_TEST_PATH_OVERRIDE" : fake_bin,
-- 
2.10.2

On 1/17/17, Alexandru Elisei <alexandru.eli...@gmail.com> wrote:
> Currently module functions return a message to emaint after invocation.
> Emaint prints this message then exits normally (with a success return
> code) even if the module encountered an error. This patch aims to
> change this by having each module public function return a tuple of
> (returncode, message). Emaint will inspect the return code and will
> exit unsuccessfully if necessary.
> ---
>  pym/portage/emaint/main.py| 11 +--
>  pym/portage/emaint/modules/binhost/binhost.py |  6 --
>  pym/portage/emaint/modules/config/config.py   |  8 ++--
>  pym/portage/emaint/modules/logs/logs.py   |  9 +
>  pym/portage/emaint/modules/merges/merges.py   | 18 +++---
>  pym/portage/emaint/modules/move/move.py   |  9 +++--
>  pym/portage/emaint/modules/resume/resume.py   |  4 +++-
>  pym/portage/emaint/modules/sync/sync.py   | 19 +--
>  pym/portage/emaint/modules/world/world.py |  8 ++--
>  9 files changed, 64 insertions(+), 28 deletions(-)
>
> diff --git a/pym/portage/emaint/main.py b/pym/portage/emaint/main.py
> index 65e3545..ef4383a 100644
> --- a/pym/portage/emaint/main.py
> +++ b/pym/portage/emaint/main.py
> @@ -115,6 +115,7 @@ class TaskHandler(object):
>   """Runs the module tasks"""
>   if tasks is None or func is None:
>   return
> + returncode = os.EX_OK
>   for task in tasks:
>   inst = task()
>   show_progress = self.show_progress_bar and self.isatty
> @@ -135,14 +136,20 @@ class TaskHandler(object):
>   # them for other tasks if there is more to do.
>   'options': options.copy()
>   }
> - result = getattr(inst, func)(**kwargs)
> + rcode, msgs = getattr(inst, func)(**kwargs)
>   if show_progress:
>   # make sure the final progress is displayed
>   self.progress_bar.display()
>   print()
>   self.progress_bar.stop()
>   if self.callback:
> - self.callback(result)
> + self.callback(msgs)
> + # Keep the last error code when using the 'all' command.
> + if rcode != os.EX_OK:
> + returncode = rcode
> +
> + if returncode != os.EX_OK:
> + sys.exit(returncode)
>
>
>  def print_results(results):
> diff --git a/pym/portage/emaint/modules/binhost/binhost.py
> b/pym/portage/emaint/modules/binhost/binhost.py
> index cf1213e..8cf3da6 100644
> --- a/pym/portage/emaint/modules/binhost/binhost.py
> +++ b/pym/portage/emaint/modules/binhost/binhost.py
> @@ -86,7 +86,9 @@ class 

[gentoo-portage-dev] Re: [PATCH v2] emaint: exit with non-zero status code when module fails (bug 567478)

2017-01-18 Thread Alexandru Elisei
ed" % x for x in 
self.not_installed]
else:
errors.append(self.world_file + " could not be opened 
for reading")
-   return errors
+   if errors:
+   return (False, errors)
+   return (True, None)

def fix(self, **kwargs):
onProgress = kwargs.get('onProgress', None)
@@ -83,7 +85,9 @@ class WorldHandler(object):
except portage.exception.PortageException:
errors.append("%s could not be opened 
for writing" % \
self.world_file)
-   return errors
+   if errors:
+   return (False, errors)
+   return (True, None)
finally:
world_set.unlock()

diff --git a/pym/portage/tests/emerge/test_simple.py
b/pym/portage/tests/emerge/test_simple.py
index b1a2af5..5930f6c 100644
--- a/pym/portage/tests/emerge/test_simple.py
+++ b/pym/portage/tests/emerge/test_simple.py
@@ -382,6 +382,7 @@ pkg_preinst() {
"PORTAGE_PYTHON" : portage_python,
"PORTAGE_REPOSITORIES" : 
settings.repositories.config_string(),
"PORTAGE_TMPDIR" : portage_tmpdir,
+   "PORT_LOGDIR" : portage_tmpdir,
"PYTHONDONTWRITEBYTECODE" : 
os.environ.get("PYTHONDONTWRITEBYTECODE", ""),
"PYTHONPATH" : pythonpath,
"__PORTAGE_TEST_PATH_OVERRIDE" : fake_bin,
-- 
2.10.2

On 1/17/17, Alexandru Elisei <alexandru.eli...@gmail.com> wrote:
> Currently module functions return a message to emaint after invocation.
> Emaint prints this message then exits normally (with a success return
> code) even if the module encountered an error. This patch aims to
> change this by having each module public function return a tuple of
> (returncode, message). Emaint will inspect the return code and will
> exit unsuccessfully if necessary.
> ---
>  pym/portage/emaint/main.py| 11 +--
>  pym/portage/emaint/modules/binhost/binhost.py |  6 --
>  pym/portage/emaint/modules/config/config.py   |  8 ++--
>  pym/portage/emaint/modules/logs/logs.py   |  9 +
>  pym/portage/emaint/modules/merges/merges.py   | 18 +++---
>  pym/portage/emaint/modules/move/move.py   |  9 +++--
>  pym/portage/emaint/modules/resume/resume.py   |  4 +++-
>  pym/portage/emaint/modules/sync/sync.py   | 19 +--
>  pym/portage/emaint/modules/world/world.py |  8 ++--
>  9 files changed, 64 insertions(+), 28 deletions(-)
>
> diff --git a/pym/portage/emaint/main.py b/pym/portage/emaint/main.py
> index 65e3545..ef4383a 100644
> --- a/pym/portage/emaint/main.py
> +++ b/pym/portage/emaint/main.py
> @@ -115,6 +115,7 @@ class TaskHandler(object):
>   """Runs the module tasks"""
>   if tasks is None or func is None:
>   return
> + returncode = os.EX_OK
>   for task in tasks:
>   inst = task()
>   show_progress = self.show_progress_bar and self.isatty
> @@ -135,14 +136,20 @@ class TaskHandler(object):
>   # them for other tasks if there is more to do.
>   'options': options.copy()
>   }
> - result = getattr(inst, func)(**kwargs)
> + rcode, msgs = getattr(inst, func)(**kwargs)
>   if show_progress:
>   # make sure the final progress is displayed
>   self.progress_bar.display()
>   print()
>   self.progress_bar.stop()
>   if self.callback:
> - self.callback(result)
> + self.callback(msgs)
> + # Keep the last error code when using the 'all' command.
> + if rcode != os.EX_OK:
> + returncode = rcode
> +
> + if returncode != os.EX_OK:
> + sys.exit(returncode)
>
>
>  def print_results(results):
> diff --git a/pym/portage/emaint/modules/binhost/binhost.py
> b/pym/portage/emaint/modules/binhost/binhost.py
> index cf1213e..8cf3da6 100644
> --- a/pym/portage/emaint/modules/binhost/binhost.py
> +++ b/pym/portage/emaint/modules/binhost/binhost.py
> @@ -86,7 +86,9 @@ class 

Re: [gentoo-portage-dev] [PATCH] emaint: exit with non-zero status code when module fails (bug 567478)

2017-01-18 Thread Alexandru Elisei
 ["'%s' is not installed" % x for x in 
self.not_installed]
else:
errors.append(self.world_file + " could not be opened 
for reading")
-   return errors
+   if errors:
+   return (False, errors)
+   return (True, None)

def fix(self, **kwargs):
onProgress = kwargs.get('onProgress', None)
@@ -83,7 +85,9 @@ class WorldHandler(object):
except portage.exception.PortageException:
errors.append("%s could not be opened 
for writing" % \
self.world_file)
-   return errors
+   if errors:
+   return (False, errors)
+   return (True, None)
finally:
world_set.unlock()

diff --git a/pym/portage/tests/emerge/test_simple.py
b/pym/portage/tests/emerge/test_simple.py
index b1a2af5..5930f6c 100644
--- a/pym/portage/tests/emerge/test_simple.py
+++ b/pym/portage/tests/emerge/test_simple.py
@@ -382,6 +382,7 @@ pkg_preinst() {
"PORTAGE_PYTHON" : portage_python,
"PORTAGE_REPOSITORIES" : 
settings.repositories.config_string(),
"PORTAGE_TMPDIR" : portage_tmpdir,
+   "PORT_LOGDIR" : portage_tmpdir,
"PYTHONDONTWRITEBYTECODE" : 
os.environ.get("PYTHONDONTWRITEBYTECODE", ""),
"PYTHONPATH" : pythonpath,
"__PORTAGE_TEST_PATH_OVERRIDE" : fake_bin,
-- 
2.10.2

On 1/17/17, Alexandru Elisei <alexandru.eli...@gmail.com> wrote:
>>
>> Please move this sys.exit to the main().  So this should just return
>> a list of the rcodes to match the tasks it was passed.
>> TaskHandler can be used easily via api, so we don't want it to exit out
>> of someone else's code.  That will also keep main() as the primary cli
>> interface.  There process the list of rcodes and exit appropriately.
>
>
> This was an oversight on my part, I will make the necessary changes.
>
> Alexandru, I tend to not prefer using numbers to indicate success/fail.
>> They can be confusing at times.  In this case 1 means fail, 0 means
>> success.  Which is the opposite of True/False.  It would be different
>> if there were multiple return code values.  Then they would be
>> meaningful.
>
>
>  Sure, I understand your point, we are only interested in success of
> failure of the modules, so having True or False as return values makes
> sense. I got the idea to use 1 as a failure return code because that's how
> the portage sync modules work (for example:
> https://gitweb.gentoo.org/proj/portage.git/tree/pym/portage/sync/modules/git/git.py#n46).
> That's also why I chose to return (returncode, message) and not (message,
> returncode).
>
>
>
> On Tue, Jan 17, 2017 at 8:23 PM, Brian Dolbec <dol...@gentoo.org> wrote:
>
>> On Tue, 17 Jan 2017 19:48:16 +0200
>> Alexandru Elisei <alexandru.eli...@gmail.com> wrote:
>>
>> > Currently module functions return a message to emaint after
>> > invocation. Emaint prints this message then exits normally (with a
>> > success return code) even if the module encountered an error. This
>> > patch aims to change this by having each module public function
>> > return a tuple of (returncode, message). Emaint will inspect the
>> > return code and will exit unsuccessfully if necessary.
>> > ---
>> >  pym/portage/emaint/main.py| 11 +--
>> >  pym/portage/emaint/modules/binhost/binhost.py |  6 --
>> >  pym/portage/emaint/modules/config/config.py   |  8 ++--
>> >  pym/portage/emaint/modules/logs/logs.py   |  9 +
>> >  pym/portage/emaint/modules/merges/merges.py   | 18 +++---
>> >  pym/portage/emaint/modules/move/move.py   |  9 +++--
>> >  pym/portage/emaint/modules/resume/resume.py   |  4 +++-
>> >  pym/portage/emaint/modules/sync/sync.py   | 19
>> > +-- pym/portage/emaint/modules/world/world.py |
>> > 8 ++-- 9 files changed, 64 insertions(+), 28 deletions(-)
>> >
>> > diff --git a/pym/portage/emaint/main.py b/pym/portage/emaint/main.py
>> > index 65e3545..ef4383a 100644
>> > --- a/pym/portage/emaint/main.py
>> > +++ b/pym/portage/emaint/main.py
>> > @@ -115,6 +115,7 @@ class TaskHandler(object):
>> >   """Runs

Re: [gentoo-portage-dev] [PATCH] emaint: exit with non-zero status code when module fails (bug 567478)

2017-01-17 Thread Alexandru Elisei
>
> Please move this sys.exit to the main().  So this should just return
> a list of the rcodes to match the tasks it was passed.
> TaskHandler can be used easily via api, so we don't want it to exit out
> of someone else's code.  That will also keep main() as the primary cli
> interface.  There process the list of rcodes and exit appropriately.


This was an oversight on my part, I will make the necessary changes.

Alexandru, I tend to not prefer using numbers to indicate success/fail.
> They can be confusing at times.  In this case 1 means fail, 0 means
> success.  Which is the opposite of True/False.  It would be different
> if there were multiple return code values.  Then they would be
> meaningful.


 Sure, I understand your point, we are only interested in success of
failure of the modules, so having True or False as return values makes
sense. I got the idea to use 1 as a failure return code because that's how
the portage sync modules work (for example:
https://gitweb.gentoo.org/proj/portage.git/tree/pym/portage/sync/modules/git/git.py#n46).
That's also why I chose to return (returncode, message) and not (message,
returncode).



On Tue, Jan 17, 2017 at 8:23 PM, Brian Dolbec <dol...@gentoo.org> wrote:

> On Tue, 17 Jan 2017 19:48:16 +0200
> Alexandru Elisei <alexandru.eli...@gmail.com> wrote:
>
> > Currently module functions return a message to emaint after
> > invocation. Emaint prints this message then exits normally (with a
> > success return code) even if the module encountered an error. This
> > patch aims to change this by having each module public function
> > return a tuple of (returncode, message). Emaint will inspect the
> > return code and will exit unsuccessfully if necessary.
> > ---
> >  pym/portage/emaint/main.py| 11 +--
> >  pym/portage/emaint/modules/binhost/binhost.py |  6 --
> >  pym/portage/emaint/modules/config/config.py   |  8 ++--
> >  pym/portage/emaint/modules/logs/logs.py   |  9 +
> >  pym/portage/emaint/modules/merges/merges.py   | 18 +++---
> >  pym/portage/emaint/modules/move/move.py   |  9 +++--
> >  pym/portage/emaint/modules/resume/resume.py   |  4 +++-
> >  pym/portage/emaint/modules/sync/sync.py   | 19
> > +-- pym/portage/emaint/modules/world/world.py |
> > 8 ++-- 9 files changed, 64 insertions(+), 28 deletions(-)
> >
> > diff --git a/pym/portage/emaint/main.py b/pym/portage/emaint/main.py
> > index 65e3545..ef4383a 100644
> > --- a/pym/portage/emaint/main.py
> > +++ b/pym/portage/emaint/main.py
> > @@ -115,6 +115,7 @@ class TaskHandler(object):
> >   """Runs the module tasks"""
> >   if tasks is None or func is None:
> >   return
> > + returncode = os.EX_OK
> >   for task in tasks:
> >   inst = task()
> >   show_progress = self.show_progress_bar and
> > self.isatty @@ -135,14 +136,20 @@ class TaskHandler(object):
> >   # them for other tasks if there is
> > more to do. 'options': options.copy()
> >   }
> > - result = getattr(inst, func)(**kwargs)
> > + rcode, msgs = getattr(inst, func)(**kwargs)
> >   if show_progress:
> >   # make sure the final progress is
> > displayed self.progress_bar.display()
> >   print()
> >   self.progress_bar.stop()
> >   if self.callback:
> > - self.callback(result)
> > + self.callback(msgs)
> > + # Keep the last error code when using the
> > 'all' command.
> > + if rcode != os.EX_OK:
> > + returncode = rcode
> > +
> > + if returncode != os.EX_OK:
> > + sys.exit(returncode)
> >
>
>
> Please move this sys.exit to the main().  So this should just return
> a list of the rcodes to match the tasks it was passed.
> TaskHandler can be used easily via api, so we don't want it to exit out
> of someone else's code.  That will also keep main() as the primary cli
> interface.  There process the list of rcodes and exit appropriately.
>
>
>
> >
> >  def print_results(results):
> > diff --git a/pym/portage/emaint/modules/binhost/binhost.py
> > b/pym/portage/emaint/modules/binhost/binhost.py
> > index cf1213e..8cf3da6 1

[gentoo-portage-dev] [PATCH] emaint: exit with non-zero status code when module fails (bug 567478)

2017-01-17 Thread Alexandru Elisei
Currently module functions return a message to emaint after invocation.
Emaint prints this message then exits normally (with a success return
code) even if the module encountered an error. This patch aims to
change this by having each module public function return a tuple of
(returncode, message). Emaint will inspect the return code and will
exit unsuccessfully if necessary.
---
 pym/portage/emaint/main.py| 11 +--
 pym/portage/emaint/modules/binhost/binhost.py |  6 --
 pym/portage/emaint/modules/config/config.py   |  8 ++--
 pym/portage/emaint/modules/logs/logs.py   |  9 +
 pym/portage/emaint/modules/merges/merges.py   | 18 +++---
 pym/portage/emaint/modules/move/move.py   |  9 +++--
 pym/portage/emaint/modules/resume/resume.py   |  4 +++-
 pym/portage/emaint/modules/sync/sync.py   | 19 +--
 pym/portage/emaint/modules/world/world.py |  8 ++--
 9 files changed, 64 insertions(+), 28 deletions(-)

diff --git a/pym/portage/emaint/main.py b/pym/portage/emaint/main.py
index 65e3545..ef4383a 100644
--- a/pym/portage/emaint/main.py
+++ b/pym/portage/emaint/main.py
@@ -115,6 +115,7 @@ class TaskHandler(object):
"""Runs the module tasks"""
if tasks is None or func is None:
return
+   returncode = os.EX_OK
for task in tasks:
inst = task()
show_progress = self.show_progress_bar and self.isatty
@@ -135,14 +136,20 @@ class TaskHandler(object):
# them for other tasks if there is more to do.
'options': options.copy()
}
-   result = getattr(inst, func)(**kwargs)
+   rcode, msgs = getattr(inst, func)(**kwargs)
if show_progress:
# make sure the final progress is displayed
self.progress_bar.display()
print()
self.progress_bar.stop()
if self.callback:
-   self.callback(result)
+   self.callback(msgs)
+   # Keep the last error code when using the 'all' command.
+   if rcode != os.EX_OK:
+   returncode = rcode
+
+   if returncode != os.EX_OK:
+   sys.exit(returncode)


 def print_results(results):
diff --git a/pym/portage/emaint/modules/binhost/binhost.py
b/pym/portage/emaint/modules/binhost/binhost.py
index cf1213e..8cf3da6 100644
--- a/pym/portage/emaint/modules/binhost/binhost.py
+++ b/pym/portage/emaint/modules/binhost/binhost.py
@@ -86,7 +86,9 @@ class BinhostHandler(object):
stale = set(metadata).difference(cpv_all)
for cpv in stale:
errors.append("'%s' is not in the repository" % cpv)
-   return errors
+   if errors:
+   return (1, errors)
+   return (os.EX_OK, None)

def fix(self,  **kwargs):
onProgress = kwargs.get('onProgress', None)
@@ -177,4 +179,4 @@ class BinhostHandler(object):
if maxval == 0:
maxval = 1
onProgress(maxval, maxval)
-   return None
+   return (os.EX_OK, None)
diff --git a/pym/portage/emaint/modules/config/config.py
b/pym/portage/emaint/modules/config/config.py
index dad024b..a4a58d9 100644
--- a/pym/portage/emaint/modules/config/config.py
+++ b/pym/portage/emaint/modules/config/config.py
@@ -36,7 +36,10 @@ class CleanConfig(object):
if onProgress:
onProgress(maxval, i+1)
i += 1
-   return self._format_output(messages)
+   msgs = self._format_output(messages)
+   if msgs:
+   return (1, msgs)
+   return (os.EX_OK, None)

def fix(self, **kwargs):
onProgress = kwargs.get('onProgress', None)
@@ -65,7 +68,8 @@ class CleanConfig(object):
i += 1
if modified:
writedict(configs, self.target)
-   return self._format_output(messages, True)
+   msgs = self._format_output(messages, True)
+   return (os.EX_OK, msgs)

def _format_output(self, messages=[], cleaned=False):
output = []
diff --git a/pym/portage/emaint/modules/logs/logs.py
b/pym/portage/emaint/modules/logs/logs.py
index fe65cf5..8c638d7 100644
--- a/pym/portage/emaint/modules/logs/logs.py
+++ b/pym/portage/emaint/modules/logs/logs.py
@@ -40,7 +40,6 @@ class CleanLogs(object):
'NUM': int: number