summaryrefslogtreecommitdiffstats
path: root/ipapython/install/util.py
blob: 58da7bb77592c1dc68a1b5a58fc374b9cee88fc4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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