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