Source code for xoutil.future.types

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ---------------------------------------------------------------------
# Copyright (c) Merchise Autrement [~º/~] and Contributors
# All rights reserved.
#
# This is free software; you can do what the LICENCE file allows you to.
#

'''Extends the standard `types` module.

Standard module defines names for all type symbols known in the standard
interpreter.

This modules mirrors all the functions (and, in general, objects) from the
standard library module `types`:mod:; but it also includes several new types
and type-related functions.

This module unifies old ``xoutil.types`` and ``xoutil.eigth.types`` modules,
which are deprecated now.

There are some symbols included in Python 2.x series, but not in Python 3 that
we don't include here:

- `TypeType`: use `type` instead

- `ObjectType`: use `object`

- `IntType`: use `int`

- `LongType`: use `long` in Python 2 and `int` in Python 3; better see
  `xoutil.eight.integer_types` definition.

- `FloatType`: use `float`

- `BooleanType`: use `bool`

- `ComplexType`: use `complex`

- `StringType`: use str

- `UnicodeType`: use `unicode` in Python 2 and `str` in Python 3; there are
  two aliases for that: `xoutil.eigth.UnicodeType` and
  `xoutil.eigth.text_type`.

- `StringTypes`: use `xoutil.eigth.StringTypes`or
  `xoutil.eigth.string_types`.

- `BufferType`: use `buffer` in Python 2 and `memoryview` in Python 3; there
  is an alias for this convention in `xoutil.eigth.buffer`.  The
  `memoryview`:class: API is similar but not exactly the same as that of
  `buffer`.

- `TupleType`: use `tuple`

- `ListType`: use `list`

- `DictType` (or `DictionaryType`): use `dict`

- `ClassType`: Python 2 old-style classes, don't exists in Python 3, see
  `xoutil.eigth.ClassTypes`.

- `InstanceType`: type of instances of Python 2 old-style classes, don't
  exists in Python 3, see `xoutil.eigth.typeof`.

- `UnboundMethodType`: use `~types.MethodType` alias

- `FileType`: use `file`

- `XRangeType` (or `xrange`): in Python 3 `range` always returns a generator
  object; use `xoutil.eigth.range`:class: for compatibility; wraps it with
  list (``list(range(...))``) to obtain old `range` style

- `SliceType`: use `slice`

- In Jython and PyPy, `MemberDescriptorType` is identical to
  `GetSetDescriptorType`; to mantain compatibility in some `xoutil` code, they
  are differentiated in this module.

- `CoroutineType` and `coroutine`: use new Python 3 `async` statement; not
  implementable in Python 2.

'''

from __future__ import (division as _py3_division,
                        print_function as _py3_print,
                        absolute_import as _py3_abs_import)

from types import *    # noqa
import types as _stdlib    # noqa

from xoutil.deprecation import deprecated, deprecate_linked
deprecate_linked()

from xoutil.eight import python_version    # noqa
from xoutil.symbols import Unset as _unset
from collections import Mapping


try:
    from types import __all__    # noqa
    __all__ = list(__all__)
except ImportError:
    # Python 3.3 don't implement '__all__' for 'string' module.
    __all__ = [name for name in dir(_stdlib) if not name.startswith('_')]

try:
    NoneType    # noqa
except NameError:
    NoneType = type(None)
    __all__.append('NoneType')

try:
    EllipsisType    # noqa
except NameError:
    EllipsisType = type(Ellipsis)
    __all__.append('EllipsisType')

try:
    DictProxyType    # noqa
except NameError:
    DictProxyType = type(type.__dict__)
    __all__.append('DictProxyType')

try:
    MappingProxyType    # noqa
except NameError:
    from xoutil.eight._types import MappingProxyType
    if MappingProxyType is not DictProxyType:
        MappingProxyType.register(DictProxyType)
    __all__.append('MappingProxyType')

try:
    NotImplementedType    # noqa
except NameError:
    NotImplementedType = type(NotImplemented)
    __all__.append('NotImplementedType')


