Home | History | Annotate | Download | only in Lib
      1 """
      2 Define names for built-in types that aren't directly accessible as a builtin.
      3 """
      4 import 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 
     11 def _f(): pass
     12 FunctionType = type(_f)
     13 LambdaType = type(lambda: None)         # Same as FunctionType
     14 CodeType = type(_f.__code__)
     15 MappingProxyType = type(type.__dict__)
     16 SimpleNamespace = type(sys.implementation)
     17 
     18 def _g():
     19     yield 1
     20 GeneratorType = type(_g())
     21 
     22 async def _c(): pass
     23 _c = _c()
     24 CoroutineType = type(_c)
     25 _c.close()  # Prevent ResourceWarning
     26 
     27 async def _ag():
     28     yield
     29 _ag = _ag()
     30 AsyncGeneratorType = type(_ag)
     31 
     32 class _C:
     33     def _m(self): pass
     34 MethodType = type(_C()._m)
     35 
     36 BuiltinFunctionType = type(len)
     37 BuiltinMethodType = type([].append)     # Same as BuiltinFunctionType
     38 
     39 ModuleType = type(sys)
     40 
     41 try:
     42     raise TypeError
     43 except 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
     50 GetSetDescriptorType = type(FunctionType.__code__)
     51 MemberDescriptorType = type(FunctionType.__globals__)
     52 
     53 del sys, _f, _g, _C, _c,                           # Not for export
     54 
     55 
     56 # Provide a PEP 3115 compliant mechanism for class creation
     57 def 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 
     64 def 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 
     96 def _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 
    113 class 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 
    171 import functools as _functools
    172 import collections.abc as _collections_abc
    173 
    174 class _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 
    211 def 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