mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2024-12-26 00:41:25 -06:00
71b3352ad0
Reviewed-By: Tomas Krizek <tkrizek@redhat.com> Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
165 lines
4.2 KiB
Python
165 lines
4.2 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__(mcs, name, bases, class_dict):
|
|
class_dict.pop('__outer_class__', None)
|
|
class_dict.pop('__outer_name__', None)
|
|
|
|
return super(InnerClassMeta, mcs).__new__(mcs, name, bases, class_dict)
|
|
|
|
def __get__(cls, obj, obj_type):
|
|
outer_class, outer_name = cls.__bind(obj_type)
|
|
if obj is None:
|
|
return cls
|
|
assert isinstance(obj, outer_class)
|
|
|
|
try:
|
|
return obj.__dict__[outer_name]
|
|
except KeyError:
|
|
inner = cls(obj)
|
|
try:
|
|
getter = inner.__get__
|
|
except AttributeError:
|
|
return inner
|
|
else:
|
|
return getter(obj, obj_type)
|
|
|
|
def __set__(cls, obj, value):
|
|
outer_class, outer_name = cls.__bind(obj.__class__)
|
|
assert isinstance(obj, outer_class)
|
|
|
|
inner = cls(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__(cls, obj):
|
|
outer_class, outer_name = cls.__bind(obj.__class__)
|
|
assert isinstance(obj, outer_class)
|
|
|
|
inner = cls(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(cls, obj_type):
|
|
try:
|
|
outer_class = cls.__dict__['__outer_class__']
|
|
name = cls.__dict__['__outer_name__']
|
|
except KeyError:
|
|
outer_class, name, value = None, None, None
|
|
for outer_class in obj_type.__mro__:
|
|
for name, value in six.iteritems(outer_class.__dict__):
|
|
if value is cls:
|
|
break
|
|
if value is cls:
|
|
break
|
|
assert value is cls
|
|
|
|
cls.__outer_class__ = outer_class
|
|
cls.__outer_name__ = name
|
|
cls.__name__ = '.'.join((outer_class.__name__, name))
|
|
cls.__qualname__ = cls.__name__
|
|
|
|
return outer_class, name
|