Implement lazy initialization of global "portdb", "settings" and other
authorZac Medico <zmedico@gentoo.org>
Thu, 26 Jun 2008 05:28:04 +0000 (05:28 -0000)
committerZac Medico <zmedico@gentoo.org>
Thu, 26 Jun 2008 05:28:04 +0000 (05:28 -0000)
variables that pollute the portage module. This works by initializing
the global variables with dummy "proxy" objects that serve as a means
to trigger lazy initialization. As soon as the first attribute access
or method call occurs on one of the proxy objects, it causes all the
proxy objects to be replaced with the real ones.

It's possible for an unsupported attribute access or method call on a
proxy object to trigger an error, leading to breakage. However, hopefully
these such corner cases will negligible (only time will tell).

svn path=/main/trunk/; revision=10798

pym/portage/__init__.py
pym/portage/util.py

index cedbd2b8ed00235d9c83cc4b27560433834533dd..54faefad5e821abba8f2bb9fc4de5ba9c7f64176 100644 (file)
@@ -6907,6 +6907,20 @@ def create_trees(config_root=None, target_root=None, trees=None):
                        binarytree, myroot, mysettings["PKGDIR"], settings=mysettings)
        return trees
 
+class _LegacyGlobalProxy(portage.util.ObjectProxy):
+       """
+       Instances of these serve as proxies to global variables
+       that are initialized on demand.
+       """
+       def __init__(self, name):
+               portage.util.ObjectProxy.__init__(self)
+               object.__setattr__(self, '_name', name)
+
+       def _get_target(self):
+               init_legacy_globals()
+               name = object.__getattribute__(self, '_name')
+               return globals()[name]
+
 # Initialization of legacy globals.  No functions/classes below this point
 # please!  When the above functions and classes become independent of the
 # below global variables, it will be possible to make the below code
@@ -6915,7 +6929,14 @@ def create_trees(config_root=None, target_root=None, trees=None):
 # code that is aware of this flag to import portage without the unnecessary
 # overhead (and other issues!) of initializing the legacy globals.
 
+_globals_initialized = False
+
 def init_legacy_globals():
+       global _globals_initialized
+       if _globals_initialized:
+               return
+       _globals_initialized = True
+
        global db, settings, root, portdb, selinux_enabled, mtimedbfile, mtimedb, \
        archlist, features, groups, pkglines, thirdpartymirrors, usedefaults, \
        profiledir, flushmtimedb
@@ -6974,7 +6995,11 @@ def init_legacy_globals():
 # use within Portage.  External use of this variable is unsupported because
 # it is experimental and it's behavior is likely to change.
 if "PORTAGE_LEGACY_GLOBALS" not in os.environ:
-       init_legacy_globals()
+       for k in ("db", "settings", "root", "portdb", "selinux_enabled",
+               "mtimedbfile", "mtimedb", "archlist", "features", "groups",
+               "pkglines", "thirdpartymirrors", "usedefaults", "profiledir",
+               "flushmtimedb"):
+               globals()[k] = _LegacyGlobalProxy(k)
 
 # Clear the cache
 dircache={}
index 61ee3d88d375f140157e49b3f54bcc920873864c..821513ae67e01ee447f041766a5328a2c96db7d9 100644 (file)
@@ -912,6 +912,67 @@ def ensure_dirs(dir_path, *args, **kwargs):
        perms_modified = apply_permissions(dir_path, *args, **kwargs)
        return created_dir or perms_modified
 
+class ObjectProxy(object):
+
+       """
+       Object that acts as a proxy to another object, forwarding
+       attribute accesses and method calls. This can be useful
+       for implementing lazy initialization.
+       """
+
+       def _get_target(self):
+               raise NotImplementedError(self)
+
+       def __getattribute__(self, attr):
+               result = object.__getattribute__(self, '_get_target')()
+               return getattr(result, attr)
+
+       def __setattr__(self, attr, value):
+               result = object.__getattribute__(self, '_get_target')()
+               setattr(result, attr, value)
+
+       def __call__(self, *args, **kwargs):
+               result = object.__getattribute__(self, '_get_target')()
+               return result(*args, **kwargs)
+
+       def __setitem__(self, key, value):
+               object.__getattribute__(self, '_get_target')()[key] = value
+
+       def __getitem__(self, key):
+               return object.__getattribute__(self, '_get_target')()[key]
+
+       def __delitem__(self, key):
+               del object.__getattribute__(self, '_get_target')()[key]
+
+       def __contains__(self, key):
+               return key in object.__getattribute__(self, '_get_target')()
+
+       def __iter__(self):
+               return iter(object.__getattribute__(self, '_get_target')())
+
+       def __len__(self):
+               return len(object.__getattribute__(self, '_get_target')())
+
+       def __repr__(self):
+               return repr(object.__getattribute__(self, '_get_target')())
+
+       def __str__(self):
+               return str(object.__getattribute__(self, '_get_target')())
+
+       def __hash__(self):
+               return hash(object.__getattribute__(self, '_get_target')())
+
+       def __eq__(self, other):
+               return object.__getattribute__(self, '_get_target')() == other
+
+       def __ne__(self, other):
+               return object.__getattribute__(self, '_get_target')() != other
+
+       def __nonzero__(self):
+               if object.__getattribute__(self, '_get_target')():
+                       return True
+               return False
+
 class LazyItemsDict(dict):
        """A mapping object that behaves like a standard dict except that it allows
        for lazy initialization of values via callable objects.  Lazy items can be