#!/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