]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Adding flags.py to infrastructure code
authorSalvatore Orlando <salvatore.orlando@eu.citrix.com>
Fri, 20 May 2011 08:05:39 +0000 (09:05 +0100)
committerSalvatore Orlando <salvatore.orlando@eu.citrix.com>
Fri, 20 May 2011 08:05:39 +0000 (09:05 +0100)
quantum/common/flags.py [new file with mode: 0644]

diff --git a/quantum/common/flags.py b/quantum/common/flags.py
new file mode 100644 (file)
index 0000000..51cbe58
--- /dev/null
@@ -0,0 +1,247 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 Citrix Systems, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+"""Command-line flag library.
+
+Wraps gflags.
+Global flags should be defined here, the rest are defined where they're used.
+
+"""
+
+import getopt
+import string
+import sys
+
+import gflags
+
+
+class FlagValues(gflags.FlagValues):
+    """Extension of gflags.FlagValues that allows undefined and runtime flags.
+
+    Unknown flags will be ignored when parsing the command line, but the
+    command line will be kept so that it can be replayed if new flags are
+    defined after the initial parsing.
+
+    """
+
+    def __init__(self, extra_context=None):
+        gflags.FlagValues.__init__(self)
+        self.__dict__['__dirty'] = []
+        self.__dict__['__was_already_parsed'] = False
+        self.__dict__['__stored_argv'] = []
+        self.__dict__['__extra_context'] = extra_context
+
+    def __call__(self, argv):
+        # We're doing some hacky stuff here so that we don't have to copy
+        # out all the code of the original verbatim and then tweak a few lines.
+        # We're hijacking the output of getopt so we can still return the
+        # leftover args at the end
+        sneaky_unparsed_args = {"value": None}
+        original_argv = list(argv)
+
+        if self.IsGnuGetOpt():
+            orig_getopt = getattr(getopt, 'gnu_getopt')
+            orig_name = 'gnu_getopt'
+        else:
+            orig_getopt = getattr(getopt, 'getopt')
+            orig_name = 'getopt'
+
+        def _sneaky(*args, **kw):
+            optlist, unparsed_args = orig_getopt(*args, **kw)
+            sneaky_unparsed_args['value'] = unparsed_args
+            return optlist, unparsed_args
+
+        try:
+            setattr(getopt, orig_name, _sneaky)
+            args = gflags.FlagValues.__call__(self, argv)
+        except gflags.UnrecognizedFlagError:
+            # Undefined args were found, for now we don't care so just
+            # act like everything went well
+            # (these three lines are copied pretty much verbatim from the end
+            # of the __call__ function we are wrapping)
+            unparsed_args = sneaky_unparsed_args['value']
+            if unparsed_args:
+                if self.IsGnuGetOpt():
+                    args = argv[:1] + unparsed_args
+                else:
+                    args = argv[:1] + original_argv[-len(unparsed_args):]
+            else:
+                args = argv[:1]
+        finally:
+            setattr(getopt, orig_name, orig_getopt)
+
+        # Store the arguments for later, we'll need them for new flags
+        # added at runtime
+        self.__dict__['__stored_argv'] = original_argv
+        self.__dict__['__was_already_parsed'] = True
+        self.ClearDirty()
+        return args
+
+    def Reset(self):
+        gflags.FlagValues.Reset(self)
+        self.__dict__['__dirty'] = []
+        self.__dict__['__was_already_parsed'] = False
+        self.__dict__['__stored_argv'] = []
+
+    def SetDirty(self, name):
+        """Mark a flag as dirty so that accessing it will case a reparse."""
+        self.__dict__['__dirty'].append(name)
+
+    def IsDirty(self, name):
+        return name in self.__dict__['__dirty']
+
+    def ClearDirty(self):
+        self.__dict__['__is_dirty'] = []
+
+    def WasAlreadyParsed(self):
+        return self.__dict__['__was_already_parsed']
+
+    def ParseNewFlags(self):
+        if '__stored_argv' not in self.__dict__:
+            return
+        new_flags = FlagValues(self)
+        for k in self.__dict__['__dirty']:
+            new_flags[k] = gflags.FlagValues.__getitem__(self, k)
+
+        new_flags(self.__dict__['__stored_argv'])
+        for k in self.__dict__['__dirty']:
+            setattr(self, k, getattr(new_flags, k))
+        self.ClearDirty()
+
+    def __setitem__(self, name, flag):
+        gflags.FlagValues.__setitem__(self, name, flag)
+        if self.WasAlreadyParsed():
+            self.SetDirty(name)
+
+    def __getitem__(self, name):
+        if self.IsDirty(name):
+            self.ParseNewFlags()
+        return gflags.FlagValues.__getitem__(self, name)
+
+    def __getattr__(self, name):
+        if self.IsDirty(name):
+            self.ParseNewFlags()
+        val = gflags.FlagValues.__getattr__(self, name)
+        if type(val) is str:
+            tmpl = string.Template(val)
+            context = [self, self.__dict__['__extra_context']]
+            return tmpl.substitute(StrWrapper(context))
+        return val
+
+
+class StrWrapper(object):
+    """Wrapper around FlagValues objects.
+
+    Wraps FlagValues objects for string.Template so that we're
+    sure to return strings.
+
+    """
+    def __init__(self, context_objs):
+        self.context_objs = context_objs
+
+    def __getitem__(self, name):
+        for context in self.context_objs:
+            val = getattr(context, name, False)
+            if val:
+                return str(val)
+        raise KeyError(name)
+
+
+# Copied from gflags with small mods to get the naming correct.
+# Originally gflags checks for the first module that is not gflags that is
+# in the call chain, we want to check for the first module that is not gflags
+# and not this module.
+def _GetCallingModule():
+    """Returns the name of the module that's calling into this module.
+
+    We generally use this function to get the name of the module calling a
+    DEFINE_foo... function.
+
+    """
+    # Walk down the stack to find the first globals dict that's not ours.
+    for depth in range(1, sys.getrecursionlimit()):
+        if not sys._getframe(depth).f_globals is globals():
+            module_name = __GetModuleName(sys._getframe(depth).f_globals)
+            if module_name == 'gflags':
+                continue
+            if module_name is not None:
+                return module_name
+    raise AssertionError("No module was found")
+
+
+# Copied from gflags because it is a private function
+def __GetModuleName(globals_dict):
+    """Given a globals dict, returns the name of the module that defines it.
+
+    Args:
+    globals_dict: A dictionary that should correspond to an environment
+      providing the values of the globals.
+
+    Returns:
+    A string (the name of the module) or None (if the module could not
+    be identified.
+
+    """
+    for name, module in sys.modules.iteritems():
+        if getattr(module, '__dict__', None) is globals_dict:
+            if name == '__main__':
+                return sys.argv[0]
+            return name
+    return None
+
+
+def _wrapper(func):
+    def _wrapped(*args, **kw):
+        kw.setdefault('flag_values', FLAGS)
+        func(*args, **kw)
+    _wrapped.func_name = func.func_name
+    return _wrapped
+
+
+FLAGS = FlagValues()
+gflags.FLAGS = FLAGS
+gflags._GetCallingModule = _GetCallingModule
+
+
+DEFINE = _wrapper(gflags.DEFINE)
+DEFINE_string = _wrapper(gflags.DEFINE_string)
+DEFINE_integer = _wrapper(gflags.DEFINE_integer)
+DEFINE_bool = _wrapper(gflags.DEFINE_bool)
+DEFINE_boolean = _wrapper(gflags.DEFINE_boolean)
+DEFINE_float = _wrapper(gflags.DEFINE_float)
+DEFINE_enum = _wrapper(gflags.DEFINE_enum)
+DEFINE_list = _wrapper(gflags.DEFINE_list)
+DEFINE_spaceseplist = _wrapper(gflags.DEFINE_spaceseplist)
+DEFINE_multistring = _wrapper(gflags.DEFINE_multistring)
+DEFINE_multi_int = _wrapper(gflags.DEFINE_multi_int)
+DEFINE_flag = _wrapper(gflags.DEFINE_flag)
+HelpFlag = gflags.HelpFlag
+HelpshortFlag = gflags.HelpshortFlag
+HelpXMLFlag = gflags.HelpXMLFlag
+
+
+def DECLARE(name, module_string, flag_values=FLAGS):
+    if module_string not in sys.modules:
+        __import__(module_string, globals(), locals())
+    if name not in flag_values:
+        raise gflags.UnrecognizedFlag(
+                "%s not defined by %s" % (name, module_string))
+
+
+# __GLOBAL FLAGS ONLY__
+# Define any app-specific flags in their own files, docs at:
+# http://code.google.com/p/python-gflags/source/browse/trunk/gflags.py#a9