• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""
2Define names for built-in types that aren't directly accessible as a builtin.
3"""
4
5import sys
6
7# Iterators in Python aren't a matter of type but of protocol.  A large
8# and changing number of builtin types implement *some* flavor of
9# iterator.  Don't check the type!  Use hasattr to check for both
10# "__iter__" and "__next__" attributes instead.
11
12def _f(): pass
13FunctionType = type(_f)
14LambdaType = type(lambda: None)         # Same as FunctionType
15CodeType = type(_f.__code__)
16MappingProxyType = type(type.__dict__)
17SimpleNamespace = type(sys.implementation)
18
19def _cell_factory():
20    a = 1
21    def f():
22        nonlocal a
23    return f.__closure__[0]
24CellType = type(_cell_factory())
25
26def _g():
27    yield 1
28GeneratorType = type(_g())
29
30async def _c(): pass
31_c = _c()
32CoroutineType = type(_c)
33_c.close()  # Prevent ResourceWarning
34
35async def _ag():
36    yield
37_ag = _ag()
38AsyncGeneratorType = type(_ag)
39
40class _C:
41    def _m(self): pass
42MethodType = type(_C()._m)
43
44BuiltinFunctionType = type(len)
45BuiltinMethodType = type([].append)     # Same as BuiltinFunctionType
46
47WrapperDescriptorType = type(object.__init__)
48MethodWrapperType = type(object().__str__)
49MethodDescriptorType = type(str.join)
50ClassMethodDescriptorType = type(dict.__dict__['fromkeys'])
51
52ModuleType = type(sys)
53
54try:
55    raise TypeError
56except TypeError as exc:
57    TracebackType = type(exc.__traceback__)
58    FrameType = type(exc.__traceback__.tb_frame)
59
60GetSetDescriptorType = type(FunctionType.__code__)
61MemberDescriptorType = type(FunctionType.__globals__)
62
63del sys, _f, _g, _C, _c, _ag, _cell_factory  # 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
147
148def get_original_bases(cls, /):
149    """Return the class's "original" bases prior to modification by `__mro_entries__`.
150
151    Examples::
152
153        from typing import TypeVar, Generic, NamedTuple, TypedDict
154
155        T = TypeVar("T")
156        class Foo(Generic[T]): ...
157        class Bar(Foo[int], float): ...
158        class Baz(list[str]): ...
159        Eggs = NamedTuple("Eggs", [("a", int), ("b", str)])
160        Spam = TypedDict("Spam", {"a": int, "b": str})
161
162        assert get_original_bases(Bar) == (Foo[int], float)
163        assert get_original_bases(Baz) == (list[str],)
164        assert get_original_bases(Eggs) == (NamedTuple,)
165        assert get_original_bases(Spam) == (TypedDict,)
166        assert get_original_bases(int) == (object,)
167    """
168    try:
169        return cls.__dict__.get("__orig_bases__", cls.__bases__)
170    except AttributeError:
171        raise TypeError(
172            f"Expected an instance of type, not {type(cls).__name__!r}"
173        ) from None
174
175
176class DynamicClassAttribute:
177    """Route attribute access on a class to __getattr__.
178
179    This is a descriptor, used to define attributes that act differently when
180    accessed through an instance and through a class.  Instance access remains
181    normal, but access to an attribute through a class will be routed to the
182    class's __getattr__ method; this is done by raising AttributeError.
183
184    This allows one to have properties active on an instance, and have virtual
185    attributes on the class with the same name.  (Enum used this between Python
186    versions 3.4 - 3.9 .)
187
188    Subclass from this to use a different method of accessing virtual attributes
189    and still be treated properly by the inspect module. (Enum uses this since
190    Python 3.10 .)
191
192    """
193    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
194        self.fget = fget
195        self.fset = fset
196        self.fdel = fdel
197        # next two lines make DynamicClassAttribute act the same as property
198        self.__doc__ = doc or fget.__doc__
199        self.overwrite_doc = doc is None
200        # support for abstract methods
201        self.__isabstractmethod__ = bool(getattr(fget, '__isabstractmethod__', False))
202
203    def __get__(self, instance, ownerclass=None):
204        if instance is None:
205            if self.__isabstractmethod__:
206                return self
207            raise AttributeError()
208        elif self.fget is None:
209            raise AttributeError("unreadable attribute")
210        return self.fget(instance)
211
212    def __set__(self, instance, value):
213        if self.fset is None:
214            raise AttributeError("can't set attribute")
215        self.fset(instance, value)
216
217    def __delete__(self, instance):
218        if self.fdel is None:
219            raise AttributeError("can't delete attribute")
220        self.fdel(instance)
221
222    def getter(self, fget):
223        fdoc = fget.__doc__ if self.overwrite_doc else None
224        result = type(self)(fget, self.fset, self.fdel, fdoc or self.__doc__)
225        result.overwrite_doc = self.overwrite_doc
226        return result
227
228    def setter(self, fset):
229        result = type(self)(self.fget, fset, self.fdel, self.__doc__)
230        result.overwrite_doc = self.overwrite_doc
231        return result
232
233    def deleter(self, fdel):
234        result = type(self)(self.fget, self.fset, fdel, self.__doc__)
235        result.overwrite_doc = self.overwrite_doc
236        return result
237
238
239class _GeneratorWrapper:
240    # TODO: Implement this in C.
241    def __init__(self, gen):
242        self.__wrapped = gen
243        self.__isgen = gen.__class__ is GeneratorType
244        self.__name__ = getattr(gen, '__name__', None)
245        self.__qualname__ = getattr(gen, '__qualname__', None)
246    def send(self, val):
247        return self.__wrapped.send(val)
248    def throw(self, tp, *rest):
249        return self.__wrapped.throw(tp, *rest)
250    def close(self):
251        return self.__wrapped.close()
252    @property
253    def gi_code(self):
254        return self.__wrapped.gi_code
255    @property
256    def gi_frame(self):
257        return self.__wrapped.gi_frame
258    @property
259    def gi_running(self):
260        return self.__wrapped.gi_running
261    @property
262    def gi_yieldfrom(self):
263        return self.__wrapped.gi_yieldfrom
264    cr_code = gi_code
265    cr_frame = gi_frame
266    cr_running = gi_running
267    cr_await = gi_yieldfrom
268    def __next__(self):
269        return next(self.__wrapped)
270    def __iter__(self):
271        if self.__isgen:
272            return self.__wrapped
273        return self
274    __await__ = __iter__
275
276def coroutine(func):
277    """Convert regular generator function to a coroutine."""
278
279    if not callable(func):
280        raise TypeError('types.coroutine() expects a callable')
281
282    if (func.__class__ is FunctionType and
283        getattr(func, '__code__', None).__class__ is CodeType):
284
285        co_flags = func.__code__.co_flags
286
287        # Check if 'func' is a coroutine function.
288        # (0x180 == CO_COROUTINE | CO_ITERABLE_COROUTINE)
289        if co_flags & 0x180:
290            return func
291
292        # Check if 'func' is a generator function.
293        # (0x20 == CO_GENERATOR)
294        if co_flags & 0x20:
295            # TODO: Implement this in C.
296            co = func.__code__
297            # 0x100 == CO_ITERABLE_COROUTINE
298            func.__code__ = co.replace(co_flags=co.co_flags | 0x100)
299            return func
300
301    # The following code is primarily to support functions that
302    # return generator-like objects (for instance generators
303    # compiled with Cython).
304
305    # Delay functools and _collections_abc import for speeding up types import.
306    import functools
307    import _collections_abc
308    @functools.wraps(func)
309    def wrapped(*args, **kwargs):
310        coro = func(*args, **kwargs)
311        if (coro.__class__ is CoroutineType or
312            coro.__class__ is GeneratorType and coro.gi_code.co_flags & 0x100):
313            # 'coro' is a native coroutine object or an iterable coroutine
314            return coro
315        if (isinstance(coro, _collections_abc.Generator) and
316            not isinstance(coro, _collections_abc.Coroutine)):
317            # 'coro' is either a pure Python generator iterator, or it
318            # implements collections.abc.Generator (and does not implement
319            # collections.abc.Coroutine).
320            return _GeneratorWrapper(coro)
321        # 'coro' is either an instance of collections.abc.Coroutine or
322        # some other object -- pass it through.
323        return coro
324
325    return wrapped
326
327GenericAlias = type(list[int])
328UnionType = type(int | str)
329
330EllipsisType = type(Ellipsis)
331NoneType = type(None)
332NotImplementedType = type(NotImplemented)
333
334def __getattr__(name):
335    if name == 'CapsuleType':
336        import _socket
337        return type(_socket.CAPI)
338    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
339
340__all__ = [n for n in globals() if n[:1] != '_']
341__all__ += ['CapsuleType']
342