• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""
2Define names for built-in types that aren't directly accessible as a builtin.
3"""
4import sys
5
6# Iterators in Python aren't a matter of type but of protocol.  A large
7# and changing number of builtin types implement *some* flavor of
8# iterator.  Don't check the type!  Use hasattr to check for both
9# "__iter__" and "__next__" attributes instead.
10
11def _f(): pass
12FunctionType = type(_f)
13LambdaType = type(lambda: None)         # Same as FunctionType
14CodeType = type(_f.__code__)
15MappingProxyType = type(type.__dict__)
16SimpleNamespace = type(sys.implementation)
17
18def _g():
19    yield 1
20GeneratorType = type(_g())
21
22async def _c(): pass
23_c = _c()
24CoroutineType = type(_c)
25_c.close()  # Prevent ResourceWarning
26
27async def _ag():
28    yield
29_ag = _ag()
30AsyncGeneratorType = type(_ag)
31
32class _C:
33    def _m(self): pass
34MethodType = type(_C()._m)
35
36BuiltinFunctionType = type(len)
37BuiltinMethodType = type([].append)     # Same as BuiltinFunctionType
38
39ModuleType = type(sys)
40
41try:
42    raise TypeError
43except TypeError:
44    tb = sys.exc_info()[2]
45    TracebackType = type(tb)
46    FrameType = type(tb.tb_frame)
47    tb = None; del tb
48
49# For Jython, the following two types are identical
50GetSetDescriptorType = type(FunctionType.__code__)
51MemberDescriptorType = type(FunctionType.__globals__)
52
53del sys, _f, _g, _C, _c,                           # Not for export
54
55
56# Provide a PEP 3115 compliant mechanism for class creation
57def new_class(name, bases=(), kwds=None, exec_body=None):
58    """Create a class object dynamically using the appropriate metaclass."""
59    meta, ns, kwds = prepare_class(name, bases, kwds)
60    if exec_body is not None:
61        exec_body(ns)
62    return meta(name, bases, ns, **kwds)
63
64def prepare_class(name, bases=(), kwds=None):
65    """Call the __prepare__ method of the appropriate metaclass.
66
67    Returns (metaclass, namespace, kwds) as a 3-tuple
68
69    *metaclass* is the appropriate metaclass
70    *namespace* is the prepared class namespace
71    *kwds* is an updated copy of the passed in kwds argument with any
72    'metaclass' entry removed. If no kwds argument is passed in, this will
73    be an empty dict.
74    """
75    if kwds is None:
76        kwds = {}
77    else:
78        kwds = dict(kwds) # Don't alter the provided mapping
79    if 'metaclass' in kwds:
80        meta = kwds.pop('metaclass')
81    else:
82        if bases:
83            meta = type(bases[0])
84        else:
85            meta = type
86    if isinstance(meta, type):
87        # when meta is a type, we first determine the most-derived metaclass
88        # instead of invoking the initial candidate directly
89        meta = _calculate_meta(meta, bases)
90    if hasattr(meta, '__prepare__'):
91        ns = meta.__prepare__(name, bases, **kwds)
92    else:
93        ns = {}
94    return meta, ns, kwds
95
96def _calculate_meta(meta, bases):
97    """Calculate the most derived metaclass."""
98    winner = meta
99    for base in bases:
100        base_meta = type(base)
101        if issubclass(winner, base_meta):
102            continue
103        if issubclass(base_meta, winner):
104            winner = base_meta
105            continue
106        # else:
107        raise TypeError("metaclass conflict: "
108                        "the metaclass of a derived class "
109                        "must be a (non-strict) subclass "
110                        "of the metaclasses of all its bases")
111    return winner
112
113class DynamicClassAttribute:
114    """Route attribute access on a class to __getattr__.
115
116    This is a descriptor, used to define attributes that act differently when
117    accessed through an instance and through a class.  Instance access remains
118    normal, but access to an attribute through a class will be routed to the
119    class's __getattr__ method; this is done by raising AttributeError.
120
121    This allows one to have properties active on an instance, and have virtual
122    attributes on the class with the same name (see Enum for an example).
123
124    """
125    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
126        self.fget = fget
127        self.fset = fset
128        self.fdel = fdel
129        # next two lines make DynamicClassAttribute act the same as property
130        self.__doc__ = doc or fget.__doc__
131        self.overwrite_doc = doc is None
132        # support for abstract methods
133        self.__isabstractmethod__ = bool(getattr(fget, '__isabstractmethod__', False))
134
135    def __get__(self, instance, ownerclass=None):
136        if instance is None:
137            if self.__isabstractmethod__:
138                return self
139            raise AttributeError()
140        elif self.fget is None:
141            raise AttributeError("unreadable attribute")
142        return self.fget(instance)
143
144    def __set__(self, instance, value):
145        if self.fset is None:
146            raise AttributeError("can't set attribute")
147        self.fset(instance, value)
148
149    def __delete__(self, instance):
150        if self.fdel is None:
151            raise AttributeError("can't delete attribute")
152        self.fdel(instance)
153
154    def getter(self, fget):
155        fdoc = fget.__doc__ if self.overwrite_doc else None
156        result = type(self)(fget, self.fset, self.fdel, fdoc or self.__doc__)
157        result.overwrite_doc = self.overwrite_doc
158        return result
159
160    def setter(self, fset):
161        result = type(self)(self.fget, fset, self.fdel, self.__doc__)
162        result.overwrite_doc = self.overwrite_doc
163        return result
164
165    def deleter(self, fdel):
166        result = type(self)(self.fget, self.fset, fdel, self.__doc__)
167        result.overwrite_doc = self.overwrite_doc
168        return result
169
170
171import functools as _functools
172import collections.abc as _collections_abc
173
174class _GeneratorWrapper:
175    # TODO: Implement this in C.
176    def __init__(self, gen):
177        self.__wrapped = gen
178        self.__isgen = gen.__class__ is GeneratorType
179        self.__name__ = getattr(gen, '__name__', None)
180        self.__qualname__ = getattr(gen, '__qualname__', None)
181    def send(self, val):
182        return self.__wrapped.send(val)
183    def throw(self, tp, *rest):
184        return self.__wrapped.throw(tp, *rest)
185    def close(self):
186        return self.__wrapped.close()
187    @property
188    def gi_code(self):
189        return self.__wrapped.gi_code
190    @property
191    def gi_frame(self):
192        return self.__wrapped.gi_frame
193    @property
194    def gi_running(self):
195        return self.__wrapped.gi_running
196    @property
197    def gi_yieldfrom(self):
198        return self.__wrapped.gi_yieldfrom
199    cr_code = gi_code
200    cr_frame = gi_frame
201    cr_running = gi_running
202    cr_await = gi_yieldfrom
203    def __next__(self):
204        return next(self.__wrapped)
205    def __iter__(self):
206        if self.__isgen:
207            return self.__wrapped
208        return self
209    __await__ = __iter__
210
211def coroutine(func):
212    """Convert regular generator function to a coroutine."""
213
214    if not callable(func):
215        raise TypeError('types.coroutine() expects a callable')
216
217    if (func.__class__ is FunctionType and
218        getattr(func, '__code__', None).__class__ is CodeType):
219
220        co_flags = func.__code__.co_flags
221
222        # Check if 'func' is a coroutine function.
223        # (0x180 == CO_COROUTINE | CO_ITERABLE_COROUTINE)
224        if co_flags & 0x180:
225            return func
226
227        # Check if 'func' is a generator function.
228        # (0x20 == CO_GENERATOR)
229        if co_flags & 0x20:
230            # TODO: Implement this in C.
231            co = func.__code__
232            func.__code__ = CodeType(
233                co.co_argcount, co.co_kwonlyargcount, co.co_nlocals,
234                co.co_stacksize,
235                co.co_flags | 0x100,  # 0x100 == CO_ITERABLE_COROUTINE
236                co.co_code,
237                co.co_consts, co.co_names, co.co_varnames, co.co_filename,
238                co.co_name, co.co_firstlineno, co.co_lnotab, co.co_freevars,
239                co.co_cellvars)
240            return func
241
242    # The following code is primarily to support functions that
243    # return generator-like objects (for instance generators
244    # compiled with Cython).
245
246    @_functools.wraps(func)
247    def wrapped(*args, **kwargs):
248        coro = func(*args, **kwargs)
249        if (coro.__class__ is CoroutineType or
250            coro.__class__ is GeneratorType and coro.gi_code.co_flags & 0x100):
251            # 'coro' is a native coroutine object or an iterable coroutine
252            return coro
253        if (isinstance(coro, _collections_abc.Generator) and
254            not isinstance(coro, _collections_abc.Coroutine)):
255            # 'coro' is either a pure Python generator iterator, or it
256            # implements collections.abc.Generator (and does not implement
257            # collections.abc.Coroutine).
258            return _GeneratorWrapper(coro)
259        # 'coro' is either an instance of collections.abc.Coroutine or
260        # some other object -- pass it through.
261        return coro
262
263    return wrapped
264
265
266__all__ = [n for n in globals() if n[:1] != '_']
267