# Check Jython and PyPy peculiarity
if MemberDescriptorType is GetSetDescriptorType:    # noqa
    class _foo(object):
        __slots__ = 'bar'
    MemberDescriptorType = type(_foo.bar)
    del _foo

FuncTypes = tuple({FunctionType, MethodType, LambdaType,    # noqa
                   BuiltinFunctionType, BuiltinMethodType})    # noqa

from xoutil.eight import class_types    # noqa
func_types = FuncTypes    # Just an alias

# These types are defined in `inspect` module for Python >= 3.3
MethodWrapperType = type(all.__call__)
WrapperDescriptorType = type(type.__call__)    # In PyPy is MethodWrapperType
ClassMethodWrapperType = type(dict.__dict__['fromkeys'])


try:
    SimpleNamespace    # noqa
except NameError:
    from abc import ABCMeta
    from xoutil.eight.meta import metaclass
    from xoutil.reprlib import recursive_repr

    __all__.append('SimpleNamespace')

[docs] class SimpleNamespace(metaclass(ABCMeta)): '''A simple attribute-based namespace. SimpleNamespace(**kwargs) ''' def __init__(self, **kwargs): self.__dict__.update(kwargs) def __eq__(self, other): # TODO: This method is not implemented in py33 ok = isinstance(other, SimpleNamespace) return ok and self.__dict__ == other.__dict__ @recursive_repr(str('namespace(...)')) def __repr__(self): keys = sorted(self.__dict__) items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys) return "{}({})".format('namespace', ", ".join(items))
del recursive_repr, ABCMeta, metaclass try: DynamicClassAttribute # noqa except NameError:
[docs] class DynamicClassAttribute(property): '''Route attribute access on a class to `~object.__getattr__`:meth:. This is a descriptor, used to define attributes that act differently when accessed through an instance and through a class. Instance access remains normal, but access to an attribute through a class will be routed to the class's `~object.__getattr__`:meth: method; this is done by raising `AttributeError`:class:. This allows one to have properties active on an instance, and have virtual attributes on the class with the same name (see `~py3:enum.Enum`:class: for an example). .. versionadded:: 1.5.5 .. versionchanged:: 1.8.0 Inherits from `property` .. note:: The class `Enum` mentioned has not yet been back-ported. .. note:: In Python version>=3.4 this is an alias to `types.DynamicClassAttribute <py3:types.DynamicClassAttribute>`:class:. ''' def __init__(self, fget=None, fset=None, fdel=None, doc=None): super(DynamicClassAttribute, self).__init__(fget, fset, fdel, doc) # support for abstract methods in Python 2 isabs = bool(getattr(fget, '__isabstractmethod__', False)) self.__isabstractmethod__ = isabs def __get__(self, obj, owner=None): if obj is None: if self.__isabstractmethod__: return self else: raise AttributeError() else: return super(DynamicClassAttribute, self).__get__(obj, owner)
__all__.append('DynamicClassAttribute') try: new_class # noqa except NameError: from xoutil.eight._types import new_class # noqa __all__.append('new_class') try: prepare_class # noqa except NameError: from xoutil.eight._types import prepare_class # noqa __all__.append('prepare_class') try: from types import _calculate_meta except ImportError: from xoutil.eight._types import _calculate_meta # noqa import re RegexPattern = type(re.compile('')) del re def _get_mro_attr(target, name, *default): '''Get a named attribute from a type. Similar to `getattr` but looking in the MRO dictionaries for the type. Used internally in this module. For example:: >>> class A(SimpleNamespace): ... x = 12 ... y = 34 >>> class B(A): ... y = 56 >>> b = B(x=1, y=2) >>> _get_mro_attr(b, 'x') 12 >>> _get_mro_attr(b, 'y') 56 ''' from xoutil.future.inspect import _static_getmro from xoutil.eight import force_type from xoutil.params import check_default, Undefined target = force_type(target) target_mro = _static_getmro(target) cls = next((c for c in target_mro if name in c.__dict__), _unset) if cls is not _unset: return cls.__dict__[name] elif check_default()(*default) is not Undefined: return default[0] else: msg = "'{}' type has no attribute '{}'" raise AttributeError(msg.format(target, name)) @deprecated(_get_mro_attr) class mro_dict(Mapping): '''Utility behaving like a read-only dict of `target` MRO attributes. Used internally in this module. For example:: >>> class A(object): ... x = 12 ... y = 34 >>> class B(A): ... y = 56 ... z = 78 >>> d = mro_dict(B) >>> d['x'] 12 >>> d['y'] 56 >>> d['z'] 78 .. deprecated:: 1.8.4 ''' __slots__ = ('_probes', '_keys') def __init__(self, target): from xoutil.future.inspect import _static_getmro from xoutil.eight import force_type as type_coerce type_ = type_coerce(target) target_mro = _static_getmro(type_) self._probes = tuple(c.__dict__ for c in target_mro) self._keys = set() def __getitem__(self, name): from xoutil.objects import get_first_of result = get_first_of(self._probes, name, default=_unset) if result is not _unset: return result else: raise KeyError(name) def __iter__(self): if not self._keys: self._settle_keys() return iter(self._keys) def __len__(self): if not self._keys: self._settle_keys() return len(self._keys) def _settle_keys(self): for probe in self._probes: for key in probe: if key not in self._keys: self._keys.add(key) @deprecated('None', '"mro_get_value_list" will be removed.') def mro_get_value_list(cls, name): '''Return a list with all `cls` class attributes in MRO. .. deprecated:: 1.8.4 ''' return list(mro_get_full_mapping(cls, name).values()) @deprecated('None', '"mro_get_full_mapping" will be removed.') def mro_get_full_mapping(cls, name): '''Return a dictionary with all items from `cls` in MRO. All values corresponding to `name` must be valid mappings. .. deprecated:: 1.8.4 ''' from xoutil.future.inspect import _static_getmro from xoutil.eight import force_type as type_coerce mro = _static_getmro(type_coerce(cls)) return {t: t.__dict__[name] for t in mro if name in t.__dict__} @deprecated('``iter(maybe)`` in an exception management block.') def is_iterable(maybe): '''Returns True if `maybe` is an iterable object. e.g. implements the `__iter__` method:: >>> is_iterable('all strings are iterable') True # Numbers are not >>> is_iterable(1) False >>> from xoutil.eight import range >>> is_iterable(range(1)) True >>> is_iterable({}) True >>> is_iterable(tuple()) True >>> is_iterable(set()) True .. deprecated:: 1.8.4 ''' try: iter(maybe) except TypeError: return False else: return True _is_collection_replacement = ''':: from xoutil.values.simple import collection, nil collection(avoid=Mapping)(maybe) is not nil ''' @deprecated(_is_collection_replacement) def is_collection(maybe): '''Test `maybe` to see if it is a tuple, a list, a set or a generator function. It returns False for dictionaries and strings:: >>> is_collection('all strings are iterable') False # Numbers are not >>> is_collection(1) False >>> from xoutil.eight import range >>> is_collection(range(1)) True >>> is_collection({}) False >>> is_collection(tuple()) True >>> is_collection(set()) True >>> is_collection(a for a in range(100)) True .. versionchanged:: 1.5.5 UserList are collections. .. deprecated:: 1.8.4 ''' from xoutil.values.simple import logic_collection_coerce, nil return logic_collection_coerce(maybe) is not nil @deprecated('``isinstance(maybe, Mapping)``') def is_mapping(maybe): '''Test `maybe` to see if it is a valid mapping. .. deprecated:: 1.8.4 ''' return isinstance(maybe, Mapping) @deprecated('``maybe + ""`` in an exception management block.') def is_string_like(maybe): '''Returns True if `maybe` acts like a string. .. deprecated:: 1.8.4 ''' try: maybe + "" except TypeError: return False else: return True @deprecated('None', '"is_scalar" will be removed.') def is_scalar(maybe): '''Returns if `maybe` is not not an iterable or a string. .. deprecated:: 1.8.4 ''' from collections import Iterable from xoutil.eight import string_types return isinstance(maybe, string_types) or not isinstance(maybe, Iterable) def is_staticmethod(cls, name): '''Returns true if a `method` is a static method. :param cls: The class or object that holds the method. :param name: The name of the method. When a static-method is declared, you can not test that condition using the traditional way:: >>> class Foo(object): ... @staticmethod ... def bar(): ... pass >>> isinstance(Foo.bar, staticmethod) False Using this function:: >>> is_staticmethod(Foo, 'bar') True ''' desc = _get_mro_attr(cls, name) return isinstance(desc, staticmethod) def is_classmethod(cls, name): '''Returns if a `method` is a class method. :param cls: The class or object that holds the method. :param name: The name of the method. When a class-method is declared, you can not test that condition using the traditional way:: >>> class Foo(object): ... @classmethod ... def bar(cls): ... pass >>> isinstance(Foo.bar, classmethod) False Using this function:: >>> is_classmethod(Foo, 'bar') True ''' desc = _get_mro_attr(cls, name) return isinstance(desc, classmethod) def is_instancemethod(cls, name): '''Returns if a `method` is neither a static nor a class method. :param cls: The class or object that holds the method. :param name: The name of the method. To find out if a method is "normal", ``isinstance(obj.method, MethodType)`` can't be used:: >>> class Foobar(object): ... @classmethod ... def cm(cls): ... pass ... def im(self): ... pass >>> isinstance(Foobar.cm, MethodType) True Using this function:: >>> is_instancemethod(Foobar, 'im') True >>> is_instancemethod(Foobar, 'cm') False ''' desc = _get_mro_attr(cls, name) return isinstance(desc, FunctionType) # noqa @deprecated('``isinstance(maybe, ModuleType)``') def is_module(maybe): '''Returns True if `maybe` is a module. .. deprecated:: 1.8.4 ''' return isinstance(maybe, ModuleType) # noqa @deprecated('``all(isinstance(obj, types) for obj in subjects)``') def are_instances(*args): '''Return True if every `subject` is an instance of (any) `types`. :param subjects: All but last positional arguments. Are the objects required to be instances of `types`. :param types: The last positional argument. Either a single type or a sequence of types. This must meet the conditions on the last argument of `isinstance`:func:. :returns: True or False. True if for every `subject`, ``isinstance(subject, types)`` is True. Otherwise, False. If no `subjects` are provided return True:: >>> are_instances(int) True .. seealso:: The function `no_instances`:func: allows to test for subjects not being instances of types. .. deprecated:: 1.8.4 ''' from xoutil.params import check_count check_count(args, 1, caller='are_instances') subjects, types = args[:-1], args[-1] if not subjects: isinstance(None, types) # HACK: always validate `types`. return all(isinstance(subject, types) for subject in subjects) @deprecated('``all(not isinstance(obj, types) for obj in subjects)``') def no_instances(*args): '''Return True if every `subject` is **not** an instance of (neither) `types`. :param subjects: All but last positional arguments. Are the objects required not to be instances of `types`. :param types: The last positional argument. Either a single type or a sequence of types. This must meet the conditions on the last argument of `isinstance`:func:. :returns: True or False. True if for every `subject`, ``isinstance(subject, types)`` is False. Otherwise, False. If no `subjects` are provided return True:: >>> no_instances(int) True .. note:: This is not the same as ``not are_instances(...)``. This function requires that *no* subject is an instance of `types`. Negating `are_instances`:func: would be True if *any* subject is not an instance of `types`. .. deprecated:: 1.8.4 ''' from xoutil.params import check_count check_count(args, 1, caller='no_instances') subjects, types = args[:-1], args[-1] if not subjects: isinstance(None, types) # HACK: always validate `types`. return all(not isinstance(subject, types) for subject in subjects) del deprecated, deprecate_linked