emerge_main: hold large objects in emerge_config

This allows emerge_main to avoid having direct local references to
large local objects (like "settings" and "trees"), making it easier to
ensure that stale objects can be garbage collected when other functions
refresh the config with calls to load_emerge_config(). This will be
much more flexible than the "gc_locals" approach that was introduce in
commit e9fd283aedf54e2effc73f4157524fe9a26993c0.
diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py
index 1629d92..c7139d4 100644
--- a/pym/_emerge/actions.py
+++ b/pym/_emerge/actions.py
@@ -55,6 +55,7 @@
 from portage.util import cmp_sort_key, writemsg, varexpand, \
 	writemsg_level, writemsg_stdout
 from portage.util.digraph import digraph
+from portage.util.SlotObject import SlotObject
 from portage.util._async.run_main_scheduler import run_main_scheduler
 from portage.util._async.SchedulerInterface import SchedulerInterface
 from portage.util._eventloop.global_event_loop import global_event_loop
@@ -3128,25 +3129,49 @@
 
 	return os.EX_OK
 
-def load_emerge_config(trees=None):
+class _emerge_config(SlotObject):
+
+	__slots__ = ('action', 'args', 'mtimedb', 'opts', 'settings', 'trees')
+
+	# Support unpack as tuple, for load_emerge_config backward compatibility.
+	def __getitem__(self, index):
+		if index == 0:
+			return self.settings
+		elif index == 1:
+			return self.trees
+		elif index == 2:
+			return self.mtimedb
+		raise IndexError(index)
+
+	def __len__(self):
+		return 3
+
+def load_emerge_config(emerge_config=None, **kargs):
+
+	if emerge_config is None:
+		emerge_config = _emerge_config(**kargs)
+
 	kwargs = {}
 	for k, envvar in (("config_root", "PORTAGE_CONFIGROOT"), ("target_root", "ROOT")):
 		v = os.environ.get(envvar, None)
 		if v and v.strip():
 			kwargs[k] = v
-	trees = portage.create_trees(trees=trees, **portage._native_kwargs(kwargs))
+	emerge_config.trees = portage.create_trees(trees=emerge_config.trees,
+				**portage._native_kwargs(kwargs))
 
-	for root_trees in trees.values():
+	for root_trees in emerge_config.trees.values():
 		settings = root_trees["vartree"].settings
 		settings._init_dirs()
 		setconfig = load_default_config(settings, root_trees)
 		root_trees["root_config"] = RootConfig(settings, root_trees, setconfig)
 
-	settings = trees[trees._target_eroot]['vartree'].settings
-	mtimedbfile = os.path.join(settings['EROOT'], portage.CACHE_PATH, "mtimedb")
-	mtimedb = portage.MtimeDB(mtimedbfile)
-	QueryCommand._db = trees
-	return settings, trees, mtimedb
+	target_eroot = emerge_config.trees._target_eroot
+	emerge_config.settings = emerge_config.trees[target_eroot]['vartree'].settings
+	emerge_config.mtimedb = portage.MtimeDB(
+		os.path.join(target_eroot, portage.CACHE_PATH, "mtimedb"))
+	QueryCommand._db = emerge_config.trees
+
+	return emerge_config
 
 def getgccversion(chost):
 	"""
@@ -3506,14 +3531,14 @@
 
 	return bool(ignored_repos)
 
-def run_action(settings, trees, mtimedb, myaction, myopts, myfiles,
-	gc_locals=None):
+def run_action(emerge_config):
 
-	# The caller may have its local variables garbage collected, so
-	# they don't consume any memory during this long-running function.
-	if gc_locals is not None:
-		gc_locals()
-		gc_locals = None
+	myaction = emerge_config.action
+	myfiles = emerge_config.args
+	mtimedb = emerge_config.mtimedb
+	myopts = emerge_config.opts
+	settings = emerge_config.settings
+	trees = emerge_config.trees
 
 	# skip global updates prior to sync, since it's called after sync
 	if myaction not in ('help', 'info', 'sync', 'version') and \
@@ -3521,7 +3546,8 @@
 		_global_updates(trees, mtimedb["updates"], quiet=("--quiet" in myopts)):
 		mtimedb.commit()
 		# Reload the whole config from scratch.
-		settings, trees, mtimedb = load_emerge_config(trees=trees)
+		settings, trees, mtimedb = \
+			load_emerge_config(emerge_config=emerge_config)
 
 	xterm_titles = "notitles" not in settings.features
 	if xterm_titles:
@@ -3531,7 +3557,8 @@
 		os.environ["FEATURES"] = os.environ.get("FEATURES","") + " digest"
 		# Reload the whole config from scratch so that the portdbapi internal
 		# config is updated with new FEATURES.
-		settings, trees, mtimedb = load_emerge_config(trees=trees)
+		settings, trees, mtimedb = \
+			load_emerge_config(emerge_config=emerge_config)
 
 	# NOTE: adjust_configs() can map options to FEATURES, so any relevant
 	# options adjustments should be made prior to calling adjust_configs().
diff --git a/pym/_emerge/main.py b/pym/_emerge/main.py
index e066c87..4b9af7c 100644
--- a/pym/_emerge/main.py
+++ b/pym/_emerge/main.py
@@ -1041,17 +1041,18 @@
 	os.umask(0o22)
 	if myaction == "sync":
 		portage._sync_disabled_warnings = True
-	settings, trees, mtimedb = load_emerge_config()
-	rval = profile_check(trees, myaction)
+	emerge_config = load_emerge_config(
+		action=myaction, args=myfiles, opts=myopts)
+	rval = profile_check(emerge_config.trees, emerge_config.action)
 	if rval != os.EX_OK:
 		return rval
 
 	tmpcmdline = []
 	if "--ignore-default-opts" not in myopts:
 		tmpcmdline.extend(portage.util.shlex_split(
-			settings.get("EMERGE_DEFAULT_OPTS", "")))
+			emerge_config.settings.get("EMERGE_DEFAULT_OPTS", "")))
 	tmpcmdline.extend(args)
-	myaction, myopts, myfiles = parse_opts(tmpcmdline)
+	emerge_config.action, emerge_config.opts, emerge_config.args = \
+		parse_opts(tmpcmdline)
 
-	return run_action(settings, trees, mtimedb, myaction, myopts, myfiles,
-		gc_locals=locals().clear)
+	return run_action(emerge_config)