diff python-modules/paver/options.py @ 25:3c2151124cee

Converted pavement.py to manage.py and added a README.
author Atul Varma <varmaa@toolness.com>
date Mon, 29 Jun 2009 10:19:33 -0700
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/python-modules/paver/options.py	Mon Jun 29 10:19:33 2009 -0700
@@ -0,0 +1,171 @@
+class OptionsError(Exception):
+    pass
+
+class Bunch(dict):
+    """A dictionary that provides attribute-style access."""
+
+    def __repr__(self):
+        keys = self.keys()
+        keys.sort()
+        args = ', '.join(['%s=%r' % (key, self[key]) for key in keys])
+        return '%s(%s)' % (self.__class__.__name__, args)
+    
+    def __getitem__(self, key):
+        item = dict.__getitem__(self, key)
+        if callable(item):
+            return item()
+        return item
+
+    def __getattr__(self, name):
+        try:
+            return self[name]
+        except KeyError:
+            raise AttributeError(name)
+
+    __setattr__ = dict.__setitem__
+
+    def __delattr__(self, name):
+        try:
+            del self[name]
+        except KeyError:
+            raise AttributeError(name)
+
+class Namespace(Bunch):
+    """A Bunch that will search dictionaries contained within to find a value.
+    The search order is set via the order() method. See the order method for
+    more information about search order.
+    """
+    def __init__(self, d=None, **kw):
+        self._sections = []
+        self._ordering = None
+        self.update(d, **kw)
+    
+    def order(self, *keys, **kw):
+        """Set the search order for this namespace. The arguments
+        should be the list of keys in the order you wish to search,
+        or a dictionary/Bunch that you want to search.
+        Keys that are left out will not be searched. If you pass in
+        no arguments, then the default ordering will be used. (The default
+        is to search the global space first, then in the order in
+        which the sections were created.)
+        
+        If you pass in a key name that is not a section, that
+        key will be silently removed from the list.
+        
+        Keyword arguments are:
+        
+        add_rest=False
+            put the sections you list at the front of the search
+            and add the remaining sections to the end
+        """
+        if not keys:
+            self._ordering = None
+            return
+        
+        order = []
+        for item in keys:
+            if isinstance(item, dict) or item in self._sections:
+                order.append(item)
+        
+        if kw.get('add_rest'):
+            # this is not efficient. do we care? probably not.
+            for item in self._sections:
+                if item not in order:
+                    order.append(item)
+        self._ordering = order
+        
+    def clear(self):
+        self._ordering = None
+        self._sections = []
+        super(Namespace, self).clear()
+    
+    def setdotted(self, key, value):
+        """Sets a namespace key, value pair where the key
+        can use dotted notation to set sub-values. For example,
+        the key "foo.bar" will set the "bar" value in the "foo"
+        Bunch in this Namespace. If foo does not exist, it is created
+        as a Bunch. If foo is a value, an OptionsError will be
+        raised."""
+        segments = key.split(".")
+        obj = self
+        segment = segments.pop(0)
+        while segments:
+            if segment not in obj:
+                obj[segment] = Bunch()
+            obj = obj[segment]
+            if not isinstance(obj, dict):
+                raise OptionsError("In setting option '%s', %s was already a value"
+                                   % (key, segment))
+            segment = segments.pop(0)
+        obj[segment] = value
+    
+    def __setitem__(self, key, value):
+        if isinstance(value, dict):
+            self._sections.insert(0, key)
+        super(Namespace, self).__setitem__(key, value)
+    
+    def get(self, key, default=None):
+        try:
+            return self[key]
+        except KeyError:
+            return default
+    
+    def __getitem__(self, key):
+        order = self._ordering
+        if order is None:
+            order = self._sections
+        try:
+            return super(Namespace, self).__getitem__(key)
+        except KeyError:
+            pass
+        for section in order:
+            if isinstance(section, dict):
+                try:
+                    return section[key]
+                except KeyError:
+                    pass
+            else:
+                try:
+                    return self[section][key]
+                except KeyError:
+                    pass
+        raise KeyError("Key %s not found in namespace" % key)
+    
+    def __setattr__(self, key, value):
+        if key.startswith("_"):
+            object.__setattr__(self, key, value)
+        else:
+            self[key] = value
+    
+    def __delitem__(self, key):
+        try:
+            index = self._sections.index(key)
+            del self._sections[index]
+        except ValueError:
+            pass
+        super(Namespace, self).__delitem__(key)
+    
+    def update(self, d=None, **kw):
+        """Update the namespace. This is less efficient than the standard 
+        dict.update but is necessary to keep track of the sections that we'll be
+        searching."""
+        items = []
+        if d:
+            # look up keys even though we call items
+            # because that's what the dict.update
+            # doc says
+            if hasattr(d, 'keys'):
+                items.extend(list(d.items()))
+            else:
+                items.extend(list(d))
+        items.extend(list(kw.items()))
+        for key, value in items:
+            self[key] = value
+    
+    __call__ = update
+    
+    def setdefault(self, key, default):
+        if not key in self:
+            self[key] = default
+            return default
+        return self[key]