Source code for utool.util_type

# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals
import sys
import six
import functools
import re
import types
from utool import util_inject
from utool._internal.meta_util_six import IntType, LongType, FloatType, BooleanType
from utool._internal import meta_util_six
#import warnings
print, rrr, profile = util_inject.inject2(__name__, '[type]')


__STR__ = meta_util_six.__STR__


if six.PY2:
    def type_str(type_):
        str_ = str(type_)
        str_ = str_.replace('<type \'', '').replace('\'>', '')
        str_ = str_.replace('<class \'', '').replace('\'>', '')
        return str_
else:
[docs] def type_str(type_): return str(type_).replace('<class \'', '').replace('\'>', '') # Very odd that I have to put in dtypes in two different ways.
try: import numpy as np HAVE_NUMPY = True NUMPY_SCALAR_NAMES = sorted(list(set( (str_.replace('numpy.', '') for str_ in (type_str(type_) for type_ in np.ScalarType) if str_.startswith('numpy.') )))) VALID_INT_TYPES = (IntType, LongType, np.typeDict['int64'], np.typeDict['int32'], np.typeDict['uint8'], np.dtype('int32'), np.dtype('uint8'), np.dtype('int64'),) VALID_FLOAT_TYPES = (FloatType, np.typeDict['float64'], np.typeDict['float32'], np.typeDict['float16'], np.dtype('float64'), np.dtype('float32'), np.dtype('float16'),) VALID_BOOL_TYPES = (BooleanType, np.bool_) NP_NDARRAY = np.ndarray LISTLIKE_TYPES = (tuple, list, NP_NDARRAY) NUMPY_TYPE_TUPLE = ( tuple([NP_NDARRAY] + list(set(np.typeDict.values())))) except (ImportError, AttributeError): # TODO remove numpy HAVE_NUMPY = False VALID_INT_TYPES = (IntType, LongType,) VALID_FLOAT_TYPES = (FloatType,) VALID_BOOL_TYPES = (BooleanType,) LISTLIKE_TYPES = (tuple, list) NP_NDARRAY = None NUMPY_TYPE_TUPLE = tuple() PRIMATIVE_TYPES = ( tuple(six.string_types) + (bytes, list, dict, int, float, bool, type(None)) )
[docs]def is_valid_floattype(type_): """ Args: type_ (``type``): type to check Returns: bool: if a ``type_`` is a valid float ``type_`` (not variable) """ return type_ in VALID_FLOAT_TYPES #try: # #flags = [type_ == float_type for float_type in VALID_FLOAT_TYPES] # #return any(flags) # tried = [] # for float_type in VALID_FLOAT_TYPES: # tried.append(float_type) # if type_ == float_type: # return True # return False #except Exception: # print('tried=%r' % (tried,)) # print('type_=%r' % (type_,)) # print('float_type=%r' % (float_type,))
[docs]def try_cast(var, type_, default=None): if type_ is None: return var try: return smart_cast(var, type_) except Exception: return default
[docs]def smart_cast(var, type_): """ casts var to type, and tries to be clever when var is a string Args: var (object): variable to cast type_ (type or str): type to attempt to cast to Returns: object: CommandLine: python -m utool.util_type --exec-smart_cast Example: >>> # ENABLE_DOCTEST >>> from utool.util_type import * # NOQA >>> var = '1' >>> type_ = 'fuzzy_subset' >>> result = smart_cast(var, type_) >>> print(result) [1] """ if is_str(var): if type_ in VALID_BOOL_TYPES: return bool_from_str(var) elif type_ is slice: args = [None if len(arg) == 0 else int(arg) for arg in var.split(':')] return slice(*args) elif type_ is list: subvar_list = var.split(',') return [smart_cast2(subvar) for subvar in subvar_list] elif isinstance(type_, six.string_types): if type_ == 'fuzzy_subset': return fuzzy_subset(var) #elif type_ == 'fuzzy_int': # return fuzzy_subset(var) else: raise NotImplementedError('Uknown smart type_=%r' % (type_,)) return type_(var)
[docs]def smart_cast2(var): r""" if the variable is a string tries to cast it to a reasonable value. maybe can just use eval. FIXME: funcname Args: var (unknown): Returns: unknown: some var CommandLine: python -m utool.util_type --test-smart_cast2 Example: >>> # ENABLE_DOCTEST >>> from utool.util_type import * # NOQA >>> import utool as ut >>> # build test data >>> var_list = ['?', 1, '1', '1.0', '1.2', 'True', None, 'None'] >>> # execute function >>> castvar_list = [smart_cast2(var) for var in var_list] >>> # verify results >>> result = ut.list_str(castvar_list, nl=False) >>> print(result) ['?', 1, 1, 1.0, 1.2, True, None, None] """ if var is None: return None if isinstance(var, six.string_types): castvar = None lower = var.lower() if lower == 'true': return True elif lower == 'false': return False elif lower == 'none': return None if var.startswith('[') and var.endswith(']'): #import re #subvar_list = re.split(r',\s*' + ut.negative_lookahead(r'[^\[\]]*\]'), var[1:-1]) return smart_cast(var[1:-1], list) elif var.startswith('(') and var.endswith(')'): #import re #subvar_list = re.split(r',\s*' + ut.negative_lookahead(r'[^\[\]]*\]'), var[1:-1]) return tuple(smart_cast(var[1:-1], list)) type_list = [int, float] for type_ in type_list: castvar = try_cast(var, type_) if castvar is not None: break if castvar is None: castvar = var else: castvar = var return castvar
[docs]def bool_from_str(str_): lower = str_.lower() if lower == 'true': return True elif lower == 'false': return False else: raise TypeError('string does not represent boolean')
[docs]def fuzzy_subset(str_): """ converts a string into an argument to list_take """ if str_ is None: return str_ if ':' in str_: return smart_cast(str_, slice) if str_.startswith('['): return smart_cast(str_[1:-1], list) else: return smart_cast(str_, list)
[docs]def fuzzy_int(str_): """ lets some special strings be interpreted as ints """ try: ret = int(str_) return ret except Exception: # Parse comma separated values as ints if re.match(r'\d*,\d*,?\d*', str_): return tuple(map(int, str_.split(','))) # Parse range values as ints if re.match(r'\d*:\d*:?\d*', str_): return tuple(range(*map(int, str_.split(':')))) raise
[docs]def assert_int(var, lbl='var'): from utool.util_arg import NO_ASSERTS if NO_ASSERTS: return try: assert is_int(var), 'type(%s)=%r is not int' % (lbl, get_type(var)) except AssertionError: print('[tools] %s = %r' % (lbl, var)) print('[tools] VALID_INT_TYPES: %r' % VALID_INT_TYPES) raise
if HAVE_NUMPY: if sys.platform == 'win32': # Well this is a weird system specific error # https://github.com/numpy/numpy/issues/3667 def get_type(var): """Gets types accounting for numpy""" return var.dtype if isinstance(var, NP_NDARRAY) else type(var) else: def get_type(var): """Gets types accounting for numpy""" return var.dtype.type if isinstance(var, NP_NDARRAY) else type(var) else:
[docs] def get_type(var): return type(var)
[docs]def is_type(var, valid_types): """ Checks for types accounting for numpy """ #printDBG('checking type var=%r' % (var,)) #var_type = type(var) #printDBG('type is type(var)=%r' % (var_type,)) #printDBG('must be in valid_types=%r' % (valid_types,)) #ret = var_type in valid_types #printDBG('result is %r ' % ret) return get_type(var) in valid_types
[docs]def is_int(var): """ Returns: bool: True if var is an integer. Note: Yuck, isinstance(True, int) returns True. This function does not have that flaw. References: http://www.peterbe.com/plog/bool-is-int CommandLine: python -m utool.util_type --test-is_int Example: >>> # ENABLE_DOCTEST >>> from utool.util_type import * # NOQA >>> var1 = 1 >>> var2 = np.array([1, 2, 3]) >>> var3 = True >>> var4 = np.array([True, True, False]) >>> result = [is_int(var) for var in [var1, var2, var3, var4]] >>> print(result) [True, True, False, False] """ #if _newbehavior: # if is_bool(var): # msg = 'Comparing bool to int. Make sure legacy code does is updated accordingly.' # print('Warning: ' + msg) # warnings.warn(msg) # return False # else: # return is_type(var, VALID_INT_TYPES) #else: return is_type(var, VALID_INT_TYPES)
[docs]def is_float(var): r""" Args: var (ndarray or scalar): Returns: var: CommandLine: python -m utool.util_type --test-is_float Example: >>> # DISABLE_DOCTEST >>> from utool.util_type import * # NOQA >>> # build test data >>> var = np.array([1.0, 2.0, 3.0]) >>> # execute function >>> assert is_float(var) is True, 'var is a float' >>> # verify results >>> print(result) """ return is_type(var, VALID_FLOAT_TYPES)
[docs]def is_str(var): return isinstance(var, six.string_types) #return is_type(var, VALID_STRING_TYPES)
[docs]def is_bool(var): return isinstance(var, VALID_BOOL_TYPES)
[docs]def is_dict(var): return isinstance(var, dict)
[docs]def is_list(var): return isinstance(var, list)
[docs]def is_listlike(var): return isinstance(var, LISTLIKE_TYPES)
[docs]def is_tuple(var): return isinstance(var, tuple)
[docs]def is_method(var): return isinstance(var, (types.MethodType,))
[docs]def is_func_or_method(var): return isinstance(var, (types.MethodType, types.FunctionType))
[docs]def is_func_or_method_or_partial(var): return isinstance(var, (types.MethodType, types.FunctionType, functools.partial))
[docs]def is_funclike(var): return hasattr(var, '__call__') #def get_list_type(list_): # if isinstance(list_, np.ndarray): # return list_.dtype # pass
[docs]def get_homogenous_list_type(list_): """ Returns the best matching python type even if it is an ndarray assumes all items in the list are of the same type. does not check this """ # TODO Expand and make work correctly if HAVE_NUMPY and isinstance(list_, np.ndarray): item = list_ elif isinstance(list_, list) and len(list_) > 0: item = list_[0] else: item = None if item is not None: if is_float(item): type_ = float elif is_int(item): type_ = int elif is_bool(item): type_ = bool elif is_str(item): type_ = str else: type_ = get_type(item) else: type_ = None return type_
if __name__ == '__main__': """ CommandLine: python -m utool.util_type python -m utool.util_type --allexamples python -m utool.util_type --allexamples --noface --nosrc """ import multiprocessing multiprocessing.freeze_support() # for win32 import utool as ut # NOQA ut.doctest_funcs()