• 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) and not isinstance(base, GenericAlias):
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.  (Enum used this between Python
159    versions 3.4 - 3.9 .)
160
161    Subclass from this to use a different method of accessing virtual atributes
162    and still be treated properly by the inspect module. (Enum uses this since
163    Python 3.10 .)
164
165    """
166    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
167        self.fget = fget
168        self.fset = fset
169        self.fdel = fdel
170        # next two lines make DynamicClassAttribute act the same as property
171        self.__doc__ = doc or fget.__doc__
172        self.overwrite_doc = doc is None
173        # support for abstract methods
174        self.__isabstractmethod__ = bool(getattr(fget, '__isabstractmethod__', False))
175
176    def __get__(self, instance, ownerclass=None):
177        if instance is None:
178            if self.__isabstractmethod__:
179                return self
180            raise AttributeError()
181        elif self.fget is None:
182            raise AttributeError("unreadable attribute")
183        return self.fget(instance)
184
185    def __set__(self, instance, value):
186        if self.fset is None:
187            raise AttributeError("can't set attribute")
188        self.fset(instance, value)
189
190    def __delete__(self, instance):
191        if self.fdel is None:
192            raise AttributeError("can't delete attribute")
193        self.fdel(instance)
194
195    def getter(self, fget):
196        fdoc = fget.__doc__ if self.overwrite_doc else None
197        result = type(self)(fget, self.fset, self.fdel, fdoc or self.__doc__)
198        result.overwrite_doc = self.overwrite_doc
199        return result
200
201    def setter(self, fset):
202        result = type(self)(self.fget, fset, self.fdel, self.__doc__)
203        result.overwrite_doc = self.overwrite_doc
204        return result
205
206    def deleter(self, fdel):
207        result = type(self)(self.fget, self.fset, fdel, self.__doc__)
208        result.overwrite_doc = self.overwrite_doc
209        return result
210
211
212class _GeneratorWrapper:
213    # TODO: Implement this in C.
214    def __init__(self, gen):
215        self.__wrapped = gen
216        self.__isgen = gen.__class__ is GeneratorType
217        self.__name__ = getattr(gen, '__name__', None)
218        self.__qualname__ = getattr(gen, '__qualname__', None)
219    def send(self, val):
220        return self.__wrapped.send(val)
221    def throw(self, tp, *rest):
222        return self.__wrapped.throw(tp, *rest)
223    def close(self):
224        return self.__wrapped.close()
225    @property
226    def gi_code(self):
227        return self.__wrapped.gi_code
228    @property
229    def gi_frame(self):
230        return self.__wrapped.gi_frame
231    @property
232    def gi_running(self):
233        return self.__wrapped.gi_running
234    @property
235    def gi_yieldfrom(self):
236        return self.__wrapped.gi_yieldfrom
237    cr_code = gi_code
238    cr_frame = gi_frame
239    cr_running = gi_running
240    cr_await = gi_yieldfrom
241    def __next__(self):
242        return next(self.__wrapped)
243    def __iter__(self):
244        if self.__isgen:
245            return self.__wrapped
246        return self
247    __await__ = __iter__
248
249def coroutine(func):
250    """Convert regular generator function to a coroutine."""
251
252    if not callable(func):
253        raise TypeError('types.coroutine() expects a callable')
254
255    if (func.__class__ is FunctionType and
256        getattr(func, '__code__', None).__class__ is CodeType):
257
258        co_flags = func.__code__.co_flags
259
260        # Check if 'func' is a coroutine function.
261        # (0x180 == CO_COROUTINE | CO_ITERABLE_COROUTINE)
262        if co_flags & 0x180:
263            return func
264
265        # Check if 'func' is a generator function.
266        # (0x20 == CO_GENERATOR)
267        if co_flags & 0x20:
268            # TODO: Implement this in C.
269            co = func.__code__
270            # 0x100 == CO_ITERABLE_COROUTINE
271            func.__code__ = co.replace(co_flags=co.co_flags | 0x100)
272            return func
273
274    # The following code is primarily to support functions that
275    # return generator-like objects (for instance generators
276    # compiled with Cython).
277
278    # Delay functools and _collections_abc import for speeding up types import.
279    import functools
280    import _collections_abc
281    @functools.wraps(func)
282    def wrapped(*args, **kwargs):
283        coro = func(*args, **kwargs)
284        if (coro.__class__ is CoroutineType or
285            coro.__class__ is GeneratorType and coro.gi_code.co_flags & 0x100):
286            # 'coro' is a native coroutine object or an iterable coroutine
287            return coro
288        if (isinstance(coro, _collections_abc.Generator) and
289            not isinstance(coro, _collections_abc.Coroutine)):
290            # 'coro' is either a pure Python generator iterator, or it
291            # implements collections.abc.Generator (and does not implement
292            # collections.abc.Coroutine).
293            return _GeneratorWrapper(coro)
294        # 'coro' is either an instance of collections.abc.Coroutine or
295        # some other object -- pass it through.
296        return coro
297
298    return wrapped
299
300GenericAlias = type(list[int])
301UnionType = type(int | str)
302
303EllipsisType = type(Ellipsis)
304NoneType = type(None)
305NotImplementedType = type(NotImplemented)
306
307__all__ = [n for n in globals() if n[:1] != '_']
308