summaryrefslogtreecommitdiffstats
path: root/ipapython/install/util.py
diff options
context:
space:
mode:
Diffstat (limited to 'ipapython/install/util.py')
-rw-r--r--ipapython/install/util.py169
1 files changed, 169 insertions, 0 deletions
diff --git a/ipapython/install/util.py b/ipapython/install/util.py
new file mode 100644
index 000000000..58da7bb77
--- /dev/null
+++ b/ipapython/install/util.py
@@ -0,0 +1,169 @@
+#
+# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
+#
+
+"""
+Utilities.
+"""
+
+import sys
+
+
+def raise_exc_info(exc_info):
+ """
+ Raise exception from exception info tuple as returned by `sys.exc_info()`.
+ """
+
+ raise exc_info[0], exc_info[1], exc_info[2]
+
+
+class from_(object):
+ """
+ Wrapper for delegating to a subgenerator.
+
+ See `run_generator_with_yield_from`.
+ """
+ __slots__ = ('obj',)
+
+ def __init__(self, obj):
+ self.obj = obj
+
+
+def run_generator_with_yield_from(gen):
+ """
+ Iterate over a generator object with subgenerator delegation.
+
+ This implements Python 3's ``yield from`` expressions, using Python 2
+ syntax:
+
+ >>> def subgen():
+ ... yield 'B'
+ ... yield 'C'
+ ...
+ >>> def gen():
+ ... yield 'A'
+ ... yield from_(subgen())
+ ... yield 'D'
+ ...
+ >>> list(run_generator_with_yield_from(gen()))
+ ['A', 'B', 'C', 'D']
+
+ Returning value from a subgenerator is not supported.
+ """
+
+ exc_info = None
+ value = None
+
+ stack = [gen]
+ while stack:
+ prev_exc_info, exc_info = exc_info, None
+ prev_value, value = value, None
+
+ gen = stack[-1]
+ try:
+ if prev_exc_info is None:
+ value = gen.send(prev_value)
+ else:
+ value = gen.throw(*prev_exc_info)
+ except StopIteration:
+ stack.pop()
+ continue
+ except BaseException:
+ exc_info = sys.exc_info()
+ stack.pop()
+ continue
+ else:
+ if isinstance(value, from_):
+ stack.append(value.obj)
+ value = None
+ continue
+
+ try:
+ value = (yield value)
+ except BaseException:
+ exc_info = sys.exc_info()
+
+ if exc_info is not None:
+ raise_exc_info(exc_info)
+
+
+class InnerClassMeta(type):
+ def __new__(cls, name, bases, class_dict):
+ class_dict.pop('__outer_class__', None)
+ class_dict.pop('__outer_name__', None)
+
+ return super(InnerClassMeta, cls).__new__(cls, name, bases, class_dict)
+
+ def __get__(self, obj, obj_type):
+ outer_class, outer_name = self.__bind(obj_type)
+ if obj is None:
+ return self
+ assert isinstance(obj, outer_class)
+
+ try:
+ return obj.__dict__[outer_name]
+ except KeyError:
+ inner = self(obj)
+ try:
+ getter = inner.__get__
+ except AttributeError:
+ return inner
+ else:
+ return getter(obj, obj_type)
+
+ def __set__(self, obj, value):
+ outer_class, outer_name = self.__bind(obj.__class__)
+ assert isinstance(obj, outer_class)
+
+ inner = self(obj)
+ try:
+ setter = inner.__set__
+ except AttributeError:
+ try:
+ inner.__delete__
+ except AttributeError:
+ obj.__dict__[outer_name] = value
+ else:
+ raise AttributeError('__set__')
+ else:
+ setter(obj, value)
+
+ def __delete__(self, obj):
+ outer_class, outer_name = self.__bind(obj.__class__)
+ assert isinstance(obj, outer_class)
+
+ inner = self(obj)
+ try:
+ deleter = inner.__delete__
+ except AttributeError:
+ try:
+ inner.__set__
+ except AttributeError:
+ try:
+ del obj.__dict__[outer_name]
+ except KeyError:
+ raise AttributeError(outer_name)
+ else:
+ raise AttributeError('__delete__')
+ else:
+ deleter(obj)
+
+ def __bind(self, obj_type):
+ try:
+ cls = self.__dict__['__outer_class__']
+ name = self.__dict__['__outer_name__']
+ except KeyError:
+ cls, name, value = None, None, None
+ for cls in obj_type.__mro__:
+ for name, value in cls.__dict__.iteritems():
+ if value is self:
+ break
+ if value is self:
+ break
+ assert value is self
+
+ self.__outer_class__ = cls
+ self.__outer_name__ = name
+ self.__name__ = '.'.join((cls.__name__, name))
+
+ return cls, name