freeipa/ipapython/install/util.py
Jan Cholasta ebdfa4380b Use six.with_metaclass to specify metaclasses
Metaclass specification is incompatible between Python 2 and 3. Use the
six.with_metaclass helper to specify metaclasses.

Reviewed-By: Petr Viktorin <pviktori@redhat.com>
2015-09-07 08:00:11 +02:00

165 lines
4.1 KiB
Python

#
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
#
"""
Utilities.
"""
import sys
import six
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:
six.reraise(*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 six.iteritems(cls.__dict__):
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))
self.__qualname__ = self.__name__
return cls, name