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