aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin H. Johnson <robbat2@gentoo.org>2010-01-26 11:52:02 +0000
committerRobin H. Johnson <robbat2@gentoo.org>2010-01-26 11:52:02 +0000
commita1ac49f3f18e6086f87f7c963c9ee3d621439c48 (patch)
tree28eb9b63348a18bf6cec78db0527c3caf5e50cb6
parentferringb found a workaround. (diff)
downloadpackages-3-a1ac49f3f18e6086f87f7c963c9ee3d621439c48.tar.gz
packages-3-a1ac49f3f18e6086f87f7c963c9ee3d621439c48.tar.bz2
packages-3-a1ac49f3f18e6086f87f7c963c9ee3d621439c48.zip
Use system snakeoil now.
-rw-r--r--snakeoil/__init__.py4
-rw-r--r--snakeoil/caching.py86
-rw-r--r--snakeoil/compatibility.py32
-rw-r--r--snakeoil/containers.py207
-rw-r--r--snakeoil/currying.py129
-rwxr-xr-xsnakeoil/debug_imports.py99
-rw-r--r--snakeoil/demandload.py226
-rw-r--r--snakeoil/dependant_methods.py86
-rw-r--r--snakeoil/descriptors.py28
-rw-r--r--snakeoil/fileutils.py284
-rw-r--r--snakeoil/fix_copy.py74
-rw-r--r--snakeoil/formatters.py495
-rw-r--r--snakeoil/iterables.py202
-rw-r--r--snakeoil/klass.py95
-rw-r--r--snakeoil/lists.py171
-rw-r--r--snakeoil/mappings.py579
-rw-r--r--snakeoil/modules.py53
-rw-r--r--snakeoil/obj.py206
-rw-r--r--snakeoil/osutils/__init__.py340
-rw-r--r--snakeoil/osutils/native_readdir.py60
-rw-r--r--snakeoil/pickling.py18
-rw-r--r--snakeoil/tar.py35
-rw-r--r--snakeoil/version.py39
-rw-r--r--snakeoil/weakrefs.py12
-rw-r--r--snakeoil/xml/__init__.py46
-rw-r--r--snakeoil/xml/bundled_elementtree.py1254
26 files changed, 0 insertions, 4860 deletions
diff --git a/snakeoil/__init__.py b/snakeoil/__init__.py
deleted file mode 100644
index 9ff5a09..0000000
--- a/snakeoil/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-# Copyright: 2005 Brian Harring <ferringb@gmail.com>
-# License: GPL2
-
-"""misc. utility functions"""
diff --git a/snakeoil/caching.py b/snakeoil/caching.py
deleted file mode 100644
index daced2b..0000000
--- a/snakeoil/caching.py
+++ /dev/null
@@ -1,86 +0,0 @@
-# Copyright: 2006 Brian Harring <ferringb@gmail.com>
-# License: GPL2
-
-"""
-instance caching metaclass
-"""
-
-from snakeoil.demandload import demandload
-demandload(
- globals(),
- 'warnings',
- 'weakref:WeakValueDictionary',
- )
-
-class native_WeakInstMeta(type):
- """metaclass for instance caching, resulting in reuse of unique instances
-
- few notes-
- - instances must be immutable (or effectively so).
- Since creating a new instance may return a preexisting instance,
- this requirement B{must} be honored.
- - due to the potential for mishap, each subclass of a caching class must
- assign __inst_caching__ = True to enable caching for the derivative.
- - conversely, __inst_caching__ = False does nothing
- (although it's useful as a sign of
- I{do not enable caching for this class}
- - instance caching can be disabled per instantiation via passing
- disabling_inst_caching=True into the class constructor.
-
- Being a metaclass, the voodoo used doesn't require modification of
- the class itself.
-
- Examples of usage is the restrictions subsystem for
- U{pkgcore project<http://pkgcore.org>}
- """
- def __new__(cls, name, bases, d):
- if d.get("__inst_caching__", False):
- d["__inst_caching__"] = True
- d["__inst_dict__"] = WeakValueDictionary()
- else:
- d["__inst_caching__"] = False
- slots = d.get('__slots__')
- if slots is not None:
- for base in bases:
- if getattr(base, '__weakref__', False):
- break
- else:
- d['__slots__'] = tuple(slots) + ('__weakref__',)
- return type.__new__(cls, name, bases, d)
-
- def __call__(cls, *a, **kw):
- """disable caching via disable_inst_caching=True"""
- if cls.__inst_caching__ and not kw.pop("disable_inst_caching", False):
- kwlist = kw.items()
- kwlist.sort()
- key = (a, tuple(kwlist))
- try:
- instance = cls.__inst_dict__.get(key)
- except (NotImplementedError, TypeError), t:
- warnings.warn(
- "caching keys for %s, got %s for a=%s, kw=%s" % (
- cls, t, a, kw))
- del t
- key = instance = None
-
- if instance is None:
- instance = super(native_WeakInstMeta, cls).__call__(*a, **kw)
-
- if key is not None:
- cls.__inst_dict__[key] = instance
- else:
- instance = super(native_WeakInstMeta, cls).__call__(*a, **kw)
-
- return instance
-
-# "Invalid name"
-# pylint: disable-msg=C0103
-
-try:
- # No name in module
- # pylint: disable-msg=E0611
- from snakeoil._caching import WeakInstMeta
- cpy_WeakInstMeta = WeakInstMeta
-except ImportError:
- cpy_WeakInstMeta = None
- WeakInstMeta = native_WeakInstMeta
diff --git a/snakeoil/compatibility.py b/snakeoil/compatibility.py
deleted file mode 100644
index fbdbb87..0000000
--- a/snakeoil/compatibility.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright: 2006 Brian Harring <ferringb@gmail.com>
-# License: GPL2
-
-"""
-Compatibility module providing native reimplementations of python2.5 functionality.
-
-Uses the native implementation from C{__builtins__} if available.
-"""
-
-def native_any(iterable):
- for x in iterable:
- if x:
- return True
- return False
-
-def native_all(iterable):
- for x in iterable:
- if not x:
- return False
- return True
-
-# using variable before assignment
-# pylint: disable-msg=E0601
-
-if "any" in __builtins__:
- any = any
- all = all
-else:
- try:
- from snakeoil._compatibility import any, all
- except ImportError:
- any, all = native_any, native_all
diff --git a/snakeoil/containers.py b/snakeoil/containers.py
deleted file mode 100644
index 4ec3f2b..0000000
--- a/snakeoil/containers.py
+++ /dev/null
@@ -1,207 +0,0 @@
-# Copyright: 2005-2007 Brian Harring <ferringb@gmail.com>
-# License: GPL2
-
-"""
-collection of container classes
-"""
-
-from snakeoil.demandload import demandload
-demandload(
- globals(),
- 'itertools:chain,ifilterfalse',
-)
-
-class InvertedContains(set):
-
- """Set that inverts all contains lookup results.
-
- Mainly useful in conjuection with LimitedChangeSet for converting
- from blacklist to whitelist.
-
- Cannot be iterated over.
- """
-
- def __contains__(self, key):
- return not set.__contains__(self, key)
-
- def __iter__(self):
- # infinite set, non iterable.
- raise TypeError("InvertedContains cannot be iterated over")
-
-
-class SetMixin(object):
- """
- A mixin providing set methods.
-
- Subclasses should provide __init__, __iter__ and __contains__.
- """
-
- def __and__(self, other, kls=None):
- # Note: for these methods we don't bother to filter dupes from this
- # list - since the subclasses __init__ should already handle this,
- # there's no point doing it twice.
- return (kls or self.__class__)(x for x in self if x in other)
-
- def __rand__(self, other):
- return self.__and__(other, kls=other.__class__)
-
- def __or__(self, other, kls=None):
- return (kls or self.__class__)(chain(self, other))
-
- def __ror__(self, other):
- return self.__or__(other, kls=other.__class__)
-
- def __xor__(self, other, kls=None):
- return (kls or self.__class__)(chain((x for x in self if x not in other),
- (x for x in other if x not in self)))
-
- def __rxor__(self, other):
- return self.__xor__(other, kls=other.__class__)
-
- def __sub__(self, other):
- return self.__class__(x for x in self if x not in other)
-
- def __rsub__(self, other):
- return other.__class__(x for x in other if x not in self)
-
- __add__ = __or__
- __radd__ = __ror__
-
-
-class LimitedChangeSet(SetMixin):
-
- """Set used to limit the number of times a key can be removed/added.
-
- specifically deleting/adding a key only once per commit,
- optionally blocking changes to certain keys.
- """
-
- _removed = 0
- _added = 1
-
- def __init__(self, initial_keys, unchangable_keys=None):
- self._new = set(initial_keys)
- if unchangable_keys is None:
- self._blacklist = []
- else:
- if isinstance(unchangable_keys, (list, tuple)):
- unchangable_keys = set(unchangable_keys)
- self._blacklist = unchangable_keys
- self._changed = set()
- self._change_order = []
- self._orig = frozenset(self._new)
-
- def add(self, key):
- if key in self._changed or key in self._blacklist:
- # it's been del'd already once upon a time.
- if key in self._new:
- return
- raise Unchangable(key)
-
- self._new.add(key)
- self._changed.add(key)
- self._change_order.append((self._added, key))
-
- def remove(self, key):
- if key in self._changed or key in self._blacklist:
- if key not in self._new:
- raise KeyError(key)
- raise Unchangable(key)
-
- if key in self._new:
- self._new.remove(key)
- self._changed.add(key)
- self._change_order.append((self._removed, key))
-
- def __contains__(self, key):
- return key in self._new
-
- def changes_count(self):
- return len(self._change_order)
-
- def commit(self):
- self._orig = frozenset(self._new)
- self._changed.clear()
- self._change_order = []
-
- def rollback(self, point=0):
- l = self.changes_count()
- if point < 0 or point > l:
- raise TypeError(
- "%s point must be >=0 and <= changes_count()" % point)
- while l > point:
- change, key = self._change_order.pop(-1)
- self._changed.remove(key)
- if change == self._removed:
- self._new.add(key)
- else:
- self._new.remove(key)
- l -= 1
-
- def __str__(self):
- return str(self._new).replace("set(", "LimitedChangeSet(", 1)
-
- def __iter__(self):
- return iter(self._new)
-
- def __len__(self):
- return len(self._new)
-
- def __eq__(self, other):
- if isinstance(other, LimitedChangeSet):
- return self._new == other._new
- elif isinstance(other, (frozenset, set)):
- return self._new == other
- return False
-
- def __ne__(self, other):
- return not (self == other)
-
-
-class Unchangable(Exception):
-
- def __init__(self, key):
- Exception.__init__(self, "key '%s' is unchangable" % (key,))
- self.key = key
-
-
-class ProtectedSet(SetMixin):
-
- """
- Wraps a set pushing all changes into a secondary set.
- """
- def __init__(self, orig_set):
- self._orig = orig_set
- self._new = set()
-
- def __contains__(self, key):
- return key in self._orig or key in self._new
-
- def __iter__(self):
- return chain(iter(self._new),
- ifilterfalse(self._new.__contains__, self._orig))
-
- def __len__(self):
- return len(self._orig.union(self._new))
-
- def add(self, key):
- if key not in self._orig:
- self._new.add(key)
-
-
-class RefCountingSet(dict):
-
- def __init__(self, iterable=None):
- if iterable is not None:
- dict.__init__(self, ((x, 1) for x in iterable))
-
- def add(self, item):
- count = self.get(item, 0)
- self[item] = count + 1
-
- def remove(self, item):
- count = self[item]
- if count == 1:
- del self[item]
- else:
- self[item] = count - 1
diff --git a/snakeoil/currying.py b/snakeoil/currying.py
deleted file mode 100644
index 33be84f..0000000
--- a/snakeoil/currying.py
+++ /dev/null
@@ -1,129 +0,0 @@
-# Copyright: 2005 Brian Harring <ferringb@gmail.com>
-# License: GPL2
-
-"""
-Function currying, generating a functor with a set of args/defaults pre bound.
-
-L{pre_curry} and L{post_curry} return "normal" python functions.
-L{partial} returns a callable object. The difference between
-L{pre_curry} and L{partial} is this::
-
- >>> def func(arg=None, self=None):
- ... return arg, self
- >>> curry = pre_curry(func, True)
- >>> part = partial(func, True)
- >>> class Test(object):
- ... curry = pre_curry(func, True)
- ... part = partial(func, True)
- ... def __repr__(self):
- ... return '<Test object>'
- >>> curry()
- (True, None)
- >>> Test().curry()
- (True, <Test object>)
- >>> part()
- (True, None)
- >>> Test().part()
- (True, None)
-
-If your curried function is not used as a class attribute the results
-should be identical. Because L{partial} has an implementation in c
-while L{pre_curry} is python you should use L{partial} if possible.
-"""
-
-from operator import attrgetter
-
-__all__ = ("pre_curry", "partial", "post_curry", "pretty_docs",
- "alias_class_method")
-
-def pre_curry(func, *args, **kwargs):
- """passed in args are prefixed, with further args appended"""
-
- if not kwargs:
- def callit(*moreargs, **morekwargs):
- return func(*(args + moreargs), **morekwargs)
- elif not args:
- def callit(*moreargs, **morekwargs):
- kw = kwargs.copy()
- kw.update(morekwargs)
- return func(*moreargs, **kw)
- else:
- def callit(*moreargs, **morekwargs):
- kw = kwargs.copy()
- kw.update(morekwargs)
- return func(*(args+moreargs), **kw)
-
- callit.func = func
- return callit
-
-
-class native_partial(object):
-
- """Like pre_curry, but does not get turned into an instance method."""
-
- def __init__(self, func, *args, **kwargs):
- self.func = func
- self.args = args
- self.kwargs = kwargs
-
- def __call__(self, *moreargs, **morekwargs):
- kw = self.kwargs.copy()
- kw.update(morekwargs)
- return self.func(*(self.args + moreargs), **kw)
-
-# Unused import, unable to import
-# pylint: disable-msg=W0611,F0401
-try:
- from functools import partial
-except ImportError:
- try:
- from snakeoil._compatibility import partial
- except ImportError:
- partial = native_partial
-
-
-def post_curry(func, *args, **kwargs):
- """passed in args are appended to any further args supplied"""
-
- if not kwargs:
- def callit(*moreargs, **morekwargs):
- return func(*(moreargs+args), **morekwargs)
- elif not args:
- def callit(*moreargs, **morekwargs):
- kw = morekwargs.copy()
- kw.update(kwargs)
- return func(*moreargs, **kw)
- else:
- def callit(*moreargs, **morekwargs):
- kw = morekwargs.copy()
- kw.update(kwargs)
- return func(*(moreargs+args), **kw)
-
- callit.func = func
- return callit
-
-def pretty_docs(wrapped, extradocs=None):
- wrapped.__module__ = wrapped.func.__module__
- doc = wrapped.func.__doc__
- if extradocs is None:
- wrapped.__doc__ = doc
- else:
- wrapped.__doc__ = extradocs
- return wrapped
-
-
-def alias_class_method(attr):
- """at runtime, redirect to another method
-
- attr is the desired attr name to lookup, and supply all later passed in
- args/kws to
-
- Useful for when setting has_key to __contains__ for example, and
- __contains__ may be overriden.
- """
- grab_attr = attrgetter(attr)
-
- def _asecond_level_call(self, *a, **kw):
- return grab_attr(self)(*a, **kw)
-
- return _asecond_level_call
diff --git a/snakeoil/debug_imports.py b/snakeoil/debug_imports.py
deleted file mode 100755
index dc137ed..0000000
--- a/snakeoil/debug_imports.py
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/usr/bin/python
-# Copyright: 2007 Brian Harring <ferringb@gmail.com>
-# License: GPL2
-
-import __builtin__
-
-class intercept_import(object):
-
- def __init__(self, callback):
- self.callback = callback
- self.stack = []
- self.seen = set()
-
- def __call__(self, *args):
- if args[0] not in self.seen:
- self.disable()
- self.callback(self.stack, args)
- self.enable()
- self.stack.append(args[0])
- self.seen.add(args[0])
- try:
- return self.orig_import(*args)
- finally:
- self.stack.pop()
-
- def enable(self):
- cur_import = __builtin__.__import__
- if isinstance(cur_import, intercept_import):
- raise RuntimeError("an intercept is already active")
- self.orig_import = cur_import
- __builtin__.__import__ = self
-
- def disable(self):
- if __builtin__.__import__ != self:
- raise RuntimeError("either not active, or a different intercept "
- "is in use")
- __builtin__.__import__ = self.orig_import
- del self.orig_import
-
-
-if __name__ == "__main__":
- import __main__
- orig = dict(__main__.__dict__.iteritems())
- del orig["intercept_import"]
- del orig["__builtin__"]
- del orig["__main__"]
-
- import sys, imp
-
- usage = "debug_imports.py [-o output_file_path || -i] scriptfile [arg] ..."
- if not sys.argv[1:]:
- print "Usage: ", usage
- sys.exit(2)
-
- # yes, at first thought, this should use getopt or optparse.
- # problem is, folks may want to spot that import, thus we can't.
-
- import traceback, pdb
-
- args = sys.argv[1:]
- if args[0] == '-o':
- if not len(args) > 2:
- print "Usage: ", usage
- sys.exit(2)
- f = open(args[1], 'w')
- def callback(modules, key, val):
- f.write("adding %s\n" % key)
- traceback.print_stack(file=f)
- args = args[2:]
- elif args[0] == '-i':
- def callback(args):
- pdb.set_trace()
- args = args[1:]
- else:
- import time
- def callback(stack, args):
- if stack:
- print "in: %s" % ', '.join(stack)
- if len(args) == 4 and args[3] is not None:
- print "from %s import %s" % (args[0], ', '.join(args[3]))
- else:
- print "import %s " % args[0]
- print time.time()
-# traceback.print_stack(file=sys.stdout)
- print
-
-
- path = args[0]
-
- sys.argv = args[:]
- i = intercept_import(callback)
- i.enable()
- print "starting\n",time.time(),"\n"
- try:
- imp.load_module("__main__", open(args[0]), args[0], ("", "r", imp.PY_SOURCE))
- finally:
- i.disable()
- print "\nfinished\n",time.time(),"\n"
- sys.exit(0)
diff --git a/snakeoil/demandload.py b/snakeoil/demandload.py
deleted file mode 100644
index a515b90..0000000
--- a/snakeoil/demandload.py
+++ /dev/null
@@ -1,226 +0,0 @@
-# Copyright: 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
-# Copyright: 2007 Marien Zwart <marienz@gentoo.org>
-# License: GPL2
-
-"""Demand load things when used.
-
-This uses L{Placeholder} objects which create an actual object on
-first use and know how to replace themselves with that object, so
-there is no performance penalty after first use.
-
-This trick is *mostly* transparent, but there are a few things you
-have to be careful with:
-
- - You may not bind a second name to a placeholder object. Specifically,
- if you demandload C{bar} in module C{foo}, you may not
- C{from foo import bar} in a third module. The placeholder object
- does not "know" it gets imported, so this does not trigger the
- demandload: C{bar} in the third module is the placeholder object.
- When that placeholder gets used it replaces itself with the actual
- module in C{foo} but not in the third module.
- Because this is normally unwanted (it introduces a small
- performance hit) the placeholder object will raise an exception if
- it detects this. But if the demandload gets triggered before the
- third module is imported you do not get that exception, so you
- have to be careful not to import or otherwise pass around the
- placeholder object without triggering it.
- - Not all operations on the placeholder object trigger demandload.
- The most common problem is that C{except ExceptionClass} does not
- work if C{ExceptionClass} is a placeholder.
- C{except module.ExceptionClass} with C{module} a placeholder does
- work. You can normally avoid this by always demandloading the
- module, not something in it.
-"""
-
-# TODO: the use of a curried func instead of subclassing needs more thought.
-
-# the replace_func used by Placeholder is currently passed in as an
-# external callable, with "partial" used to provide arguments to it.
-# This works, but has the disadvantage that calling
-# demand_compile_regexp needs to import re (to hand re.compile to
-# partial). One way to avoid that would be to add a wrapper function
-# that delays the import (well, triggers the demandload) at the time
-# the regexp is used, but that's a bit convoluted. A different way is
-# to make replace_func a method of Placeholder implemented through
-# subclassing instead of a callable passed to its __init__. The
-# current version does not do this because getting/setting attributes
-# of Placeholder is annoying because of the
-# __getattribute__/__setattr__ override.
-
-
-from snakeoil.modules import load_any
-from snakeoil.currying import partial
-
-# There are some demandloaded imports below the definition of demandload.
-
-_allowed_chars = "".join((x.isalnum() or x in "_.") and " " or "a"
- for x in map(chr, xrange(256)))
-
-def parse_imports(imports):
- """Parse a sequence of strings describing imports.
-
- For every input string it returns a tuple of (import, targetname).
- Examples::
-
- 'foo' -> ('foo', 'foo')
- 'foo:bar' -> ('foo.bar', 'bar')
- 'foo:bar,baz@spork' -> ('foo.bar', 'bar'), ('foo.baz', 'spork')
- 'foo@bar' -> ('foo', 'bar')
-
- Notice 'foo.bar' is not a valid input. This simplifies the code,
- but if it is desired it can be added back.
-
- @type imports: sequence of C{str} objects.
- @rtype: iterable of tuples of two C{str} objects.
- """
- for s in imports:
- fromlist = s.split(':', 1)
- if len(fromlist) == 1:
- # Not a "from" import.
- if '.' in s:
- raise ValueError('dotted imports unsupported.')
- split = s.split('@', 1)
- for s in split:
- if not s.translate(_allowed_chars).isspace():
- raise ValueError("bad target: %s" % s)
- if len(split) == 2:
- yield tuple(split)
- else:
- yield split[0], split[0]
- else:
- # "from" import.
- base, targets = fromlist
- if not base.translate(_allowed_chars).isspace():
- raise ValueError("bad target: %s" % base)
- for target in targets.split(','):
- split = target.split('@', 1)
- for s in split:
- if not s.translate(_allowed_chars).isspace():
- raise ValueError("bad target: %s" % s)
- yield base + '.' + split[0], split[-1]
-
-
-class Placeholder(object):
-
- """Object that knows how to replace itself when first accessed.
-
- See the module docstring for common problems with its use.
- """
-
- def __init__(self, scope, name, replace_func):
- """Initialize.
-
- @param scope: the scope we live in, normally the result of
- C{globals()}.
- @param name: the name we have in C{scope}.
- @param replace_func: callable returning the object to replace us with.
- """
- object.__setattr__(self, '_scope', scope)
- object.__setattr__(self, '_name', name)
- object.__setattr__(self, '_replace_func', replace_func)
-
- def _already_replaced(self):
- name = object.__getattribute__(self, '_name')
- raise ValueError('Placeholder for %r was triggered twice' % (name,))
-
- def _replace(self):
- """Replace ourself in C{scope} with the result of our C{replace_func}.
-
- @returns: the result of calling C{replace_func}.
- """
- replace_func = object.__getattribute__(self, '_replace_func')
- scope = object.__getattribute__(self, '_scope')
- name = object.__getattribute__(self, '_name')
- # Paranoia, explained in the module docstring.
- already_replaced = object.__getattribute__(self, '_already_replaced')
- object.__setattr__(self, '_replace_func', already_replaced)
-
- # Cleanup, possibly unnecessary.
- object.__setattr__(self, '_scope', None)
-
- result = replace_func()
- scope[name] = result
- return result
-
- # Various methods proxied to our replacement.
-
- def __str__(self):
- return self.__getattribute__('__str__')()
-
- def __getattribute__(self, attr):
- result = object.__getattribute__(self, '_replace')()
- return getattr(result, attr)
-
- def __setattr__(self, attr, value):
- result = object.__getattribute__(self, '_replace')()
- setattr(result, attr, value)
-
- def __call__(self, *args, **kwargs):
- result = object.__getattribute__(self, '_replace')()
- return result(*args, **kwargs)
-
-
-def demandload(scope, *imports):
- """Import modules into scope when each is first used.
-
- scope should be the value of C{globals()} in the module calling
- this function. (using C{locals()} may work but is not recommended
- since mutating that is not safe).
-
- Other args are strings listing module names.
- names are handled like this::
-
- foo import foo
- foo@bar import foo as bar
- foo:bar from foo import bar
- foo:bar,quux from foo import bar, quux
- foo.bar:quux from foo.bar import quux
- foo:baz@quux from foo import baz as quux
- """
- for source, target in parse_imports(imports):
- scope[target] = Placeholder(scope, target, partial(load_any, source))
-
-
-demandload(globals(), 're')
-
-# Extra name to make undoing monkeypatching demandload with
-# disabled_demandload easier.
-enabled_demandload = demandload
-
-
-def disabled_demandload(scope, *imports):
- """Exactly like L{demandload} but does all imports immediately."""
- for source, target in parse_imports(imports):
- scope[target] = load_any(source)
-
-
-class RegexPlaceholder(Placeholder):
- """
- Compiled Regex object that knows how to replace itself when first accessed.
-
- See the module docstring for common problems with its use; used by
- L{demand_compile_regexp}.
- """
-
- def _replace(self):
- args, kwargs = object.__getattribute__(self, '_replace_func')
- object.__setattr__(self, '_replace_func',
- partial(re.compile, *args, **kwargs))
- return Placeholder._replace(self)
-
-
-
-def demand_compile_regexp(scope, name, *args, **kwargs):
- """Demandloaded version of L{re.compile}.
-
- Extra arguments are passed unchanged to L{re.compile}.
-
- This returns the placeholder, which you *must* bind to C{name} in
- the scope you pass as C{scope}. It is done this way to prevent
- confusing code analysis tools like pylint.
-
- @param scope: the scope, just like for L{demandload}.
- @param name: the name of the compiled re object in that scope.
- @returns: the placeholder object.
- """
- return RegexPlaceholder(scope, name, (args, kwargs))
diff --git a/snakeoil/dependant_methods.py b/snakeoil/dependant_methods.py
deleted file mode 100644
index b7597b5..0000000
--- a/snakeoil/dependant_methods.py
+++ /dev/null
@@ -1,86 +0,0 @@
-# Copyright: 2005-2006 Brian Harring <ferringb@gmail.com>
-# License: GPL2
-
-"""Metaclass to inject dependencies into method calls.
-
-Essentially, method a must be run prior to method b, invoking method a
-if b is called first.
-"""
-
-from snakeoil.lists import iflatten_instance
-from snakeoil.currying import partial
-
-__all__ = ["ForcedDepends"]
-
-def ensure_deps(self, name, *a, **kw):
- ignore_deps = "ignore_deps" in kw
- if ignore_deps:
- del kw["ignore_deps"]
- s = [name]
- else:
- s = yield_deps(self, self.stage_depends, name)
-
- r = True
- for dep in s:
- if dep not in self._stage_state:
- r = getattr(self, dep).raw_func(*a, **kw)
- if r:
- self._stage_state.add(dep)
- else:
- return r
- return r
-
-def yield_deps(inst, d, k):
- # While at first glance this looks like should use expandable_chain,
- # it shouldn't. --charlie
- if k not in d:
- yield k
- return
- s = [k, iflatten_instance(d.get(k, ()))]
- while s:
- if isinstance(s[-1], basestring):
- yield s.pop(-1)
- continue
- exhausted = True
- for x in s[-1]:
- v = d.get(x)
- if v:
- s.append(x)
- s.append(iflatten_instance(v))
- exhausted = False
- break
- yield x
- if exhausted:
- s.pop(-1)
-
-
-class ForcedDepends(type):
- """
- Metaclass forcing methods to run in a certain order.
-
- Dependencies are controlled by the existance of a stage_depends
- dict in the class namespace. Its keys are method names, values are
- either a string (name of preceeding method), or list/tuple
- (proceeding methods).
-
- U{pkgcore projects pkgcore.intefaces.format.build_base is an example consumer<http://pkgcore.org>}
- to look at for usage.
- """
- def __call__(cls, *a, **kw):
- o = super(ForcedDepends, cls).__call__(*a, **kw)
- if not getattr(cls, "stage_depends"):
- return o
-
- if not hasattr(o, "_stage_state"):
- o._stage_state = set()
-
- # wrap the funcs
-
- for x in set(x for x in iflatten_instance(o.stage_depends.iteritems())
- if x):
- f = getattr(o, x)
- f2 = partial(ensure_deps, o, x)
- f2.raw_func = f
- setattr(o, x, f2)
-
- return o
diff --git a/snakeoil/descriptors.py b/snakeoil/descriptors.py
deleted file mode 100644
index bf31b9f..0000000
--- a/snakeoil/descriptors.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright: 2006 Marien Zwart <marienz@gentoo.org>
-# License: GPL2
-
-
-"""Classes implementing the descriptor protocol."""
-
-
-class classproperty(object):
-
- """Like the builtin C{property} but takes a single classmethod.
-
- Used like this:
-
- class Example(object):
-
- @classproperty
- def test(cls):
- # Do stuff with cls here (it is Example or a subclass).
-
- Now both C{Example.test} and C{Example().test} invoke the getter.
- A "normal" property only works on instances.
- """
-
- def __init__(self, getter):
- self.getter = getter
-
- def __get__(self, instance, owner):
- return self.getter(owner)
diff --git a/snakeoil/fileutils.py b/snakeoil/fileutils.py
deleted file mode 100644
index 9c9478e..0000000
--- a/snakeoil/fileutils.py
+++ /dev/null
@@ -1,284 +0,0 @@
-# Copyright: 2005-2006 Brian Harring <ferringb@gmail.com>
-# License: GPL2
-
-"""
-file related operations, mainly reading
-"""
-
-import re, os
-from shlex import shlex
-from snakeoil.mappings import ProtectedDict
-from snakeoil.osutils import readlines
-
-class AtomicWriteFile(file):
-
- """File class that stores the changes in a tempfile.
-
- Upon close call, uses rename to replace the destination.
-
- Similar to file protocol behaviour, except for the C{__init__}, and
- that close *must* be called for the changes to be made live,
-
- if C{__del__} is triggered it's assumed that an exception occured,
- thus the changes shouldn't be made live.
- """
- def __init__(self, fp, binary=False, perms=None, uid=-1, gid=-1, **kwds):
- self.is_finalized = False
- if binary:
- file_mode = "wb"
- else:
- file_mode = "w"
- fp = os.path.realpath(fp)
- self.original_fp = fp
- self.temp_fp = os.path.join(
- os.path.dirname(fp), ".update.%s" % os.path.basename(fp))
- old_umask = None
- if perms:
- # give it just write perms
- old_umask = os.umask(0200)
- try:
- file.__init__(self, self.temp_fp, mode=file_mode, **kwds)
- finally:
- if old_umask is not None:
- os.umask(old_umask)
- if perms:
- os.chmod(self.temp_fp, perms)
- if (gid, uid) != (-1, -1):
- os.chown(self.temp_fp, uid, gid)
-
- def close(self):
- file.close(self)
- os.rename(self.temp_fp, self.original_fp)
- self.is_finalized = True
-
- def __del__(self):
- file.close(self)
- if not self.is_finalized:
- os.unlink(self.temp_fp)
-
-
-def iter_read_bash(bash_source):
- """
- Read file honoring bash commenting rules.
-
- Note that it's considered good behaviour to close filehandles, as
- such, either iterate fully through this, or use read_bash instead.
- Once the file object is no longer referenced the handle will be
- closed, but be proactive instead of relying on the garbage
- collector.
-
- @param bash_source: either a file to read from
- or a string holding the filename to open.
- """
- if isinstance(bash_source, basestring):
- bash_source = readlines(bash_source, True)
- for s in bash_source:
- s = s.strip()
- if s and s[0] != "#":
- yield s
-
-
-def read_bash(bash_source):
- return list(iter_read_bash(bash_source))
-read_bash.__doc__ = iter_read_bash.__doc__
-
-
-def read_dict(bash_source, splitter="=", source_isiter=False):
- """
- read key value pairs, ignoring bash-style comments.
-
- @param splitter: the string to split on. Can be None to
- default to str.split's default
- @param bash_source: either a file to read from,
- or a string holding the filename to open.
- """
- d = {}
- if not source_isiter:
- filename = bash_source
- i = iter_read_bash(bash_source)
- else:
- # XXX what to do?
- filename = '<unknown>'
- i = bash_source
- line_count = 1
- try:
- for k in i:
- line_count += 1
- try:
- k, v = k.split(splitter, 1)
- except ValueError:
- raise ParseError(filename, line_count)
- if len(v) > 2 and v[0] == v[-1] and v[0] in ("'", '"'):
- v = v[1:-1]
- d[k] = v
- finally:
- del i
- return d
-
-def read_bash_dict(bash_source, vars_dict=None, sourcing_command=None):
- """
- read bash source, yielding a dict of vars
-
- @param bash_source: either a file to read from
- or a string holding the filename to open
- @param vars_dict: initial 'env' for the sourcing.
- Is protected from modification.
- @type vars_dict: dict or None
- @param sourcing_command: controls whether a source command exists.
- If one does and is encountered, then this func is called.
- @type sourcing_command: callable
- @raise ParseError: thrown if invalid syntax is encountered.
- @return: dict representing the resultant env if bash executed the source.
- """
-
- # quite possibly I'm missing something here, but the original
- # portage_util getconfig/varexpand seemed like it only went
- # halfway. The shlex posix mode *should* cover everything.
-
- if vars_dict is not None:
- d, protected = ProtectedDict(vars_dict), True
- else:
- d, protected = {}, False
- if isinstance(bash_source, basestring):
- f = open(bash_source, "r")
- else:
- f = bash_source
- s = bash_parser(f, sourcing_command=sourcing_command, env=d)
-
- try:
- tok = ""
- try:
- while tok is not None:
- key = s.get_token()
- if key is None:
- break
- elif key.isspace():
- # we specifically have to check this, since we're
- # screwing with the whitespace filters below to
- # detect empty assigns
- continue
- eq = s.get_token()
- if eq != '=':
- raise ParseError(bash_source, s.lineno,
- "got token %r, was expecting '='" % eq)
- val = s.get_token()
- if val is None:
- val = ''
- # look ahead to see if we just got an empty assign.
- next_tok = s.get_token()
- if next_tok == '=':
- # ... we did.
- # leftmost insertions, thus reversed ordering
- s.push_token(next_tok)
- s.push_token(val)
- val = ''
- else:
- s.push_token(next_tok)
- d[key] = val
- except ValueError, e:
- raise ParseError(bash_source, s.lineno, str(e))
- finally:
- del f
- if protected:
- d = d.new
- return d
-
-
-var_find = re.compile(r'\\?(\${\w+}|\$\w+)')
-backslash_find = re.compile(r'\\.')
-def nuke_backslash(s):
- s = s.group()
- if s == "\\\n":
- return "\n"
- try:
- return chr(ord(s))
- except TypeError:
- return s[1]
-
-class bash_parser(shlex):
- def __init__(self, source, sourcing_command=None, env=None):
- self.__dict__['state'] = ' '
- shlex.__init__(self, source, posix=True)
- self.wordchars += "@${}/.-+/:~^"
- self.wordchars = frozenset(self.wordchars)
- if sourcing_command is not None:
- self.source = sourcing_command
- if env is None:
- env = {}
- self.env = env
- self.__pos = 0
-
- def __setattr__(self, attr, val):
- if attr == "state":
- if (self.state, val) in (
- ('"', 'a'), ('a', '"'), ('a', ' '), ("'", 'a')):
- strl = len(self.token)
- if self.__pos != strl:
- self.changed_state.append(
- (self.state, self.token[self.__pos:]))
- self.__pos = strl
- self.__dict__[attr] = val
-
- def sourcehook(self, newfile):
- try:
- return shlex.sourcehook(self, newfile)
- except IOError, ie:
- raise ParseError(newfile, 0, str(ie))
-
- def read_token(self):
- self.changed_state = []
- self.__pos = 0
- tok = shlex.read_token(self)
- if tok is None:
- return tok
- self.changed_state.append((self.state, self.token[self.__pos:]))
- tok = ''
- for s, t in self.changed_state:
- if s in ('"', "a"):
- tok += self.var_expand(t).replace("\\\n", '')
- else:
- tok += t
- return tok
-
- def var_expand(self, val):
- prev, pos = 0, 0
- l = []
- match = var_find.search(val)
- while match is not None:
- pos = match.start()
- if val[pos] == '\\':
- # it's escaped. either it's \\$ or \\${ , either way,
- # skipping two ahead handles it.
- pos += 2
- else:
- var = val[match.start():match.end()].strip("${}")
- if prev != pos:
- l.append(val[prev:pos])
- if var in self.env:
- if not isinstance(self.env[var], basestring):
- raise ValueError(
- "env key %r must be a string, not %s: %r" % (
- var, type(self.env[var]), self.env[var]))
- l.append(self.env[var])
- else:
- l.append("")
- prev = pos = match.end()
- match = var_find.search(val, pos)
-
- # do \\ cleansing, collapsing val down also.
- val = backslash_find.sub(nuke_backslash, ''.join(l) + val[prev:])
- return val
-
-
-class ParseError(Exception):
-
- def __init__(self, filename, line, errmsg=None):
- if errmsg is not None:
- Exception.__init__(self,
- "error parsing '%s' on or before %i: err %s" %
- (filename, line, errmsg))
- else:
- Exception.__init__(self,
- "error parsing '%s' on or before %i" %
- (filename, line))
- self.file, self.line, self.errmsg = filename, line, errmsg
diff --git a/snakeoil/fix_copy.py b/snakeoil/fix_copy.py
deleted file mode 100644
index 3a73d5b..0000000
--- a/snakeoil/fix_copy.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# Copyright (C) 2005, 2006 Canonical Ltd
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-"""A version of inspect that includes what 'copy' needs.
-
-Importing the python standard module 'copy' is far more expensive than it
-needs to be, because copy imports 'inspect' which imports 'tokenize'.
-And 'copy' only needs 2 small functions out of 'inspect', but has to
-load all of 'tokenize', which makes it horribly slow.
-
-This module is designed to use tricky hacks in import rules, to avoid this
-overhead.
-"""
-
-
-####
-# These are the only 2 functions that 'copy' needs from 'inspect'
-# As you can see, they are quite trivial, and don't justify the
-# 40ms spent to import 'inspect' because it is importing 'tokenize'
-# These are copied verbatim from the python standard library.
-
-# ----------------------------------------------------------- class helpers
-def _searchbases(cls, accum):
- # Simulate the "classic class" search order.
- if cls in accum:
- return
- accum.append(cls)
- for base in cls.__bases__:
- _searchbases(base, accum)
-
-
-def getmro(cls):
- "Return tuple of base classes (including cls) in method resolution order."
- if hasattr(cls, "__mro__"):
- return cls.__mro__
- else:
- result = []
- _searchbases(cls, result)
- return tuple(result)
-
-
-def inject_copy():
- """Import the 'copy' module with a hacked 'inspect' module"""
- # We don't actually care about 'getmro' but we need to pass
- # something in the list so that we get the direct module,
- # rather than getting the base module
- import sys
-
- # Don't hack around if 'inspect' already exists
- if 'inspect' in sys.modules:
- import copy
- return
-
- mod = __import__('snakeoil.fix_copy',
- globals(), locals(), ['getmro'])
-
- sys.modules['inspect'] = mod
- try:
- import copy
- finally:
- del sys.modules['inspect']
diff --git a/snakeoil/formatters.py b/snakeoil/formatters.py
deleted file mode 100644
index b327bb4..0000000
--- a/snakeoil/formatters.py
+++ /dev/null
@@ -1,495 +0,0 @@
-# Copyright: 2006 Marien Zwart <marienz@gentoo.org>
-# License: GPL2
-
-"""Classes wrapping a file-like object to do fancy output on it."""
-
-import os
-import errno
-
-from snakeoil.klass import GetAttrProxy
-from snakeoil.demandload import demandload
-demandload(globals(), 'locale')
-
-
-class native_StreamClosed(KeyboardInterrupt):
- """Raised by L{Formatter.write} if the stream it prints to was closed.
-
- This inherits from C{KeyboardInterrupt} because it should usually
- be handled the same way: a common way of triggering this exception
- is by closing a pager before the script finished outputting, which
- should be handled like control+c, not like an error.
- """
-
-
-# "Invalid name" (for fg and bg methods, too short)
-# pylint: disable-msg=C0103
-
-
-class Formatter(object):
-
- """Abstract formatter base class.
-
- The types of most of the instance attributes is undefined (depends
- on the implementation of the particular Formatter subclass).
-
- @ivar bold: object to pass to L{write} to switch to bold mode.
- @ivar underline: object to pass to L{write} to switch to underlined mode.
- @ivar reset: object to pass to L{write} to turn off bold and underline.
- @ivar wrap: boolean indicating we auto-linewrap (defaults to off).
- @ivar autoline: boolean indicating we are in auto-newline mode
- (defaults to on).
- """
-
- def __init__(self):
- self.autoline = True
- self.wrap = False
-
- def write(self, *args, **kwargs):
- """Write something to the stream.
-
- Acceptable arguments are:
- - Strings are simply written to the stream.
- - C{None} is ignored.
- - Functions are called with the formatter as argument.
- Their return value is then used the same way as the other
- arguments.
- - Formatter subclasses might special-case certain objects.
-
- Accepts wrap and autoline as keyword arguments. Effect is
- the same as setting them before the write call and resetting
- them afterwards.
-
- Accepts first_prefixes and later_prefixes as keyword
- arguments. They should be sequences that are temporarily
- appended to the first_prefix and later_prefix attributes.
-
- Accepts prefixes as a keyword argument. Effect is the same as
- setting first_prefixes and later_prefixes to the same value.
-
- Accepts first_prefix, later_prefix and prefix as keyword
- argument. Effect is the same as setting first_prefixes,
- later_prefixes or prefixes to a one-element tuple.
-
- The formatter has a couple of attributes that are useful as argument
- to write.
- """
-
- def fg(self, color=None):
- """Change foreground color.
-
- @type color: a string or C{None}.
- @param color: color to change to. A default is used if omitted.
- C{None} resets to the default color.
- """
-
- def bg(self, color=None):
- """Change background color.
-
- @type color: a string or C{None}.
- @param color: color to change to. A default is used if omitted.
- C{None} resets to the default color.
- """
-
- def error(self, message):
- """Format a string as an error message."""
- self.write(message, prefixes=(
- self.fg('red'), self.bold, '!!! ', self.reset))
-
- def warn(self, message):
- """Format a string as a warning message."""
- self.write(message, prefixes=(
- self.fg('yellow'), self.bold, '*** ', self.reset))
-
- def title(self, string):
- """Set the title to string"""
- pass
-
-
-class native_PlainTextFormatter(Formatter):
-
- """Formatter writing plain text to a file-like object.
-
- @ivar width: contains the current maximum line length.
- @ivar encoding: the encoding unicode strings should be converted to.
- @ivar first_prefix: prefixes to output at the beginning of every write.
- @ivar later_prefix: prefixes to output on each line after the first of
- every write.
- """
-
- bold = underline = reset = ''
-
- def __init__(self, stream, width=79, encoding=None):
- """Initialize.
-
- @type stream: file-like object.
- @param stream: stream to output to.
- @param width: maximum line width (defaults to 79).
- @param encoding: encoding unicode strings are converted to.
- """
- Formatter.__init__(self)
- self.stream = stream
- if encoding is None:
- encoding = getattr(self.stream, 'encoding', None)
- if encoding is None:
- try:
- encoding = locale.getpreferredencoding()
- except locale.Error:
- encoding = 'ascii'
- self.encoding = encoding
- self.width = width
- self._pos = 0
- self._in_first_line = True
- self._wrote_something = False
- self.first_prefix = []
- self.later_prefix = []
-
-
- def _write_prefix(self, wrap):
- if self._in_first_line:
- prefix = self.first_prefix
- else:
- prefix = self.later_prefix
- # This is a bit braindead since it duplicates a lot of code
- # from write. Avoids fun things like word wrapped prefix though.
-
- for thing in prefix:
- while callable(thing):
- thing = thing(self)
- if thing is None:
- continue
- if not isinstance(thing, basestring):
- thing = str(thing)
- self._pos += len(thing)
- if isinstance(thing, unicode):
- thing = thing.encode(self.encoding, 'replace')
- self.stream.write(thing)
- if wrap and self._pos >= self.width:
- # XXX What to do? Our prefix does not fit.
- # This makes sure we still output something,
- # but it is completely arbitrary.
- self._pos = self.width - 10
-
-
- def write(self, *args, **kwargs):
- wrap = kwargs.get('wrap', self.wrap)
- autoline = kwargs.get('autoline', self.autoline)
- prefixes = kwargs.get('prefixes')
- first_prefixes = kwargs.get('first_prefixes')
- later_prefixes = kwargs.get('later_prefixes')
- if prefixes is not None:
- if first_prefixes is not None or later_prefixes is not None:
- raise TypeError(
- 'do not pass first_prefixes or later_prefixes '
- 'if prefixes is passed')
- first_prefixes = later_prefixes = prefixes
- prefix = kwargs.get('prefix')
- first_prefix = kwargs.get('first_prefix')
- later_prefix = kwargs.get('later_prefix')
- if prefix is not None:
- if first_prefix is not None or later_prefix is not None:
- raise TypeError(
- 'do not pass first_prefix or later_prefix with prefix')
- first_prefix = later_prefix = prefix
- if first_prefix is not None:
- if first_prefixes is not None:
- raise TypeError(
- 'do not pass both first_prefix and first_prefixes')
- first_prefixes = (first_prefix,)
- if later_prefix is not None:
- if later_prefixes is not None:
- raise TypeError(
- 'do not pass both later_prefix and later_prefixes')
- later_prefixes = (later_prefix,)
- if first_prefixes is not None:
- self.first_prefix.extend(first_prefixes)
- if later_prefixes is not None:
- self.later_prefix.extend(later_prefixes)
- # Remove this nested try block once we depend on python 2.5
- try:
- try:
- for arg in args:
- # If we're at the start of the line, write our prefix.
- # There is a deficiency here: if neither our arg nor our
- # prefix affect _pos (both are escape sequences or empty)
- # we will write prefix more than once. This should not
- # matter.
- if not self._pos:
- self._write_prefix(wrap)
- while callable(arg):
- arg = arg(self)
- if arg is None:
- continue
- if not isinstance(arg, basestring):
- arg = str(arg)
- is_unicode = isinstance(arg, unicode)
- while wrap and self._pos + len(arg) > self.width:
- # We have to split.
- maxlen = self.width - self._pos
- space = arg.rfind(' ', 0, maxlen)
- if space == -1:
- # No space to split on.
-
- # If we are on the first line we can simply go to
- # the next (this helps if the "later" prefix is
- # shorter and should not really matter if not).
-
- # If we are on the second line and have already
- # written something we can also go to the next
- # line.
- if self._in_first_line or self._wrote_something:
- bit = ''
- else:
- # Forcibly split this as far to the right as
- # possible.
- bit = arg[:maxlen]
- arg = arg[maxlen:]
- else:
- bit = arg[:space]
- # Omit the space we split on.
- arg = arg[space+1:]
- if is_unicode:
- bit = bit.encode(self.encoding, 'replace')
- self.stream.write(bit)
- self.stream.write('\n')
- self._pos = 0
- self._in_first_line = False
- self._wrote_something = False
- self._write_prefix(wrap)
-
- # This fits.
- self._wrote_something = True
- self._pos += len(arg)
- if is_unicode:
- arg = arg.encode(self.encoding, 'replace')
- self.stream.write(arg)
- if autoline:
- self.stream.write('\n')
- self._wrote_something = False
- self._pos = 0
- self._in_first_line = True
- except IOError, e:
- if e.errno == errno.EPIPE:
- raise StreamClosed(e)
- raise
- finally:
- if first_prefixes is not None:
- self.first_prefix = self.first_prefix[:-len(first_prefixes)]
- if later_prefixes is not None:
- self.later_prefix = self.later_prefix[:-len(later_prefixes)]
-
- def fg(self, color=None):
- return ''
-
- def bg(self, color=None):
- return ''
-
-try:
- from snakeoil._formatters import PlainTextFormatter, StreamClosed
- class PlainTextFormatter(PlainTextFormatter, Formatter):
- __doc__ = native_PlainTextFormatter.__doc__
- __slots__ = ()
- def fg(self, color=None):
- return ''
- bg = fg
-
-except ImportError:
- PlainTextFormatter = native_PlainTextFormatter
- StreamClosed = native_StreamClosed
-
-# This is necessary because the curses module is optional (and we
-# should run on a very minimal python for bootstrapping).
-try:
- import curses
-except ImportError:
- TerminfoColor = None
-else:
- class TerminfoColor(object):
-
- def __init__(self, mode, color):
- self.mode = mode
- self.color = color
-
- def __call__(self, formatter):
- if self.color is None:
- formatter._current_colors[self.mode] = None
- res = formatter._color_reset
- # slight abuse of boolean True/False and 1/0 equivalence
- other = formatter._current_colors[not self.mode]
- if other is not None:
- res = res + other
- else:
- if self.mode == 0:
- default = curses.COLOR_WHITE
- else:
- default = curses.COLOR_BLACK
- color = formatter._colors.get(self.color, default)
- # The curses module currently segfaults if handed a
- # bogus template so check explicitly.
- template = formatter._set_color[self.mode]
- if template:
- res = curses.tparm(template, color)
- else:
- res = ''
- formatter._current_colors[self.mode] = res
- formatter.stream.write(res)
-
-
- class TerminfoCode(object):
- def __init__(self, value):
- self.value = value
-
- class TerminfoMode(TerminfoCode):
- def __call__(self, formatter):
- formatter._modes.add(self)
- formatter.stream.write(self.value)
-
- class TerminfoReset(TerminfoCode):
- def __call__(self, formatter):
- formatter._modes.clear()
- formatter.stream.write(self.value)
-
-
- class TerminfoFormatter(PlainTextFormatter):
-
- """Formatter writing to a tty, using terminfo to do colors."""
-
- _colors = dict(
- black = curses.COLOR_BLACK,
- red = curses.COLOR_RED,
- green = curses.COLOR_GREEN,
- yellow = curses.COLOR_YELLOW,
- blue = curses.COLOR_BLUE,
- magenta = curses.COLOR_MAGENTA,
- cyan = curses.COLOR_CYAN,
- white = curses.COLOR_WHITE)
-
- # Remapping of TERM setting to more capable equivalent.
- # Mainly used to force on the hardstatus (aka title bar updates)
- # capability for terminals that do not support this by default.
- term_alternates = {
- 'xterm': 'xterm+sl',
- 'screen': 'screen-s',
- }
-
- def __init__(self, stream, term=None, forcetty=False, encoding=None):
- """Initialize.
-
- @type stream: file-like object.
- @param stream: stream to output to, defaulting to C{sys.stdout}.
- @type term: string.
- @param term: terminal type, pulled from the environment if omitted.
- @type forcetty: bool
- @param forcetty: force output of colors even if the wrapped stream
- is not a tty.
- """
- PlainTextFormatter.__init__(self, stream, encoding=encoding)
- fd = stream.fileno()
- if term is None:
- # We only apply the remapping if we are guessing the
- # terminal type from the environment. If we get a term
- # type passed explicitly we just use it as-is (if the
- # caller wants the remap just doing the
- # term_alternates lookup there is easy enough.)
- term_env = os.environ.get('TERM')
- term_alt = self.term_alternates.get(term_env)
- for term in (term_alt, term_env, 'dumb'):
- if term is not None:
- try:
- curses.setupterm(fd=fd, term=term)
- except curses.error:
- pass
- else:
- break
- else:
- raise ValueError(
- 'no terminfo entries, not even for "dumb"?')
- else:
- # TODO maybe do something more useful than raising curses.error
- # if term is not in the terminfo db here?
- curses.setupterm(fd=fd, term=term)
- self.width = curses.tigetnum('cols')
- self.reset = TerminfoReset(curses.tigetstr('sgr0'))
- self.bold = TerminfoMode(curses.tigetstr('bold'))
- self.underline = TerminfoMode(curses.tigetstr('smul'))
- self._color_reset = curses.tigetstr('op')
- self._set_color = (
- curses.tigetstr('setaf'), curses.tigetstr('setab'))
- # [fg, bg]
- self._current_colors = [None, None]
- self._modes = set()
- self._pos = 0
-
- def fg(self, color=None):
- return TerminfoColor(0, color)
-
- def bg(self, color=None):
- return TerminfoColor(1, color)
-
- def write(self, *args, **kwargs):
- PlainTextFormatter.write(self, *args, **kwargs)
- try:
- if self._modes:
- self.reset(self)
- if self._current_colors != [None, None]:
- self._current_colors = [None, None]
- self.stream.write(self._color_reset)
- except IOError, e:
- if e.errno == errno.EPIPE:
- raise StreamClosed(e)
- raise
-
- def title(self, string):
- # I want to use curses.tigetflag('hs') here but at least
- # the screen-s entry defines a tsl and fsl string but does
- # not set the hs flag. So just check for the ability to
- # jump to and out of the status line, without checking if
- # the status line we're using exists.
- if curses.tigetstr('tsl') and curses.tigetstr('fsl'):
- self.stream.write(
- curses.tigetstr('tsl') + string + curses.tigetstr('fsl'))
- self.stream.flush()
-
-
-class ObserverFormatter(object):
-
- def __init__(self, real_formatter):
- self._formatter = real_formatter
-
- def write(self, *args):
- self._formatter.write(autoline=False, *args)
-
- __getattr__ = GetAttrProxy("_formatter")
-
-
-def get_formatter(stream):
- """TerminfoFormatter if the stream is a tty, else PlainTextFormatter."""
- if TerminfoColor is None:
- return PlainTextFormatter(stream)
- try:
- fd = stream.fileno()
- except AttributeError:
- pass
- else:
- # We do this instead of stream.isatty() because TerminfoFormatter
- # needs an fd to pass to curses, not just a filelike talking to a tty.
- if os.isatty(fd):
- try:
- return TerminfoFormatter(stream)
- except curses.error:
- # This happens if TERM is unset and possibly in more cases.
- # Just fall back to the PlainTextFormatter.
- pass
- return PlainTextFormatter(stream)
-
-
-def decorate_forced_wrapping(setting=True):
- def wrapped_func(func):
- def f(out, *args, **kwds):
- oldwrap = out.wrap
- out.wrap = setting
- try:
- return func(out, *args, **kwds)
- finally:
- out.wrap = oldwrap
- return f
- return wrapped_func
diff --git a/snakeoil/iterables.py b/snakeoil/iterables.py
deleted file mode 100644
index e6f05bf..0000000
--- a/snakeoil/iterables.py
+++ /dev/null
@@ -1,202 +0,0 @@
-# Copyright: 2006 Brian Harring <ferringb@gmail.com>
-# License: GPL2
-
-from collections import deque
-
-class expandable_chain(object):
- """
- chained iterables, with the ability to add new iterables to the chain
- as long as the instance hasn't raised StopIteration already.
- """
-
- __slot__ = ("iterables", "__weakref__")
-
- def __init__(self, *iterables):
- """
- accepts N iterables, must have at least one specified
- """
- self.iterables = deque()
- self.extend(iterables)
-
- def __iter__(self):
- return self
-
- def next(self):
- if self.iterables is not None:
- while self.iterables:
- try:
- return self.iterables[0].next()
- except StopIteration:
- self.iterables.popleft()
- self.iterables = None
- raise StopIteration()
-
- def append(self, iterable):
- """append an iterable to the chain to be consumed"""
- if self.iterables is None:
- raise StopIteration()
- self.iterables.append(iter(iterable))
-
- def appendleft(self, iterable):
- """prepend an iterable to the chain to be consumed"""
- if self.iterables is None:
- raise StopIteration()
- self.iterables.appendleft(iter(iterable))
-
- def extend(self, iterables):
- """extend multiple iterables to the chain to be consumed"""
- if self.iterables is None:
- raise StopIteration()
- self.iterables.extend(iter(x) for x in iterables)
-
- def extendleft(self, iterables):
- """prepend multiple iterables to the chain to be consumed"""
- if self.iterables is None:
- raise StopIteration()
- self.iterables.extendleft(iter(x) for x in iterables)
-
-
-class caching_iter(object):
- """
- On demand consumes from an iterable so as to appear like a tuple
- """
- __slots__ = ("iterable", "__weakref__", "cached_list", "sorter")
-
- def __init__(self, iterable, sorter=None):
- self.sorter = sorter
- self.iterable = iter(iterable)
- self.cached_list = []
-
- def __setitem__(self, key, val):
- raise TypeError("non modifiable")
-
- def __getitem__(self, index):
- existing_len = len(self.cached_list)
- if self.iterable is not None and self.sorter:
- self.cached_list.extend(self.iterable)
- self.cached_list = tuple(self.sorter(self.cached_list))
- self.iterable = self.sorter = None
- existing_len = len(self.cached_list)
-
- if index < 0:
- if self.iterable is not None:
- self.cached_list = tuple(self.cached_list + list(self.iterable))
- self.iterable = None
- existing_len = len(self.cached_list)
-
- index = existing_len + index
- if index < 0:
- raise IndexError("list index out of range")
-
- elif index >= existing_len - 1:
- if self.iterable is not None:
- try:
- self.cached_list.extend(self.iterable.next()
- for i in xrange(existing_len - index + 1))
- except StopIteration:
- # consumed, baby.
- self.iterable = None
- self.cached_list = tuple(self.cached_list)
- raise IndexError("list index out of range")
-
- return self.cached_list[index]
-
- def __cmp__(self, other):
- if self.iterable is not None:
- if self.sorter:
- self.cached_list.extend(self.iterable)
- self.cached_list = tuple(self.sorter(self.cached_list))
- self.sorter = None
- else:
- self.cached_list = tuple(self.cached_list + list(self.iterable))
- self.iterable = None
- return cmp(self.cached_list, other)
-
- def __nonzero__(self):
- if self.cached_list:
- return True
-
- if self.iterable:
- for x in self.iterable:
- self.cached_list.append(x)
- return True
- # if we've made it here... then nothing more in the iterable.
- self.iterable = self.sorter = None
- self.cached_list = ()
- return False
-
- def __len__(self):
- if self.iterable is not None:
- self.cached_list.extend(self.iterable)
- if self.sorter:
- self.cached_list = tuple(self.sorter(self.cached_list))
- self.sorter = None
- else:
- self.cached_list = tuple(self.cached_list)
- self.iterable = None
- return len(self.cached_list)
-
- def __iter__(self):
- if (self.sorter is not None and
- self.iterable is not None and
- len(self.cached_list) == 0):
- self.cached_list = tuple(self.sorter(self.iterable))
- self.iterable = self.sorter = None
-
- for x in self.cached_list:
- yield x
- if self.iterable is not None:
- for x in self.iterable:
- self.cached_list.append(x)
- yield x
- else:
- return
- self.iterable = None
- self.cached_list = tuple(self.cached_list)
-
- def __hash__(self):
- if self.iterable is not None:
- self.cached_list.extend(self.iterable)
- self.cached_list = tuple(self.cached_list)
- self.iterable = None
- return hash(self.cached_list)
-
- def __str__(self):
- return "iterable(%s), cached: %s" % (
- self.iterable, str(self.cached_list))
-
-def iter_sort(sorter, *iterables):
- """Merge a number of sorted iterables into a single sorted iterable.
-
- @type sorter: callable.
- @param sorter: function, passed a list of [element, iterable].
- @param iterables: iterables to consume from.
- B{Required} to yield in presorted order.
- """
- l = []
- for x in iterables:
- try:
- x = iter(x)
- l.append([x.next(), x])
- except StopIteration:
- pass
- if len(l) == 1:
- yield l[0][0]
- for x in l[0][1]:
- yield x
- return
- l = sorter(l)
- while l:
- yield l[0][0]
- for y in l[0][1]:
- l[0][0] = y
- break
- else:
- del l[0]
- if len(l) == 1:
- yield l[0][0]
- for x in l[0][1]:
- yield x
- break
- continue
- l = sorter(l)
diff --git a/snakeoil/klass.py b/snakeoil/klass.py
deleted file mode 100644
index 6763c1f..0000000
--- a/snakeoil/klass.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# Copyright: 2006 Brian Harring <ferringb@gmail.com>
-# License: GPL2
-
-from operator import attrgetter
-from snakeoil.caching import WeakInstMeta
-from collections import deque
-
-def native_GetAttrProxy(target):
- def reflected_getattr(self, attr):
- return getattr(getattr(self, target), attr)
- return reflected_getattr
-
-def native_contains(self, key):
- try:
- self[key]
- return True
- except KeyError:
- return False
-
-def native_get(self, key, default=None):
- try:
- return self[key]
- except KeyError:
- return default
-
-
-attrlist_getter = attrgetter("__attr_comparison__")
-def native_generic_eq(inst1, inst2, sentinel=object()):
- if inst1 is inst2:
- return True
- for attr in attrlist_getter(inst1):
- if getattr(inst1, attr, sentinel) != \
- getattr(inst2, attr, sentinel):
- return False
- return True
-
-def native_generic_ne(inst1, inst2, sentinel=object()):
- if inst1 is inst2:
- return False
- for attr in attrlist_getter(inst1):
- if getattr(inst1, attr, sentinel) != \
- getattr(inst2, attr, sentinel):
- return True
- return False
-
-try:
- from snakeoil._klass import (GetAttrProxy, contains, get,
- generic_eq, generic_ne)
-except ImportError:
- GetAttrProxy = native_GetAttrProxy
- contains = native_contains
- get = native_get
- generic_eq = native_generic_eq
- generic_ne = native_generic_ne
-
-
-def generic_equality(name, bases, scope, real_type=type,
- eq=generic_eq, ne=generic_ne):
- attrlist = scope.pop("__attr_comparison__", None)
- if attrlist is None:
- raise TypeError("__attr_comparison__ must be in the classes scope")
- for x in attrlist:
- if not isinstance(x, str):
- raise TypeError("all members of attrlist must be strings- "
- " got %r %s" % (type(x), repr(x)))
-
- scope["__attr_comparison__"] = tuple(attrlist)
- scope.setdefault("__eq__", eq)
- scope.setdefault("__ne__", ne)
- return real_type(name, bases, scope)
-
-
-class chained_getter(object):
- def __metaclass__(name, bases, scope):
- return generic_equality(name, bases, scope, real_type=WeakInstMeta)
- __slots__ = ('namespace', 'chain')
- __fifo_cache__ = deque()
- __inst_caching__ = True
- __attr_comparison__ = ("namespace",)
-
- def __init__(self, namespace):
- self.namespace = namespace
- self.chain = map(attrgetter, namespace.split("."))
- if len(self.__fifo_cache__) > 10:
- self.__fifo_cache__.popleft()
- self.__fifo_cache__.append(self)
-
- def __hash__(self):
- return hash(self.namespace)
-
- def __call__(self, obj):
- o = obj
- for f in self.chain:
- o = f(o)
- return o
diff --git a/snakeoil/lists.py b/snakeoil/lists.py
deleted file mode 100644
index 3cc8d4c..0000000
--- a/snakeoil/lists.py
+++ /dev/null
@@ -1,171 +0,0 @@
-# Copyright: 2005 Brian Harring <ferringb@gmail.com>
-# License: GPL2
-
-"""
-sequence related operations
-"""
-
-from snakeoil.iterables import expandable_chain
-
-def unstable_unique(sequence):
- """
- lifted from python cookbook, credit: Tim Peters
- Return a list of the elements in s in arbitrary order, sans duplicates
- """
-
- n = len(sequence)
- # assume all elements are hashable, if so, it's linear
- try:
- return list(set(sequence))
- except TypeError:
- pass
-
- # so much for linear. abuse sort.
- try:
- t = sorted(sequence)
- except TypeError:
- pass
- else:
- assert n > 0
- last = t[0]
- lasti = i = 1
- while i < n:
- if t[i] != last:
- t[lasti] = last = t[i]
- lasti += 1
- i += 1
- return t[:lasti]
-
- # blah. back to original portage.unique_array
- u = []
- for x in sequence:
- if x not in u:
- u.append(x)
- return u
-
-def stable_unique(iterable):
- """
- return unique list from iterable, preserving ordering
- """
- return list(iter_stable_unique(iterable))
-
-def iter_stable_unique(iterable):
- """
- generator yielding unique elements from iterable, preserving ordering
- """
- s = set()
- for x in iterable:
- if x not in s:
- yield x
- s.add(x)
-
-def native_iflatten_instance(l, skip_flattening=(basestring,)):
- """
- collapse [[1],2] into [1,2]
-
- @param skip_flattening: list of classes to not descend through
- """
- if isinstance(l, skip_flattening):
- yield l
- return
- iters = expandable_chain(l)
- try:
- while True:
- x = iters.next()
- if hasattr(x, '__iter__') and not isinstance(x, skip_flattening):
- iters.appendleft(x)
- else:
- yield x
- except StopIteration:
- pass
-
-def native_iflatten_func(l, skip_func):
- """
- collapse [[1],2] into [1,2]
-
- @param skip_func: a callable that returns True when iflatten_func should
- descend no further
- """
- if skip_func(l):
- yield l
- return
- iters = expandable_chain(l)
- try:
- while True:
- x = iters.next()
- if hasattr(x, '__iter__') and not skip_func(x):
- iters.appendleft(x)
- else:
- yield x
- except StopIteration:
- pass
-
-
-try:
- # No name "readdir" in module osutils
- # pylint: disable-msg=E0611
- from snakeoil._lists import iflatten_instance, iflatten_func
- cpy_builtin = True
-except ImportError:
- cpy_builtin = False
- cpy_iflatten_instance = cpy_iflatten_func = None
- iflatten_instance = native_iflatten_instance
- iflatten_func = native_iflatten_func
-
-
-class ChainedLists(object):
- """
- sequences chained together, without collapsing into a list
- """
- __slots__ = ("_lists", "__weakref__")
-
- def __init__(self, *lists):
- """
- all args must be sequences
- """
- # ensure they're iterable
- for x in lists:
- iter(x)
-
- if isinstance(lists, tuple):
- lists = list(lists)
- self._lists = lists
-
- def __len__(self):
- return sum(len(l) for l in self._lists)
-
- def __getitem__(self, idx):
- if idx < 0:
- idx += len(self)
- if idx < 0:
- raise IndexError
- for l in self._lists:
- l2 = len(l)
- if idx < l2:
- return l[idx]
- idx -= l2
- else:
- raise IndexError
-
- def __setitem__(self, idx, val):
- raise TypeError("not mutable")
-
- def __delitem__(self, idx):
- raise TypeError("not mutable")
-
- def __iter__(self):
- for l in self._lists:
- for x in l:
- yield x
-
- def __contains__(self, obj):
- return obj in iter(self)
-
- def __str__(self):
- return "[ %s ]" % ", ".join(str(l) for l in self._lists)
-
- def append(self, item):
- self._lists.append(item)
-
- def extend(self, items):
- self._lists.extend(items)
diff --git a/snakeoil/mappings.py b/snakeoil/mappings.py
deleted file mode 100644
index 7847c8c..0000000
--- a/snakeoil/mappings.py
+++ /dev/null
@@ -1,579 +0,0 @@
-# Copyright: 2005-2007 Brian Harring <ferringb@gmail.com>
-# License: GPL2
-
-"""
-miscellanious mapping/dict related classes
-"""
-
-import operator
-from itertools import imap, chain, ifilterfalse, izip
-from snakeoil.klass import get, contains
-from collections import deque
-
-
-class DictMixin(object):
- """
- new style class replacement for L{UserDict.DictMixin}
- designed around iter* methods rather then forcing lists as DictMixin does
- """
-
- __slots__ = ()
-
- __externally_mutable__ = True
-
- def __init__(self, iterable=None, **kwargs):
- if iterable is not None:
- self.update(iterable)
-
- if kwargs:
- self.update(kwargs.iteritems())
-
- def __iter__(self):
- return self.iterkeys()
-
- def keys(self):
- return list(self.iterkeys())
-
- def values(self):
- return list(self.itervalues())
-
- def items(self):
- return list(self.iteritems())
-
- def update(self, iterable):
- for k, v in iterable:
- self[k] = v
-
- get = get
- __contains__ = contains
-
- # default cmp actually operates based on key len comparison, oddly enough
- def __cmp__(self, other):
- for k1, k2 in izip(sorted(self), sorted(other)):
- c = cmp(k1, k2)
- if c != 0:
- return c
- c = cmp(self[k1], other[k2])
- if c != 0:
- return c
- c = cmp(len(self), len(other))
- return c
-
- def __eq__(self, other):
- return self.__cmp__(other) == 0
-
- def __ne__(self, other):
- return self.__cmp__(other) != 0
-
- def pop(self, key, default=None):
- if not self.__externally_mutable__:
- raise AttributeError(self, "pop")
- try:
- val = self[key]
- del self[key]
- except KeyError:
- if default is not None:
- return default
- raise
- return val
-
- def setdefault(self, key, default=None):
- if not self.__externally_mutable__:
- raise AttributeError(self, "setdefault")
- if key in self:
- return self[key]
- self[key] = default
- return default
-
- def has_key(self, key):
- return key in self
-
- def iterkeys(self):
- raise NotImplementedError(self, "iterkeys")
-
- def itervalues(self):
- return imap(self.__getitem__, self)
-
- def iteritems(self):
- for k in self:
- yield k, self[k]
-
- def __getitem__(self, key):
- raise NotImplementedError(self, "__getitem__")
-
- def __setitem__(self, key, val):
- if not self.__externally_mutable__:
- raise AttributeError(self, "__setitem__")
- raise NotImplementedError(self, "__setitem__")
-
- def __delitem__(self, key):
- if not self.__externally_mutable__:
- raise AttributeError(self, "__delitem__")
- raise NotImplementedError(self, "__delitem__")
-
- def clear(self):
- if not self.__externally_mutable__:
- raise AttributeError(self, "clear")
- # crappy, override if faster method exists.
- map(self.__delitem__, self.keys())
-
- def __len__(self):
- c = 0
- for x in self:
- c += 1
- return c
-
- def popitem(self):
- if not self.__externally_mutable__:
- raise AttributeError(self, "popitem")
- # do it this way so python handles the stopiteration; faster
- for key, val in self.iteritems():
- del self[key]
- return key, val
- raise KeyError("container is empty")
-
-
-class LazyValDict(DictMixin):
-
- """
- Mapping that loads values via a callable
-
- given a function to get keys, and to look up the val for those keys, it'll
- lazily load key definitions and values as requested
- """
- __slots__ = ("_keys", "_keys_func", "_vals", "_val_func")
- __externally_mutable__ = False
-
- def __init__(self, get_keys_func, get_val_func):
- """
- @param get_keys_func: either a container, or func to call to get keys.
- @param get_val_func: a callable that is JIT called
- with the key requested.
- """
- if not callable(get_val_func):
- raise TypeError("get_val_func isn't a callable")
- if hasattr(get_keys_func, "__iter__"):
- self._keys = get_keys_func
- self._keys_func = None
- else:
- if not callable(get_keys_func):
- raise TypeError(
- "get_keys_func isn't iterable or callable")
- self._keys_func = get_keys_func
- self._val_func = get_val_func
- self._vals = {}
-
- def __getitem__(self, key):
- if self._keys_func is not None:
- self._keys = set(self._keys_func())
- self._keys_func = None
- if key in self._vals:
- return self._vals[key]
- if key in self._keys:
- v = self._vals[key] = self._val_func(key)
- return v
- raise KeyError(key)
-
- def keys(self):
- if self._keys_func is not None:
- self._keys = set(self._keys_func())
- self._keys_func = None
- return list(self._keys)
-
- def iterkeys(self):
- if self._keys_func is not None:
- self._keys = set(self._keys_func())
- self._keys_func = None
- return iter(self._keys)
-
- def itervalues(self):
- return imap(self.__getitem__, self.iterkeys())
-
- def iteritems(self):
- return ((k, self[k]) for k in self.iterkeys())
-
- def __contains__(self, key):
- if self._keys_func is not None:
- self._keys = set(self._keys_func())
- self._keys_func = None
- return key in self._keys
-
- def __len__(self):
- if self._keys_func is not None:
- self._keys = set(self._keys_func())
- self._keys_func = None
- return len(self._keys)
-
-
-class LazyFullValLoadDict(LazyValDict):
-
- __slots__ = ()
-
- def __getitem__(self, key):
- if self._keys_func is not None:
- self._keys = set(self._keys_func())
- self._keys_func = None
- if key in self._vals:
- return self._vals[key]
- if key in self._keys:
- if self._val_func is not None:
- self._vals.update(self._val_func(self._keys))
- return self._vals[key]
- raise KeyError(key)
-
-
-class ProtectedDict(DictMixin):
-
- """
- Mapping wrapper storing changes to a dict without modifying the original.
-
- Changes are stored in a secondary dict, protecting the underlying
- mapping from changes.
- """
-
- __slots__ = ("orig", "new", "blacklist")
-
- def __init__(self, orig):
- self.orig = orig
- self.new = {}
- self.blacklist = {}
-
- def __setitem__(self, key, val):
- self.new[key] = val
- if key in self.blacklist:
- del self.blacklist[key]
-
- def __getitem__(self, key):
- if key in self.new:
- return self.new[key]
- if key in self.blacklist:
- raise KeyError(key)
- return self.orig[key]
-
- def __delitem__(self, key):
- if key in self.new:
- del self.new[key]
- self.blacklist[key] = True
- return
- elif key in self.orig:
- if key not in self.blacklist:
- self.blacklist[key] = True
- return
- raise KeyError(key)
-
- def iterkeys(self):
- for k in self.new.iterkeys():
- yield k
- for k in self.orig.iterkeys():
- if k not in self.blacklist and k not in self.new:
- yield k
-
- def __contains__(self, key):
- return key in self.new or (key not in self.blacklist and
- key in self.orig)
-
-
-class ImmutableDict(dict):
-
- """Immutable Dict, non changable after instantiating"""
-
- _hash_key_grabber = operator.itemgetter(0)
-
- def __delitem__(self, *args):
- raise TypeError("non modifiable")
-
- __setitem__ = clear = update = pop = popitem = setdefault = __delitem__
-
- def __hash__(self):
- k = self.items()
- k.sort(key=self._hash_key_grabber)
- return hash(tuple(k))
-
- __delattr__ = __setitem__
- __setattr__ = __setitem__
-
-
-class IndeterminantDict(object):
-
- """A wrapped dict with constant defaults, and a function for other keys."""
-
- __slots__ = ("__initial", "__pull")
-
- def __init__(self, pull_func, starter_dict=None):
- object.__init__(self)
- if starter_dict is None:
- self.__initial = {}
- else:
- self.__initial = starter_dict
- self.__pull = pull_func
-
- def __getitem__(self, key):
- if key in self.__initial:
- return self.__initial[key]
- else:
- return self.__pull(key)
-
- def get(self, key, val=None):
- try:
- return self[key]
- except KeyError:
- return val
-
- def __hash__(self):
- raise TypeError("non hashable")
-
- def __delitem__(self, *args):
- raise TypeError("non modifiable")
-
- pop = get
-
- clear = update = popitem = setdefault = __setitem__ = __delitem__
- __iter__ = keys = values = items = __len__ = __delitem__
- iteritems = iterkeys = itervalues = __delitem__
-
-
-class StackedDict(DictMixin):
-
- """A non modifiable dict that makes multiple dicts appear as one"""
-
- def __init__(self, *dicts):
- self._dicts = dicts
-
- def __getitem__(self, key):
- for x in self._dicts:
- if key in x:
- return x[key]
- raise KeyError(key)
-
- def iterkeys(self):
- s = set()
- for k in ifilterfalse(s.__contains__, chain(*map(iter, self._dicts))):
- s.add(k)
- yield k
-
- def __contains__(self, key):
- for x in self._dicts:
- if key in x:
- return True
- return False
-
- def __setitem__(self, *a):
- raise TypeError("non modifiable")
-
- __delitem__ = clear = __setitem__
-
-
-class OrderedDict(DictMixin):
-
- """Dict that preserves insertion ordering which is used for iteration ops"""
-
- __slots__ = ("_data", "_order")
-
- def __init__(self, iterable=()):
- self._order = deque()
- self._data = {}
- for k, v in iterable:
- self[k] = v
-
- def __setitem__(self, key, val):
- if key not in self:
- self._order.append(key)
- self._data[key] = val
-
- def __delitem__(self, key):
- del self._data[key]
-
- for idx, o in enumerate(self._order):
- if o == key:
- del self._order[idx]
- break
- else:
- raise AssertionError("orderdict lost its internal ordering")
-
- def __getitem__(self, key):
- return self._data[key]
-
- def __len__(self):
- return len(self._order)
-
- def iterkeys(self):
- return iter(self._order)
-
- def clear(self):
- self._order = deque()
- self._data = {}
-
- def __contains__(self, key):
- return key in self._data
-
-
-class ListBackedDict(DictMixin):
-
- __slots__ = ("_data")
- _kls = list
- _key_grabber = operator.itemgetter(0)
- _value_grabber = operator.itemgetter(1)
-
- def __init__(self, iterables=()):
- self._data = self._kls((k, v) for k, v in iterables)
-
- def __setitem__(self, key, val):
- for idx, vals in enumerate(self._data):
- if vals[0] == key:
- self._data[idx] = (key, val)
- break
- else:
- self._data.append((key, val))
-
- def __getitem__(self, key):
- for existing_key, val in self._data:
- if key == existing_key:
- return val
- raise KeyError(key)
-
- def __delitem__(self, key):
- l = self._kls((k, v) for k, v in self._data if k != key)
- if len(l) == len(self._data):
- # no match.
- raise KeyError(key)
- self._data = l
-
- def iterkeys(self):
- return imap(self._key_grabber, self._data)
-
- def itervalues(self):
- return imap(self._value_grabber, self._data)
-
- def iteritems(self):
- return iter(self._data)
-
- def __contains__(self, key):
- for k, v in self._data:
- if k == key:
- return True
- return False
-
- def __len__(self):
- return len(self._data)
-
-class TupleBackedDict(ListBackedDict):
- __slots__ = ()
- _kls = tuple
-
- def __setitem__(self, key, val):
- self._data = self._kls(
- chain((x for x in self.iteritems() if x[0] != key), ((key, val),)))
-
-
-class PreservingFoldingDict(DictMixin):
-
- """dict that uses a 'folder' function when looking up keys.
-
- The most common use for this is to implement a dict with
- case-insensitive key values (by using C{str.lower} as folder
- function).
-
- This version returns the original 'unfolded' key.
- """
-
- def __init__(self, folder, sourcedict=None):
- self._folder = folder
- # dict mapping folded keys to (original key, value)
- self._dict = {}
- if sourcedict is not None:
- self.update(sourcedict)
-
- def copy(self):
- return PreservingFoldingDict(self._folder, self.iteritems())
-
- def refold(self, folder=None):
- """Use the remembered original keys to update to a new folder.
-
- If folder is C{None}, keep the current folding function (this
- is useful if the folding function uses external data and that
- data changed).
- """
- if folder is not None:
- self._folder = folder
- oldDict = self._dict
- self._dict = {}
- for key, value in oldDict.itervalues():
- self._dict[self._folder(key)] = (key, value)
-
- def __getitem__(self, key):
- return self._dict[self._folder(key)][1]
-
- def __setitem__(self, key, value):
- self._dict[self._folder(key)] = (key, value)
-
- def __delitem__(self, key):
- del self._dict[self._folder(key)]
-
- def iteritems(self):
- return self._dict.itervalues()
-
- def iterkeys(self):
- for val in self._dict.itervalues():
- yield val[0]
-
- def itervalues(self):
- for val in self._dict.itervalues():
- yield val[1]
-
- def __contains__(self, key):
- return self._folder(key) in self._dict
-
- def __len__(self):
- return len(self._dict)
-
- def clear(self):
- self._dict = {}
-
-
-class NonPreservingFoldingDict(DictMixin):
-
- """dict that uses a 'folder' function when looking up keys.
-
- The most common use for this is to implement a dict with
- case-insensitive key values (by using C{str.lower} as folder
- function).
-
- This version returns the 'folded' key.
- """
-
- def __init__(self, folder, sourcedict=None):
- self._folder = folder
- # dict mapping folded keys to values.
- self._dict = {}
- if sourcedict is not None:
- self.update(sourcedict)
-
- def copy(self):
- return NonPreservingFoldingDict(self._folder, self.iteritems())
-
- def __getitem__(self, key):
- return self._dict[self._folder(key)]
-
- def __setitem__(self, key, value):
- self._dict[self._folder(key)] = value
-
- def __delitem__(self, key):
- del self._dict[self._folder(key)]
-
- def iterkeys(self):
- return iter(self._dict)
-
- def itervalues(self):
- return self._dict.itervalues()
-
- def iteritems(self):
- return self._dict.iteritems()
-
- def __contains__(self, key):
- return self._folder(key) in self._dict
-
- def __len__(self):
- return len(self._dict)
-
- def clear(self):
- self._dict = {}
diff --git a/snakeoil/modules.py b/snakeoil/modules.py
deleted file mode 100644
index d685001..0000000
--- a/snakeoil/modules.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright: 2005 Brian Harring <ferringb@gmail.com>
-# License: GPL2
-
-"""
-dynamic import functionality
-"""
-
-import sys
-
-class FailedImport(ImportError):
- def __init__(self, trg, e):
- ImportError.__init__(
- self, "Failed importing target '%s': '%s'" % (trg, e))
- self.trg, self.e = trg, e
-
-
-def load_module(name):
- """load 'name' module, throwing a FailedImport if __import__ fails"""
- if name in sys.modules:
- return sys.modules[name]
- try:
- m = __import__(name)
- # __import__('foo.bar') returns foo, so...
- for bit in name.split('.')[1:]:
- m = getattr(m, bit)
- return m
- except (KeyboardInterrupt, SystemExit):
- raise
- except Exception, e:
- raise FailedImport(name, e)
-
-
-def load_attribute(name):
- """load a specific attribute, rather then a module"""
- chunks = name.rsplit(".", 1)
- if len(chunks) == 1:
- raise FailedImport(name, "it isn't an attribute, it's a module")
- try:
- m = load_module(chunks[0])
- m = getattr(m, chunks[1])
- return m
- except (AttributeError, ImportError), e:
- raise FailedImport(name, e)
-
-
-def load_any(name):
- """Load a module or attribute."""
- try:
- return load_module(name)
- except FailedImport, fi:
- if not isinstance(fi.e, ImportError):
- raise
- return load_attribute(name)
diff --git a/snakeoil/obj.py b/snakeoil/obj.py
deleted file mode 100644
index 9101280..0000000
--- a/snakeoil/obj.py
+++ /dev/null
@@ -1,206 +0,0 @@
-# Copyright: 2006 Brian Harring <ferringb@gmail.com>
-# License: GPL2
-
-from operator import attrgetter
-from snakeoil.currying import pre_curry
-from snakeoil.mappings import DictMixin
-
-def alias_method(getter, self, *a, **kwd):
- return getter(self.__obj__)(*a, **kwd)
-
-def instantiate(inst):
- delayed = object.__getattribute__(inst, "__delayed__")
- obj = delayed[1](*delayed[2], **delayed[3])
- object.__setattr__(inst, "__obj__", obj)
- object.__delattr__(inst, "__delayed__")
- return obj
-
-
-# we exempt __getattribute__ since we cover it already, same
-# for __new__ and __init__
-base_kls_descriptors = frozenset(
- ('__delattr__', '__doc__', '__hash__', '__reduce__',
- '__reduce_ex__', '__repr__', '__setattr__', '__str__'))
-
-class BaseDelayedObject(object):
- """
- delay actual instantiation
- """
-
- def __new__(cls, desired_kls, func, *a, **kwd):
- o = object.__new__(cls)
- object.__setattr__(o, "__delayed__", (desired_kls, func, a, kwd))
- object.__setattr__(o, "__obj__", None)
- return o
-
- def __getattribute__(self, attr):
- obj = object.__getattribute__(self, "__obj__")
- if obj is None:
- if attr == "__class__":
- return object.__getattribute__(self, "__delayed__")[0]
-
- obj = instantiate(self)
- # now we grow some attributes.
-
- if attr == "__obj__":
- # special casing for alias_method
- return obj
- return getattr(obj, attr)
-
- # special case the normal descriptors
- for x in base_kls_descriptors:
- locals()[x] = pre_curry(alias_method, attrgetter(x))
- del x
-
-
-# note that we ignore __getattribute__; we already handle it.
-kls_descriptors = frozenset([
- # simple comparison protocol...
- '__cmp__',
- # rich comparison protocol...
- '__le__', '__lt__', '__eq__', '__ne__', '__gt__', '__ge__',
- # unicode conversion
- '__unicode__',
- # truth...
- '__nonzero__',
- # container protocol...
- '__len__', '__getitem__', '__setitem__', '__delitem__',
- '__iter__', '__contains__',
- # deprecated sequence protocol bits...
- '__getslice__', '__setslice__', '__delslice__',
- # numeric...
- '__add__', '__sub__', '__mul__', '__floordiv__', '__mod__',
- '__divmod__', '__pow__', '__lshift__', '__rshift__',
- '__and__', '__xor__', '__or__', '__div__', '__truediv__',
- '__rad__', '__rsub__', '__rmul__', '__rdiv__', '__rtruediv__',
- '__rfloordiv__', '__rmod__', '__rdivmod__', '__rpow__',
- '__rlshift__', '__rrshift__', '__rand__', '__rxor__', '__ror__',
- '__iadd__', '__isub__', '__imul__', '__idiv__', '__itruediv__',
- '__ifloordiv__', '__imod__', '__ipow__', '__ilshift__',
- '__irshift__', '__iand__', '__ixor__', '__ior__',
- '__neg__', '__pos__', '__abs__', '__invert__', '__complex__',
- '__int__', '__long__', '__float__', '__oct__', '__hex__',
- '__coerce__',
- # remaining...
- '__call__'])
-
-descriptor_overrides = dict((k, pre_curry(alias_method, attrgetter(k)))
- for k in kls_descriptors)
-
-method_cache = {}
-def make_kls(kls):
- special_descriptors = tuple(sorted(kls_descriptors.intersection(dir(kls))))
- if not special_descriptors:
- return BaseDelayedObject
- o = method_cache.get(special_descriptors, None)
- if o is None:
- class CustomDelayedObject(BaseDelayedObject):
- locals().update((k, descriptor_overrides[k])
- for k in special_descriptors)
-
- o = CustomDelayedObject
- method_cache[special_descriptors] = o
- return o
-
-def DelayedInstantiation_kls(kls, *a, **kwd):
- return DelayedInstantiation(kls, kls, *a, **kwd)
-
-class_cache = {}
-def DelayedInstantiation(resultant_kls, func, *a, **kwd):
- """Generate an objects that does not get initialized before it is used.
-
- The returned object can be passed around without triggering
- initialization. The first time it is actually used (an attribute
- is accessed) it is initialized once.
-
- The returned "fake" object cannot completely reliably mimic a
- builtin type. It will usually work but some corner cases may fail
- in confusing ways. Make sure to test if DelayedInstantiation has
- no unwanted side effects.
-
- @param resultant_kls: type object to fake an instance of.
- @param func: callable, the return value is used as initialized object.
- """
- o = class_cache.get(resultant_kls, None)
- if o is None:
- o = make_kls(resultant_kls)
- class_cache[resultant_kls] = o
- return o(resultant_kls, func, *a, **kwd)
-
-
-slotted_dict_cache = {}
-def make_SlottedDict_kls(keys):
- new_keys = tuple(sorted(keys))
- o = slotted_dict_cache.get(new_keys, None)
- if o is None:
- class SlottedDict(DictMixin):
- __slots__ = new_keys
- __externally_mutable__ = True
-
- def __init__(self, iterables=()):
- if iterables:
- self.update(iterables)
-
- __setitem__ = object.__setattr__
-
- def __getitem__(self, key):
- try:
- return getattr(self, key)
- except AttributeError:
- raise KeyError(key)
-
- def __delitem__(self, key):
- # Python does not raise anything if you delattr an
- # unset slot (works ok if __slots__ is not involved).
- try:
- getattr(self, key)
- except AttributeError:
- raise KeyError(key)
- delattr(self, key)
-
- def __iter__(self):
- for k in self.__slots__:
- if hasattr(self, k):
- yield k
-
- def iterkeys(self):
- return iter(self)
-
- def itervalues(self):
- for k in self:
- yield self[k]
-
- def get(self, key, default=None):
- return getattr(self, key, default)
-
- def pop(self, key, *a):
- # faster then the exception form...
- l = len(a)
- if l > 1:
- raise TypeError("pop accepts 1 or 2 args only")
- if hasattr(self, key):
- o = getattr(self, key)
- object.__delattr__(self, key)
- elif l:
- o = a[0]
- else:
- raise KeyError(key)
- return o
-
- def clear(self):
- for k in self:
- del self[k]
-
- def update(self, iterable):
- for k, v in iterable:
- setattr(self, k, v)
-
- def __len__(self):
- return len(self.keys())
-
- def __contains__(self, key):
- return hasattr(self, key)
-
- o = SlottedDict
- slotted_dict_cache[new_keys] = o
- return o
diff --git a/snakeoil/osutils/__init__.py b/snakeoil/osutils/__init__.py
deleted file mode 100644
index 62c1b28..0000000
--- a/snakeoil/osutils/__init__.py
+++ /dev/null
@@ -1,340 +0,0 @@
-# Copyright 2004-2007 Brian Harring <ferringb@gmail.com>
-# Copyright 2006 Marien Zwart <marienz@gentoo.org>
-# Distributed under the terms of the GNU General Public License v2
-
-"""
-os specific utilities, FS access mainly
-
-"""
-
-import os, stat
-import fcntl
-import errno
-
-__all__ = ['abspath', 'abssymlink', 'ensure_dirs', 'join', 'pjoin',
- 'listdir_files', 'listdir_dirs', 'listdir', 'readlines', 'readfile',
- 'readdir']
-
-
-# No name '_readdir' in module osutils
-# pylint: disable-msg=E0611
-
-try:
- from snakeoil.osutils import _readdir as module
-except ImportError:
- from snakeoil.osutils import native_readdir as module
-
-listdir = module.listdir
-listdir_dirs = module.listdir_dirs
-listdir_files = module.listdir_files
-readdir = module.readdir
-
-del module
-
-
-def ensure_dirs(path, gid=-1, uid=-1, mode=0777, minimal=True):
- """
- ensure dirs exist, creating as needed with (optional) gid, uid, and mode.
-
- be forewarned- if mode is specified to a mode that blocks the euid
- from accessing the dir, this code *will* try to create the dir.
- """
-
- try:
- st = os.stat(path)
- except OSError:
- base = os.path.sep
- try:
- um = os.umask(0)
- # if the dir perms would lack +wx, we have to force it
- force_temp_perms = ((mode & 0300) != 0300)
- resets = []
- apath = normpath(os.path.abspath(path))
- sticky_parent = False
-
- for directory in apath.split(os.path.sep):
- base = join(base, directory)
- try:
- st = os.stat(base)
- if not stat.S_ISDIR(st.st_mode):
- return False
-
- # if it's a subdir, we need +wx at least
- if apath != base:
- if ((st.st_mode & 0300) != 0300):
- try:
- os.chmod(base, (st.st_mode | 0300))
- except OSError:
- return False
- resets.append((base, st.st_mode))
- sticky_parent = (st.st_gid & stat.S_ISGID)
-
- except OSError:
- # nothing exists.
- try:
- if force_temp_perms:
- os.mkdir(base, 0700)
- resets.append((base, mode))
- else:
- os.mkdir(base, mode)
- if base == apath and sticky_parent:
- resets.append((base, mode))
- if gid != -1 or uid != -1:
- os.chown(base, uid, gid)
- except OSError:
- return False
-
- try:
- for base, m in reversed(resets):
- os.chmod(base, m)
- if uid != -1 or gid != -1:
- os.chown(base, uid, gid)
- except OSError:
- return False
-
- finally:
- os.umask(um)
- return True
- else:
- try:
- if ((gid != -1 and gid != st.st_gid) or
- (uid != -1 and uid != st.st_uid)):
- os.chown(path, uid, gid)
- if minimal:
- if mode != (st.st_mode & mode):
- os.chmod(path, st.st_mode | mode)
- elif mode != (st.st_mode & 07777):
- os.chmod(path, mode)
- except OSError:
- return False
- return True
-
-
-def abssymlink(symlink):
- """
- Read a symlink, resolving if it is relative, returning the absolute.
- If the path doesn't exist, OSError is thrown.
-
- @param symlink: filepath to resolve
- @return: resolve path.
- """
- mylink = os.readlink(symlink)
- if mylink[0] != '/':
- mydir = os.path.dirname(symlink)
- mylink = mydir+"/"+mylink
- return os.path.normpath(mylink)
-
-
-def abspath(path):
- """
- resolve a path absolutely, including symlink resolving.
- Throws OSError if the path doesn't exist
-
- Note that if it's a symlink and the target doesn't exist, it'll still
- return the target.
-
- @param path: filepath to resolve.
- @return: resolve path
- """
- path = os.path.abspath(path)
- try:
- return abssymlink(path)
- except OSError, e:
- if e.errno == errno.EINVAL:
- return path
- raise
-
-
-def native_normpath(mypath):
- """
- normalize path- //usr/bin becomes /usr/bin
- """
- newpath = os.path.normpath(mypath)
- if newpath.startswith('//'):
- return newpath[1:]
- return newpath
-
-native_join = os.path.join
-
-def native_readfile(mypath, none_on_missing=False):
- """
- read a file, returning the contents
-
- @param mypath: fs path for the file to read
- @param none_on_missing: whether to return None if the file is missing,
- else through the exception
- """
- try:
- return open(mypath, "r").read()
- except IOError, oe:
- if none_on_missing and oe.errno == errno.ENOENT:
- return None
- raise
-
-
-class readlines_iter(object):
- __slots__ = ("iterable", "mtime")
- def __init__(self, iterable, mtime):
- self.iterable = iterable
- self.mtime = mtime
-
- def __iter__(self):
- return self.iterable
-
-
-def native_readlines(mypath, strip_newlines=True, swallow_missing=False,
- none_on_missing=False):
- """
- read a file, yielding each line
-
- @param mypath: fs path for the file to read
- @param strip_newlines: strip trailing newlines?
- @param swallow_missing: throw an IOError if missing, or swallow it?
- @param none_on_missing: if the file is missing, return None, else
- if the file is missing return an empty iterable
- """
- try:
- f = open(mypath, "r")
- except IOError, ie:
- if ie.errno != errno.ENOENT or not swallow_missing:
- raise
- if none_on_missing:
- return None
- return readlines_iter(iter([]), None)
-
- if not strip_newlines:
- return readlines_iter(f, os.fstat(f.fileno()).st_mtime)
-
- return readlines_iter((x.strip("\n") for x in f), os.fstat(f.fileno()).st_mtime)
-
-
-try:
- from snakeoil.osutils._posix import normpath, join, readfile, readlines
-except ImportError:
- normpath = native_normpath
- join = native_join
- readfile = native_readfile
- readlines = native_readlines
-
-# convenience. importing join into a namespace is ugly, pjoin less so
-pjoin = join
-
-class LockException(Exception):
- """Base lock exception class"""
- def __init__(self, path, reason):
- Exception.__init__(self, path, reason)
- self.path, self.reason = path, reason
-
-class NonExistant(LockException):
- """Missing file/dir exception"""
- def __init__(self, path, reason=None):
- LockException.__init__(self, path, reason)
- def __str__(self):
- return (
- "Lock action for '%s' failed due to not being a valid dir/file %s"
- % (self.path, self.reason))
-
-class GenericFailed(LockException):
- """The fallback lock exception class.
-
- Covers perms, IOError's, and general whackyness.
- """
- def __str__(self):
- return "Lock action for '%s' failed due to '%s'" % (
- self.path, self.reason)
-
-
-# should the fd be left open indefinitely?
-# IMO, it shouldn't, but opening/closing everytime around is expensive
-
-
-class FsLock(object):
-
- """
- fnctl based locks
- """
-
- __slots__ = ("path", "fd", "create")
- def __init__(self, path, create=False):
- """
- @param path: fs path for the lock
- @param create: controls whether the file will be created
- if the file doesn't exist.
- If true, the base dir must exist, and it will create a file.
- If you want to lock via a dir, you have to ensure it exists
- (create doesn't suffice).
- @raise NonExistant: if no file/dir exists for that path,
- and cannot be created
- """
- self.path = path
- self.fd = None
- self.create = create
- if not create:
- if not os.path.exists(path):
- raise NonExistant(path)
-
- def _acquire_fd(self):
- if self.create:
- try:
- self.fd = os.open(self.path, os.R_OK|os.O_CREAT)
- except OSError, oe:
- raise GenericFailed(self.path, oe)
- else:
- try:
- self.fd = os.open(self.path, os.R_OK)
- except OSError, oe:
- raise NonExistant(self.path, oe)
-
- def _enact_change(self, flags, blocking):
- if self.fd is None:
- self._acquire_fd()
- # we do it this way, due to the fact try/except is a bit of a hit
- if not blocking:
- try:
- fcntl.flock(self.fd, flags|fcntl.LOCK_NB)
- except IOError, ie:
- if ie.errno == errno.EAGAIN:
- return False
- raise GenericFailed(self.path, ie)
- else:
- fcntl.flock(self.fd, flags)
- return True
-
- def acquire_write_lock(self, blocking=True):
- """
- Acquire an exclusive lock
-
- Note if you have a read lock, it implicitly upgrades atomically
-
- @param blocking: if enabled, don't return until we have the lock
- @return: True if lock is acquired, False if not.
- """
- return self._enact_change(fcntl.LOCK_EX, blocking)
-
- def acquire_read_lock(self, blocking=True):
- """
- Acquire a shared lock
-
- Note if you have a write lock, it implicitly downgrades atomically
-
- @param blocking: if enabled, don't return until we have the lock
- @return: True if lock is acquired, False if not.
- """
- return self._enact_change(fcntl.LOCK_SH, blocking)
-
- def release_write_lock(self):
- """Release an write/exclusive lock if held"""
- self._enact_change(fcntl.LOCK_UN, False)
-
- def release_read_lock(self):
- """Release an shared/read lock if held"""
- self._enact_change(fcntl.LOCK_UN, False)
-
- def __del__(self):
- # alright, it's 5:45am, yes this is weird code.
- try:
- if self.fd is not None:
- self.release_read_lock()
- finally:
- if self.fd is not None:
- os.close(self.fd)
diff --git a/snakeoil/osutils/native_readdir.py b/snakeoil/osutils/native_readdir.py
deleted file mode 100644
index c09eb1d..0000000
--- a/snakeoil/osutils/native_readdir.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# Copyright: 2006-2007 Brian Harring <ferringb@gmail.com>
-# Copyright: 2006 Marien Zwart <marienz@gentoo.org>
-# License: GPL2
-
-"""Wrapper for readdir which grabs file type from d_type."""
-
-
-import os, errno
-from stat import (S_IFDIR, S_IFREG, S_IFCHR, S_IFBLK, S_IFIFO, S_IFLNK, S_IFSOCK,
- S_IFMT, S_ISDIR, S_ISREG)
-
-listdir = os.listdir
-
-# we can still use the cpy pjoin here, just need to do something about the
-# import cycle.
-pjoin = os.path.join
-
-def stat_swallow_enoent(path, check, default=False, stat=os.stat):
- try:
- return check(stat(path).st_mode)
- except OSError, oe:
- if oe.errno == errno.ENOENT:
- return default
- raise
-
-def listdir_dirs(path, followSymlinks=True):
- scheck = S_ISDIR
- pjf = pjoin
- lstat = os.lstat
- if followSymlinks:
- return [x for x in os.listdir(path) if
- stat_swallow_enoent(pjf(path, x), scheck)]
- lstat = os.lstat
- return [x for x in os.listdir(path) if
- scheck(lstat(pjf(path, x)).st_mode)]
-
-def listdir_files(path, followSymlinks=True):
- scheck = S_ISREG
- pjf = pjoin
- if followSymlinks:
- return [x for x in os.listdir(path) if
- stat_swallow_enoent(pjf(path, x), scheck)]
- lstat = os.lstat
- return [x for x in os.listdir(path) if
- scheck(lstat(pjf(path, x)).st_mode)]
-
-def readdir(path):
- pjf = pjoin
- assocs = {
- S_IFREG: "file",
- S_IFDIR: "directory",
- S_IFLNK: "symlink",
- S_IFCHR: "chardev",
- S_IFBLK: "block",
- S_IFSOCK: "socket",
- S_IFIFO: "fifo",
- }
- things = listdir(path)
- lstat = os.lstat
- return [(name, assocs[S_IFMT(lstat(pjf(path, name)).st_mode)]) for name in things]
diff --git a/snakeoil/pickling.py b/snakeoil/pickling.py
deleted file mode 100644
index fe1b39f..0000000
--- a/snakeoil/pickling.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright: 2007 Brian Harring <ferringb@gmail.com>
-# License: GPL2
-
-"""
-convenience module using cPickle if available, else failing back to pickle
-"""
-
-try:
- from cPickle import *
-except ImportError:
- from pickle import *
-
-def iter_stream(stream):
- try:
- while True:
- yield load(stream)
- except EOFError:
- pass
diff --git a/snakeoil/tar.py b/snakeoil/tar.py
deleted file mode 100644
index 8f581b1..0000000
--- a/snakeoil/tar.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright: 2006 Brian Harring <ferringb@gmail.com>
-# License: GPL2
-
-"""
-tar file access
-
-monkey patching of stdlib tarfile to reduce mem usage (33% reduction).
-
-note this is also racey; N threads trying an import, if they're after
-the *original* tarfile, they may inadvertantly get ours.
-"""
-
-import sys
-t = sys.modules.pop("tarfile", None)
-tarfile = __import__("tarfile")
-if t is not None:
- sys.modules["tarfile"] = t
-else:
- del sys.modules["tarfile"]
-del t
-# ok, we now have our own local copy to monkey patch
-
-class TarInfo(tarfile.TarInfo):
- __slots__ = (
- "name", "mode", "uid", "gid", "size", "mtime", "chksum", "type",
- "linkname", "uname", "gname", "devmajor", "devminor", "prefix",
- "offset", "offset_data", "buf", "sparse", "_link_target")
-
-tarfile.TarInfo = TarInfo
-# finished monkey patching. now to lift things out of our tarfile
-# module into this scope so from/import behaves properly.
-
-for x in tarfile.__all__:
- locals()[x] = getattr(tarfile, x)
-del x
diff --git a/snakeoil/version.py b/snakeoil/version.py
deleted file mode 100644
index 4c48c5f..0000000
--- a/snakeoil/version.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright: 2006 Marien Zwart <marienz@gentoo.org>
-# License: GPL2
-
-
-"""Version information (tied to bzr)."""
-
-import os
-
-__version__ = '0.1_rc2'
-
-_ver = None
-
-def get_version():
- """@returns: a string describing the snakeoil version."""
- global _ver
- if _ver is not None:
- return _ver
-
- try:
- from snakeoil.bzr_verinfo import version_info
- except ImportError:
- try:
- from bzrlib import branch, errors
- except ImportError:
- ver = 'unknown (not from an sdist tarball, bzr unavailable)'
- else:
- try:
- # Returns a (branch, relpath) tuple, ignore relpath.
- b = branch.Branch.open_containing(os.path.realpath(__file__))[0]
- except errors.NotBranchError:
- ver = 'unknown (not from an sdist tarball, not a bzr branch)'
- else:
- ver = '%s:%s %s' % (b.nick, b.revno(), b.last_revision())
- else:
- ver = '%(branch_nick)s:%(revno)s %(revision_id)s' % version_info
-
- _ver = 'snakeoil %s\n(bzr rev %s)' % (__version__, ver)
-
- return _ver
diff --git a/snakeoil/weakrefs.py b/snakeoil/weakrefs.py
deleted file mode 100644
index 16336be..0000000
--- a/snakeoil/weakrefs.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright: 2006 Brian Harring <ferringb@gmail.com>
-# License: GPL2
-
-# Unused import
-# pylint: disable-msg=W0611
-
-try:
- # No name in module
- # pylint: disable-msg=E0611
- from snakeoil._caching import WeakValCache
-except ImportError:
- from weakref import WeakValueDictionary as WeakValCache
diff --git a/snakeoil/xml/__init__.py b/snakeoil/xml/__init__.py
deleted file mode 100644
index 4091f02..0000000
--- a/snakeoil/xml/__init__.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright: 2006 Brian Harring <ferringb@gmail.com>
-# License: GPL2
-
-"""
-indirection to load ElementTree
-"""
-# essentially... prefer cElementTree, then 2.5 bundled, then
-# elementtree, then 2.5 bundled, then our own bundled
-
-# "No name etree in module xml", "Reimport cElementTree"
-# pylint: disable-msg=E0611,W0404
-
-gotit = True
-try:
- import cElementTree as etree
-except ImportError:
- gotit = False
-if not gotit:
- try:
- from xml.etree import cElementTree as etree
- gotit = True
- except ImportError:
- pass
-if not gotit:
- try:
- from elementtree import ElementTree as etree
- gotit = True
- except ImportError:
- pass
-if not gotit:
- try:
- from xml.etree import ElementTree as etree
- gotit = True
- except ImportError:
- pass
-
-if not gotit:
- from snakeoil.xml import bundled_elementtree as etree
-del gotit
-
-def escape(string):
- """
- simple escaping of &, <, and >
- """
- return string.replace("&", "&amp;").replace("<", "&lt;").replace(">",
- "&gt;")
diff --git a/snakeoil/xml/bundled_elementtree.py b/snakeoil/xml/bundled_elementtree.py
deleted file mode 100644
index 5d8b1d3..0000000
--- a/snakeoil/xml/bundled_elementtree.py
+++ /dev/null
@@ -1,1254 +0,0 @@
-#
-# ElementTree
-# $Id: ElementTree.py 2326 2005-03-17 07:45:21Z fredrik $
-#
-# light-weight XML support for Python 1.5.2 and later.
-#
-# history:
-# 2001-10-20 fl created (from various sources)
-# 2001-11-01 fl return root from parse method
-# 2002-02-16 fl sort attributes in lexical order
-# 2002-04-06 fl TreeBuilder refactoring, added PythonDoc markup
-# 2002-05-01 fl finished TreeBuilder refactoring
-# 2002-07-14 fl added basic namespace support to ElementTree.write
-# 2002-07-25 fl added QName attribute support
-# 2002-10-20 fl fixed encoding in write
-# 2002-11-24 fl changed default encoding to ascii; fixed attribute encoding
-# 2002-11-27 fl accept file objects or file names for parse/write
-# 2002-12-04 fl moved XMLTreeBuilder back to this module
-# 2003-01-11 fl fixed entity encoding glitch for us-ascii
-# 2003-02-13 fl added XML literal factory
-# 2003-02-21 fl added ProcessingInstruction/PI factory
-# 2003-05-11 fl added tostring/fromstring helpers
-# 2003-05-26 fl added ElementPath support
-# 2003-07-05 fl added makeelement factory method
-# 2003-07-28 fl added more well-known namespace prefixes
-# 2003-08-15 fl fixed typo in ElementTree.findtext (Thomas Dartsch)
-# 2003-09-04 fl fall back on emulator if ElementPath is not installed
-# 2003-10-31 fl markup updates
-# 2003-11-15 fl fixed nested namespace bug
-# 2004-03-28 fl added XMLID helper
-# 2004-06-02 fl added default support to findtext
-# 2004-06-08 fl fixed encoding of non-ascii element/attribute names
-# 2004-08-23 fl take advantage of post-2.1 expat features
-# 2005-02-01 fl added iterparse implementation
-# 2005-03-02 fl fixed iterparse support for pre-2.2 versions
-#
-# Copyright (c) 1999-2005 by Fredrik Lundh. All rights reserved.
-#
-# fredrik@pythonware.com
-# http://www.pythonware.com
-#
-# --------------------------------------------------------------------
-# The ElementTree toolkit is
-#
-# Copyright (c) 1999-2005 by Fredrik Lundh
-#
-# By obtaining, using, and/or copying this software and/or its
-# associated documentation, you agree that you have read, understood,
-# and will comply with the following terms and conditions:
-#
-# Permission to use, copy, modify, and distribute this software and
-# its associated documentation for any purpose and without fee is
-# hereby granted, provided that the above copyright notice appears in
-# all copies, and that both that copyright notice and this permission
-# notice appear in supporting documentation, and that the name of
-# Secret Labs AB or the author not be used in advertising or publicity
-# pertaining to distribution of the software without specific, written
-# prior permission.
-#
-# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
-# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
-# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
-# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
-# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
-# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
-# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
-# OF THIS SOFTWARE.
-# --------------------------------------------------------------------
-
-__all__ = [
- # public symbols
- "Comment",
- "dump",
- "Element", "ElementTree",
- "fromstring",
- "iselement", "iterparse",
- "parse",
- "PI", "ProcessingInstruction",
- "QName",
- "SubElement",
- "tostring",
- "TreeBuilder",
- "VERSION", "XML",
- "XMLTreeBuilder",
- ]
-
-##
-# The <b>Element</b> type is a flexible container object, designed to
-# store hierarchical data structures in memory. The type can be
-# described as a cross between a list and a dictionary.
-# <p>
-# Each element has a number of properties associated with it:
-# <ul>
-# <li>a <i>tag</i>. This is a string identifying what kind of data
-# this element represents (the element type, in other words).</li>
-# <li>a number of <i>attributes</i>, stored in a Python dictionary.</li>
-# <li>a <i>text</i> string.</li>
-# <li>an optional <i>tail</i> string.</li>
-# <li>a number of <i>child elements</i>, stored in a Python sequence</li>
-# </ul>
-#
-# To create an element instance, use the {@link #Element} or {@link
-# #SubElement} factory functions.
-# <p>
-# The {@link #ElementTree} class can be used to wrap an element
-# structure, and convert it from and to XML.
-##
-
-import string, sys, re
-
-class _SimpleElementPath:
- # emulate pre-1.2 find/findtext/findall behaviour
- def find(self, element, tag):
- for elem in element:
- if elem.tag == tag:
- return elem
- return None
- def findtext(self, element, tag, default=None):
- for elem in element:
- if elem.tag == tag:
- return elem.text or ""
- return default
- def findall(self, element, tag):
- if tag[:3] == ".//":
- return element.getiterator(tag[3:])
- result = []
- for elem in element:
- if elem.tag == tag:
- result.append(elem)
- return result
-
-try:
- import ElementPath
-except ImportError:
- # FIXME: issue warning in this case?
- ElementPath = _SimpleElementPath()
-
-# TODO: add support for custom namespace resolvers/default namespaces
-# TODO: add improved support for incremental parsing
-
-VERSION = "1.2.6"
-
-##
-# Internal element class. This class defines the Element interface,
-# and provides a reference implementation of this interface.
-# <p>
-# You should not create instances of this class directly. Use the
-# appropriate factory functions instead, such as {@link #Element}
-# and {@link #SubElement}.
-#
-# @see Element
-# @see SubElement
-# @see Comment
-# @see ProcessingInstruction
-
-class _ElementInterface:
- # <tag attrib>text<child/>...</tag>tail
-
- ##
- # (Attribute) Element tag.
-
- tag = None
-
- ##
- # (Attribute) Element attribute dictionary. Where possible, use
- # {@link #_ElementInterface.get},
- # {@link #_ElementInterface.set},
- # {@link #_ElementInterface.keys}, and
- # {@link #_ElementInterface.items} to access
- # element attributes.
-
- attrib = None
-
- ##
- # (Attribute) Text before first subelement. This is either a
- # string or the value None, if there was no text.
-
- text = None
-
- ##
- # (Attribute) Text after this element's end tag, but before the
- # next sibling element's start tag. This is either a string or
- # the value None, if there was no text.
-
- tail = None # text after end tag, if any
-
- def __init__(self, tag, attrib):
- self.tag = tag
- self.attrib = attrib
- self._children = []
-
- def __repr__(self):
- return "<Element %s at %x>" % (self.tag, id(self))
-
- ##
- # Creates a new element object of the same type as this element.
- #
- # @param tag Element tag.
- # @param attrib Element attributes, given as a dictionary.
- # @return A new element instance.
-
- def makeelement(self, tag, attrib):
- return Element(tag, attrib)
-
- ##
- # Returns the number of subelements.
- #
- # @return The number of subelements.
-
- def __len__(self):
- return len(self._children)
-
- ##
- # Returns the given subelement.
- #
- # @param index What subelement to return.
- # @return The given subelement.
- # @exception IndexError If the given element does not exist.
-
- def __getitem__(self, index):
- return self._children[index]
-
- ##
- # Replaces the given subelement.
- #
- # @param index What subelement to replace.
- # @param element The new element value.
- # @exception IndexError If the given element does not exist.
- # @exception AssertionError If element is not a valid object.
-
- def __setitem__(self, index, element):
- assert iselement(element)
- self._children[index] = element
-
- ##
- # Deletes the given subelement.
- #
- # @param index What subelement to delete.
- # @exception IndexError If the given element does not exist.
-
- def __delitem__(self, index):
- del self._children[index]
-
- ##
- # Returns a list containing subelements in the given range.
- #
- # @param start The first subelement to return.
- # @param stop The first subelement that shouldn't be returned.
- # @return A sequence object containing subelements.
-
- def __getslice__(self, start, stop):
- return self._children[start:stop]
-
- ##
- # Replaces a number of subelements with elements from a sequence.
- #
- # @param start The first subelement to replace.
- # @param stop The first subelement that shouldn't be replaced.
- # @param elements A sequence object with zero or more elements.
- # @exception AssertionError If a sequence member is not a valid object.
-
- def __setslice__(self, start, stop, elements):
- for element in elements:
- assert iselement(element)
- self._children[start:stop] = list(elements)
-
- ##
- # Deletes a number of subelements.
- #
- # @param start The first subelement to delete.
- # @param stop The first subelement to leave in there.
-
- def __delslice__(self, start, stop):
- del self._children[start:stop]
-
- ##
- # Adds a subelement to the end of this element.
- #
- # @param element The element to add.
- # @exception AssertionError If a sequence member is not a valid object.
-
- def append(self, element):
- assert iselement(element)
- self._children.append(element)
-
- ##
- # Inserts a subelement at the given position in this element.
- #
- # @param index Where to insert the new subelement.
- # @exception AssertionError If the element is not a valid object.
-
- def insert(self, index, element):
- assert iselement(element)
- self._children.insert(index, element)
-
- ##
- # Removes a matching subelement. Unlike the <b>find</b> methods,
- # this method compares elements based on identity, not on tag
- # value or contents.
- #
- # @param element What element to remove.
- # @exception ValueError If a matching element could not be found.
- # @exception AssertionError If the element is not a valid object.
-
- def remove(self, element):
- assert iselement(element)
- self._children.remove(element)
-
- ##
- # Returns all subelements. The elements are returned in document
- # order.
- #
- # @return A list of subelements.
- # @defreturn list of Element instances
-
- def getchildren(self):
- return self._children
-
- ##
- # Finds the first matching subelement, by tag name or path.
- #
- # @param path What element to look for.
- # @return The first matching element, or None if no element was found.
- # @defreturn Element or None
-
- def find(self, path):
- return ElementPath.find(self, path)
-
- ##
- # Finds text for the first matching subelement, by tag name or path.
- #
- # @param path What element to look for.
- # @param default What to return if the element was not found.
- # @return The text content of the first matching element, or the
- # default value no element was found. Note that if the element
- # has is found, but has no text content, this method returns an
- # empty string.
- # @defreturn string
-
- def findtext(self, path, default=None):
- return ElementPath.findtext(self, path, default)
-
- ##
- # Finds all matching subelements, by tag name or path.
- #
- # @param path What element to look for.
- # @return A list or iterator containing all matching elements,
- # in document order.
- # @defreturn list of Element instances
-
- def findall(self, path):
- return ElementPath.findall(self, path)
-
- ##
- # Resets an element. This function removes all subelements, clears
- # all attributes, and sets the text and tail attributes to None.
-
- def clear(self):
- self.attrib.clear()
- self._children = []
- self.text = self.tail = None
-
- ##
- # Gets an element attribute.
- #
- # @param key What attribute to look for.
- # @param default What to return if the attribute was not found.
- # @return The attribute value, or the default value, if the
- # attribute was not found.
- # @defreturn string or None
-
- def get(self, key, default=None):
- return self.attrib.get(key, default)
-
- ##
- # Sets an element attribute.
- #
- # @param key What attribute to set.
- # @param value The attribute value.
-
- def set(self, key, value):
- self.attrib[key] = value
-
- ##
- # Gets a list of attribute names. The names are returned in an
- # arbitrary order (just like for an ordinary Python dictionary).
- #
- # @return A list of element attribute names.
- # @defreturn list of strings
-
- def keys(self):
- return self.attrib.keys()
-
- ##
- # Gets element attributes, as a sequence. The attributes are
- # returned in an arbitrary order.
- #
- # @return A list of (name, value) tuples for all attributes.
- # @defreturn list of (string, string) tuples
-
- def items(self):
- return self.attrib.items()
-
- ##
- # Creates a tree iterator. The iterator loops over this element
- # and all subelements, in document order, and returns all elements
- # with a matching tag.
- # <p>
- # If the tree structure is modified during iteration, the result
- # is undefined.
- #
- # @param tag What tags to look for (default is to return all elements).
- # @return A list or iterator containing all the matching elements.
- # @defreturn list or iterator
-
- def getiterator(self, tag=None):
- nodes = []
- if tag == "*":
- tag = None
- if tag is None or self.tag == tag:
- nodes.append(self)
- for node in self._children:
- nodes.extend(node.getiterator(tag))
- return nodes
-
-# compatibility
-_Element = _ElementInterface
-
-##
-# Element factory. This function returns an object implementing the
-# standard Element interface. The exact class or type of that object
-# is implementation dependent, but it will always be compatible with
-# the {@link #_ElementInterface} class in this module.
-# <p>
-# The element name, attribute names, and attribute values can be
-# either 8-bit ASCII strings or Unicode strings.
-#
-# @param tag The element name.
-# @param attrib An optional dictionary, containing element attributes.
-# @param **extra Additional attributes, given as keyword arguments.
-# @return An element instance.
-# @defreturn Element
-
-def Element(tag, attrib={}, **extra):
- attrib = attrib.copy()
- attrib.update(extra)
- return _ElementInterface(tag, attrib)
-
-##
-# Subelement factory. This function creates an element instance, and
-# appends it to an existing element.
-# <p>
-# The element name, attribute names, and attribute values can be
-# either 8-bit ASCII strings or Unicode strings.
-#
-# @param parent The parent element.
-# @param tag The subelement name.
-# @param attrib An optional dictionary, containing element attributes.
-# @param **extra Additional attributes, given as keyword arguments.
-# @return An element instance.
-# @defreturn Element
-
-def SubElement(parent, tag, attrib={}, **extra):
- attrib = attrib.copy()
- attrib.update(extra)
- element = parent.makeelement(tag, attrib)
- parent.append(element)
- return element
-
-##
-# Comment element factory. This factory function creates a special
-# element that will be serialized as an XML comment.
-# <p>
-# The comment string can be either an 8-bit ASCII string or a Unicode
-# string.
-#
-# @param text A string containing the comment string.
-# @return An element instance, representing a comment.
-# @defreturn Element
-
-def Comment(text=None):
- element = Element(Comment)
- element.text = text
- return element
-
-##
-# PI element factory. This factory function creates a special element
-# that will be serialized as an XML processing instruction.
-#
-# @param target A string containing the PI target.
-# @param text A string containing the PI contents, if any.
-# @return An element instance, representing a PI.
-# @defreturn Element
-
-def ProcessingInstruction(target, text=None):
- element = Element(ProcessingInstruction)
- element.text = target
- if text:
- element.text = element.text + " " + text
- return element
-
-PI = ProcessingInstruction
-
-##
-# QName wrapper. This can be used to wrap a QName attribute value, in
-# order to get proper namespace handling on output.
-#
-# @param text A string containing the QName value, in the form {uri}local,
-# or, if the tag argument is given, the URI part of a QName.
-# @param tag Optional tag. If given, the first argument is interpreted as
-# an URI, and this argument is interpreted as a local name.
-# @return An opaque object, representing the QName.
-
-class QName:
- def __init__(self, text_or_uri, tag=None):
- if tag:
- text_or_uri = "{%s}%s" % (text_or_uri, tag)
- self.text = text_or_uri
- def __str__(self):
- return self.text
- def __hash__(self):
- return hash(self.text)
- def __cmp__(self, other):
- if isinstance(other, QName):
- return cmp(self.text, other.text)
- return cmp(self.text, other)
-
-##
-# ElementTree wrapper class. This class represents an entire element
-# hierarchy, and adds some extra support for serialization to and from
-# standard XML.
-#
-# @param element Optional root element.
-# @keyparam file Optional file handle or name. If given, the
-# tree is initialized with the contents of this XML file.
-
-class ElementTree:
-
- def __init__(self, element=None, file=None):
- assert element is None or iselement(element)
- self._root = element # first node
- if file:
- self.parse(file)
-
- ##
- # Gets the root element for this tree.
- #
- # @return An element instance.
- # @defreturn Element
-
- def getroot(self):
- return self._root
-
- ##
- # Replaces the root element for this tree. This discards the
- # current contents of the tree, and replaces it with the given
- # element. Use with care.
- #
- # @param element An element instance.
-
- def _setroot(self, element):
- assert iselement(element)
- self._root = element
-
- ##
- # Loads an external XML document into this element tree.
- #
- # @param source A file name or file object.
- # @param parser An optional parser instance. If not given, the
- # standard {@link XMLTreeBuilder} parser is used.
- # @return The document root element.
- # @defreturn Element
-
- def parse(self, source, parser=None):
- if not hasattr(source, "read"):
- source = open(source, "rb")
- if not parser:
- parser = XMLTreeBuilder()
- while 1:
- data = source.read(32768)
- if not data:
- break
- parser.feed(data)
- self._root = parser.close()
- return self._root
-
- ##
- # Creates a tree iterator for the root element. The iterator loops
- # over all elements in this tree, in document order.
- #
- # @param tag What tags to look for (default is to return all elements)
- # @return An iterator.
- # @defreturn iterator
-
- def getiterator(self, tag=None):
- assert self._root is not None
- return self._root.getiterator(tag)
-
- ##
- # Finds the first toplevel element with given tag.
- # Same as getroot().find(path).
- #
- # @param path What element to look for.
- # @return The first matching element, or None if no element was found.
- # @defreturn Element or None
-
- def find(self, path):
- assert self._root is not None
- if path[:1] == "/":
- path = "." + path
- return self._root.find(path)
-
- ##
- # Finds the element text for the first toplevel element with given
- # tag. Same as getroot().findtext(path).
- #
- # @param path What toplevel element to look for.
- # @param default What to return if the element was not found.
- # @return The text content of the first matching element, or the
- # default value no element was found. Note that if the element
- # has is found, but has no text content, this method returns an
- # empty string.
- # @defreturn string
-
- def findtext(self, path, default=None):
- assert self._root is not None
- if path[:1] == "/":
- path = "." + path
- return self._root.findtext(path, default)
-
- ##
- # Finds all toplevel elements with the given tag.
- # Same as getroot().findall(path).
- #
- # @param path What element to look for.
- # @return A list or iterator containing all matching elements,
- # in document order.
- # @defreturn list of Element instances
-
- def findall(self, path):
- assert self._root is not None
- if path[:1] == "/":
- path = "." + path
- return self._root.findall(path)
-
- ##
- # Writes the element tree to a file, as XML.
- #
- # @param file A file name, or a file object opened for writing.
- # @param encoding Optional output encoding (default is US-ASCII).
-
- def write(self, file, encoding="us-ascii"):
- assert self._root is not None
- if not hasattr(file, "write"):
- file = open(file, "wb")
- if not encoding:
- encoding = "us-ascii"
- elif encoding != "utf-8" and encoding != "us-ascii":
- file.write("<?xml version='1.0' encoding='%s'?>\n" % encoding)
- self._write(file, self._root, encoding, {})
-
- def _write(self, file, node, encoding, namespaces):
- # write XML to file
- tag = node.tag
- if tag is Comment:
- file.write("<!-- %s -->" % _escape_cdata(node.text, encoding))
- elif tag is ProcessingInstruction:
- file.write("<?%s?>" % _escape_cdata(node.text, encoding))
- else:
- items = node.items()
- xmlns_items = [] # new namespaces in this scope
- try:
- if isinstance(tag, QName) or tag[:1] == "{":
- tag, xmlns = fixtag(tag, namespaces)
- if xmlns: xmlns_items.append(xmlns)
- except TypeError:
- _raise_serialization_error(tag)
- file.write("<" + _encode(tag, encoding))
- if items or xmlns_items:
- items.sort() # lexical order
- for k, v in items:
- try:
- if isinstance(k, QName) or k[:1] == "{":
- k, xmlns = fixtag(k, namespaces)
- if xmlns: xmlns_items.append(xmlns)
- except TypeError:
- _raise_serialization_error(k)
- try:
- if isinstance(v, QName):
- v, xmlns = fixtag(v, namespaces)
- if xmlns: xmlns_items.append(xmlns)
- except TypeError:
- _raise_serialization_error(v)
- file.write(" %s=\"%s\"" % (_encode(k, encoding),
- _escape_attrib(v, encoding)))
- for k, v in xmlns_items:
- file.write(" %s=\"%s\"" % (_encode(k, encoding),
- _escape_attrib(v, encoding)))
- if node.text or len(node):
- file.write(">")
- if node.text:
- file.write(_escape_cdata(node.text, encoding))
- for n in node:
- self._write(file, n, encoding, namespaces)
- file.write("</" + _encode(tag, encoding) + ">")
- else:
- file.write(" />")
- for k, v in xmlns_items:
- del namespaces[v]
- if node.tail:
- file.write(_escape_cdata(node.tail, encoding))
-
-# --------------------------------------------------------------------
-# helpers
-
-##
-# Checks if an object appears to be a valid element object.
-#
-# @param An element instance.
-# @return A true value if this is an element object.
-# @defreturn flag
-
-def iselement(element):
- # FIXME: not sure about this; might be a better idea to look
- # for tag/attrib/text attributes
- return isinstance(element, _ElementInterface) or hasattr(element, "tag")
-
-##
-# Writes an element tree or element structure to sys.stdout. This
-# function should be used for debugging only.
-# <p>
-# The exact output format is implementation dependent. In this
-# version, it's written as an ordinary XML file.
-#
-# @param elem An element tree or an individual element.
-
-def dump(elem):
- # debugging
- if not isinstance(elem, ElementTree):
- elem = ElementTree(elem)
- elem.write(sys.stdout)
- tail = elem.getroot().tail
- if not tail or tail[-1] != "\n":
- sys.stdout.write("\n")
-
-def _encode(s, encoding):
- try:
- return s.encode(encoding)
- except AttributeError:
- return s # 1.5.2: assume the string uses the right encoding
-
-if sys.version[:3] == "1.5":
- _escape = re.compile(r"[&<>\"\x80-\xff]+") # 1.5.2
-else:
- _escape = re.compile(eval(r'u"[&<>\"\u0080-\uffff]+"'))
-
-_escape_map = {
- "&": "&amp;",
- "<": "&lt;",
- ">": "&gt;",
- '"': "&quot;",
-}
-
-_namespace_map = {
- # "well-known" namespace prefixes
- "http://www.w3.org/XML/1998/namespace": "xml",
- "http://www.w3.org/1999/xhtml": "html",
- "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf",
- "http://schemas.xmlsoap.org/wsdl/": "wsdl",
-}
-
-def _raise_serialization_error(text):
- raise TypeError(
- "cannot serialize %r (type %s)" % (text, type(text).__name__)
- )
-
-def _encode_entity(text, pattern=_escape):
- # map reserved and non-ascii characters to numerical entities
- def escape_entities(m, map=_escape_map):
- out = []
- append = out.append
- for char in m.group():
- text = map.get(char)
- if text is None:
- text = "&#%d;" % ord(char)
- append(text)
- return string.join(out, "")
- try:
- return _encode(pattern.sub(escape_entities, text), "ascii")
- except TypeError:
- _raise_serialization_error(text)
-
-#
-# the following functions assume an ascii-compatible encoding
-# (or "utf-16")
-
-def _escape_cdata(text, encoding=None, replace=string.replace):
- # escape character data
- try:
- if encoding:
- try:
- text = _encode(text, encoding)
- except UnicodeError:
- return _encode_entity(text)
- text = replace(text, "&", "&amp;")
- text = replace(text, "<", "&lt;")
- text = replace(text, ">", "&gt;")
- return text
- except (TypeError, AttributeError):
- _raise_serialization_error(text)
-
-def _escape_attrib(text, encoding=None, replace=string.replace):
- # escape attribute value
- try:
- if encoding:
- try:
- text = _encode(text, encoding)
- except UnicodeError:
- return _encode_entity(text)
- text = replace(text, "&", "&amp;")
- text = replace(text, "'", "&apos;") # FIXME: overkill
- text = replace(text, "\"", "&quot;")
- text = replace(text, "<", "&lt;")
- text = replace(text, ">", "&gt;")
- return text
- except (TypeError, AttributeError):
- _raise_serialization_error(text)
-
-def fixtag(tag, namespaces):
- # given a decorated tag (of the form {uri}tag), return prefixed
- # tag and namespace declaration, if any
- if isinstance(tag, QName):
- tag = tag.text
- namespace_uri, tag = string.split(tag[1:], "}", 1)
- prefix = namespaces.get(namespace_uri)
- if prefix is None:
- prefix = _namespace_map.get(namespace_uri)
- if prefix is None:
- prefix = "ns%d" % len(namespaces)
- namespaces[namespace_uri] = prefix
- if prefix == "xml":
- xmlns = None
- else:
- xmlns = ("xmlns:%s" % prefix, namespace_uri)
- else:
- xmlns = None
- return "%s:%s" % (prefix, tag), xmlns
-
-##
-# Parses an XML document into an element tree.
-#
-# @param source A filename or file object containing XML data.
-# @param parser An optional parser instance. If not given, the
-# standard {@link XMLTreeBuilder} parser is used.
-# @return An ElementTree instance
-
-def parse(source, parser=None):
- tree = ElementTree()
- tree.parse(source, parser)
- return tree
-
-##
-# Parses an XML document into an element tree incrementally, and reports
-# what's going on to the user.
-#
-# @param source A filename or file object containing XML data.
-# @param events A list of events to report back. If omitted, only "end"
-# events are reported.
-# @return A (event, elem) iterator.
-
-class iterparse:
-
- def __init__(self, source, events=None):
- if not hasattr(source, "read"):
- source = open(source, "rb")
- self._file = source
- self._events = []
- self._index = 0
- self.root = self._root = None
- self._parser = XMLTreeBuilder()
- # wire up the parser for event reporting
- parser = self._parser._parser
- append = self._events.append
- if events is None:
- events = ["end"]
- for event in events:
- if event == "start":
- try:
- parser.ordered_attributes = 1
- parser.specified_attributes = 1
- def handler(tag, attrib_in, event=event, append=append,
- start=self._parser._start_list):
- append((event, start(tag, attrib_in)))
- parser.StartElementHandler = handler
- except AttributeError:
- def handler(tag, attrib_in, event=event, append=append,
- start=self._parser._start):
- append((event, start(tag, attrib_in)))
- parser.StartElementHandler = handler
- elif event == "end":
- def handler(tag, event=event, append=append,
- end=self._parser._end):
- append((event, end(tag)))
- parser.EndElementHandler = handler
- elif event == "start-ns":
- def handler(prefix, uri, event=event, append=append):
- try:
- uri = _encode(uri, "ascii")
- except UnicodeError:
- pass
- append((event, (prefix or "", uri)))
- parser.StartNamespaceDeclHandler = handler
- elif event == "end-ns":
- def handler(prefix, event=event, append=append):
- append((event, None))
- parser.EndNamespaceDeclHandler = handler
-
- def next(self):
- while 1:
- try:
- item = self._events[self._index]
- except IndexError:
- if self._parser is None:
- self.root = self._root
- try:
- raise StopIteration
- except NameError:
- raise IndexError
- # load event buffer
- del self._events[:]
- self._index = 0
- data = self._file.read(16384)
- if data:
- self._parser.feed(data)
- else:
- self._root = self._parser.close()
- self._parser = None
- else:
- self._index = self._index + 1
- return item
-
- try:
- iter
- def __iter__(self):
- return self
- except NameError:
- def __getitem__(self, index):
- return self.next()
-
-##
-# Parses an XML document from a string constant. This function can
-# be used to embed "XML literals" in Python code.
-#
-# @param source A string containing XML data.
-# @return An Element instance.
-# @defreturn Element
-
-def XML(text):
- parser = XMLTreeBuilder()
- parser.feed(text)
- return parser.close()
-
-##
-# Parses an XML document from a string constant, and also returns
-# a dictionary which maps from element id:s to elements.
-#
-# @param source A string containing XML data.
-# @return A tuple containing an Element instance and a dictionary.
-# @defreturn (Element, dictionary)
-
-def XMLID(text):
- parser = XMLTreeBuilder()
- parser.feed(text)
- tree = parser.close()
- ids = {}
- for elem in tree.getiterator():
- id = elem.get("id")
- if id:
- ids[id] = elem
- return tree, ids
-
-##
-# Parses an XML document from a string constant. Same as {@link #XML}.
-#
-# @def fromstring(text)
-# @param source A string containing XML data.
-# @return An Element instance.
-# @defreturn Element
-
-fromstring = XML
-
-##
-# Generates a string representation of an XML element, including all
-# subelements.
-#
-# @param element An Element instance.
-# @return An encoded string containing the XML data.
-# @defreturn string
-
-def tostring(element, encoding=None):
- class dummy:
- pass
- data = []
- file = dummy()
- file.write = data.append
- ElementTree(element).write(file, encoding)
- return string.join(data, "")
-
-##
-# Generic element structure builder. This builder converts a sequence
-# of {@link #TreeBuilder.start}, {@link #TreeBuilder.data}, and {@link
-# #TreeBuilder.end} method calls to a well-formed element structure.
-# <p>
-# You can use this class to build an element structure using a custom XML
-# parser, or a parser for some other XML-like format.
-#
-# @param element_factory Optional element factory. This factory
-# is called to create new Element instances, as necessary.
-
-class TreeBuilder:
-
- def __init__(self, element_factory=None):
- self._data = [] # data collector
- self._elem = [] # element stack
- self._last = None # last element
- self._tail = None # true if we're after an end tag
- if element_factory is None:
- element_factory = _ElementInterface
- self._factory = element_factory
-
- ##
- # Flushes the parser buffers, and returns the toplevel documen
- # element.
- #
- # @return An Element instance.
- # @defreturn Element
-
- def close(self):
- assert len(self._elem) == 0, "missing end tags"
- assert self._last is not None, "missing toplevel element"
- return self._last
-
- def _flush(self):
- if self._data:
- if self._last is not None:
- text = string.join(self._data, "")
- if self._tail:
- assert self._last.tail is None, "internal error (tail)"
- self._last.tail = text
- else:
- assert self._last.text is None, "internal error (text)"
- self._last.text = text
- self._data = []
-
- ##
- # Adds text to the current element.
- #
- # @param data A string. This should be either an 8-bit string
- # containing ASCII text, or a Unicode string.
-
- def data(self, data):
- self._data.append(data)
-
- ##
- # Opens a new element.
- #
- # @param tag The element name.
- # @param attrib A dictionary containing element attributes.
- # @return The opened element.
- # @defreturn Element
-
- def start(self, tag, attrs):
- self._flush()
- self._last = elem = self._factory(tag, attrs)
- if self._elem:
- self._elem[-1].append(elem)
- self._elem.append(elem)
- self._tail = 0
- return elem
-
- ##
- # Closes the current element.
- #
- # @param tag The element name.
- # @return The closed element.
- # @defreturn Element
-
- def end(self, tag):
- self._flush()
- self._last = self._elem.pop()
- assert self._last.tag == tag,\
- "end tag mismatch (expected %s, got %s)" % (
- self._last.tag, tag)
- self._tail = 1
- return self._last
-
-##
-# Element structure builder for XML source data, based on the
-# <b>expat</b> parser.
-#
-# @keyparam target Target object. If omitted, the builder uses an
-# instance of the standard {@link #TreeBuilder} class.
-# @keyparam html Predefine HTML entities. This flag is not supported
-# by the current implementation.
-# @see #ElementTree
-# @see #TreeBuilder
-
-class XMLTreeBuilder:
-
- def __init__(self, html=0, target=None):
- try:
- from xml.parsers import expat
- except ImportError:
- raise ImportError(
- "No module named expat; use SimpleXMLTreeBuilder instead"
- )
- self._parser = parser = expat.ParserCreate(None, "}")
- if target is None:
- target = TreeBuilder()
- self._target = target
- self._names = {} # name memo cache
- # callbacks
- parser.DefaultHandlerExpand = self._default
- parser.StartElementHandler = self._start
- parser.EndElementHandler = self._end
- parser.CharacterDataHandler = self._data
- # let expat do the buffering, if supported
- try:
- self._parser.buffer_text = 1
- except AttributeError:
- pass
- # use new-style attribute handling, if supported
- try:
- self._parser.ordered_attributes = 1
- self._parser.specified_attributes = 1
- parser.StartElementHandler = self._start_list
- except AttributeError:
- pass
- encoding = None
- if not parser.returns_unicode:
- encoding = "utf-8"
- # target.xml(encoding, None)
- self._doctype = None
- self.entity = {}
-
- def _fixtext(self, text):
- # convert text string to ascii, if possible
- try:
- return _encode(text, "ascii")
- except UnicodeError:
- return text
-
- def _fixname(self, key):
- # expand qname, and convert name string to ascii, if possible
- try:
- name = self._names[key]
- except KeyError:
- name = key
- if "}" in name:
- name = "{" + name
- self._names[key] = name = self._fixtext(name)
- return name
-
- def _start(self, tag, attrib_in):
- fixname = self._fixname
- tag = fixname(tag)
- attrib = {}
- for key, value in attrib_in.items():
- attrib[fixname(key)] = self._fixtext(value)
- return self._target.start(tag, attrib)
-
- def _start_list(self, tag, attrib_in):
- fixname = self._fixname
- tag = fixname(tag)
- attrib = {}
- if attrib_in:
- for i in xrange(0, len(attrib_in), 2):
- attrib[fixname(attrib_in[i])] = self._fixtext(attrib_in[i+1])
- return self._target.start(tag, attrib)
-
- def _data(self, text):
- return self._target.data(self._fixtext(text))
-
- def _end(self, tag):
- return self._target.end(self._fixname(tag))
-
- def _default(self, text):
- prefix = text[:1]
- if prefix == "&":
- # deal with undefined entities
- try:
- self._target.data(self.entity[text[1:-1]])
- except KeyError:
- from xml.parsers import expat
- raise expat.error(
- "undefined entity %s: line %d, column %d" %
- (text, self._parser.ErrorLineNumber,
- self._parser.ErrorColumnNumber)
- )
- elif prefix == "<" and text[:9] == "<!DOCTYPE":
- self._doctype = [] # inside a doctype declaration
- elif self._doctype is not None:
- # parse doctype contents
- if prefix == ">":
- self._doctype = None
- return
- text = string.strip(text)
- if not text:
- return
- self._doctype.append(text)
- n = len(self._doctype)
- if n > 2:
- type = self._doctype[1]
- if type == "PUBLIC" and n == 4:
- name, type, pubid, system = self._doctype
- elif type == "SYSTEM" and n == 3:
- name, type, system = self._doctype
- pubid = None
- else:
- return
- if pubid:
- pubid = pubid[1:-1]
- self.doctype(name, pubid, system[1:-1])
- self._doctype = None
-
- ##
- # Handles a doctype declaration.
- #
- # @param name Doctype name.
- # @param pubid Public identifier.
- # @param system System identifier.
-
- def doctype(self, name, pubid, system):
- pass
-
- ##
- # Feeds data to the parser.
- #
- # @param data Encoded data.
-
- def feed(self, data):
- self._parser.Parse(data, 0)
-
- ##
- # Finishes feeding data to the parser.
- #
- # @return An element structure.
- # @defreturn Element
-
- def close(self):
- self._parser.Parse("", 1) # end of data
- tree = self._target.close()
- del self._target, self._parser # get rid of circular references
- return tree