Source code for utool.util_class

# -*- coding: utf-8 -*-
"""
In this module:
    * a metaclass allowing for reloading of single class instances
    * functions to autoinject methods into a class upon instance creation.
    * A wrapper class allowing an object's properties to be used as kwargs
    * a metaclass to forward properties to another class

    ReloadingMetaclass
    KwargsWrapper
"""
from __future__ import absolute_import, division, print_function
import sys
import six
import types
import functools
import collections
from collections import defaultdict
from utool import util_inject
from utool import util_set
from utool import util_arg
from utool._internal.meta_util_six import get_funcname
print, rrr, profile = util_inject.inject2(__name__, '[class]', DEBUG=False)


# Registers which classes have which attributes
# FIXME: this might cause memory leaks
# FIXME: this does cause weird reimport behavior
__CLASSTYPE_ATTRIBUTES__ = defaultdict(util_set.oset)
__CLASSTYPE_POSTINJECT_FUNCS__ = defaultdict(util_set.oset)
__CLASSNAME_CLASSKEY_REGISTER__ = defaultdict(util_set.oset)


#_rrr = rrr
#def rrr(verbose=True):
#    """ keep registered functions through reloads ? """
#    global __CLASSTYPE_ATTRIBUTES__
#    global __CLASSTYPE_POSTINJECT_FUNCS__
#    cta = __CLASSTYPE_ATTRIBUTES__.copy()
#    ctpif = __CLASSTYPE_POSTINJECT_FUNCS__.copy()
#    rrr_(verbose=verbose)
#    __CLASSTYPE_ATTRIBUTES__ = cta
#    __CLASSTYPE_POSTINJECT_FUNCS__ = ctpif


QUIET_CLASS = util_arg.get_argflag(('--quiet-class', '--quietclass'))
VERBOSE_CLASS = (
    util_arg.get_argflag(('--verbose-class', '--verbclass')) or
    (not QUIET_CLASS and util_arg.VERYVERBOSE))


[docs]def inject_instance(self, classkey=None, allow_override=False, verbose=VERBOSE_CLASS, strict=True): """ Injects an instance (self) of type (classkey) with all functions registered to (classkey) call this in the __init__ class function Args: self: the class instance classkey: key for a class, preferably the class type itself, but it doesnt have to be SeeAlso: make_class_method_decorator Example: >>> # DOCTEST_DISABLE >>> utool.make_class_method_decorator(InvertedIndex)(smk_debug.invindex_dbgstr) >>> utool.inject_instance(invindex) """ import utool as ut if verbose: print('[util_class] begin inject_instance') try: if classkey is None: # Probably should depricate this block of code # It tries to do too much classkey = self.__class__ if classkey == 'ibeis.gui.models_and_views.IBEISTableView': # HACK HACK HACK from guitool.__PYQT__ import QtGui classkey = QtGui.QAbstractItemView if len(__CLASSTYPE_ATTRIBUTES__[classkey]) == 0: print('[utool] Warning: no classes of type %r are registered' % (classkey,)) print('[utool] type(self)=%r, self=%r' % (type(self), self)), print('[utool] Checking to see if anybody else was registered...') print('[utool] __CLASSTYPE_ATTRIBUTES__ = ' + ut.list_str(__CLASSTYPE_ATTRIBUTES__.keys())) for classtype_, _ in six.iteritems(__CLASSTYPE_ATTRIBUTES__): isinstance(self, classtype_) classkey = classtype_ print('[utool] Warning: using subclass=%r' % (classtype_,)) break func_list = __CLASSTYPE_ATTRIBUTES__[classkey] if verbose: print('[util_class] injecting %d methods\n with classkey=%r\n into %r' % (len(func_list), classkey, self,)) for func in func_list: if VERBOSE_CLASS: print('[util_class] * injecting %r' % (func,)) method_name = None # Allow user to register tuples for aliases if isinstance(func, tuple): func, method_name = func inject_func_as_method(self, func, method_name=method_name, allow_override=allow_override, verbose=verbose) except Exception as ex: ut.printex(ex, 'ISSUE WHEN INJECTING %r' % (classkey,), iswarning=not strict) if strict: raise
[docs]def postinject_instance(self, classkey, verbose=VERBOSE_CLASS): if verbose: print('[util_class] Running postinject functions on %r' % (self,)) for func in __CLASSTYPE_POSTINJECT_FUNCS__[classkey]: func(self) if verbose: print('[util_class] Finished injecting instance self=%r' % (self,))
[docs]def inject_all_external_modules(self, classname=None, allow_override='override+warn', strict=True): """ dynamically injects registered module methods into a class instance FIXME: naming convention and use this in all places where this clas is used """ #import utool as ut if classname is None: classname = self.__class__.__name__ #import utool as ut #ut.embed() NEW = True if NEW: classkey_list = [key for key in __CLASSTYPE_ATTRIBUTES__ if key[0] == classname] else: injected_modules = get_injected_modules(classname) # the variable must be named CLASS_INJECT_KEY # and only one class can be specified per module. classkey_list = [module.CLASS_INJECT_KEY for module in injected_modules] for classkey in classkey_list: inject_instance( self, classkey=classkey, allow_override=allow_override, strict=False) for classkey in classkey_list: postinject_instance( self, classkey=classkey)
[docs]def reload_injected_modules(classname): injected_modules = get_injected_modules(classname) for module in injected_modules: if hasattr(module, 'rrr'): module.rrr() else: import imp print('rrr not defined in module=%r' % (module,)) imp.reload(module)
[docs]def get_injected_modules(classname): r""" Example: >>> # DISABLE_DOCTEST >>> from utool.util_class import __CLASSNAME_CLASSKEY_REGISTER__ # NOQA """ modname_list = __CLASSNAME_CLASSKEY_REGISTER__[classname] injected_modules = [] for modstr in modname_list: parts = modstr.split('.') pkgname = '.'.join(parts[:-1]) modname = parts[-1] try: exec('from %s import %s' % (pkgname, modname, ), globals(), locals()) module = eval(modname) injected_modules.append(module) except ImportError as ex: ut.printex(ex, 'Cannot load package=%r, module=%r' % (pkgname, modname, )) return injected_modules
[docs]def autogen_import_list(classname, conditional_imports=None): import utool as ut #ut.embed() #line_list = [] line_list = ['import sys # NOQA'] for modname in __CLASSNAME_CLASSKEY_REGISTER__[classname]: # <super hacky> condition = None for x in conditional_imports: if modname == x[1]: condition = x[0] # </super hacky> parts = modname.split('.') frompart = '.'.join(parts[:-1]) imppart = parts[-1] #line = 'from %s import %s # NOQA' % (frompart, imppart) if condition is None: line = 'from %s import %s' % (frompart, imppart) else: line = ut.codeblock( ''' if not ut.get_argflag({condition}) or '{frompart}' in sys.modules: from {frompart} import {imppart} ''').format(condition=condition, frompart=frompart, imppart=imppart) line_list.append(line) src = '\n'.join(line_list) return src
[docs]def autogen_explicit_injectable_metaclass(classname, regen_command=None, conditional_imports=None): r""" Args: classname (?): Returns: ?: CommandLine: python -m utool.util_class --exec-autogen_explicit_injectable_metaclass Example: >>> # DISABLE_DOCTEST >>> from utool.util_class import * # NOQA >>> from utool.util_class import __CLASSTYPE_ATTRIBUTES__ # NOQA >>> import ibeis >>> import ibeis.control.IBEISControl >>> classname = ibeis.control.controller_inject.CONTROLLER_CLASSNAME >>> result = autogen_explicit_injectable_metaclass(classname) >>> print(result) """ import utool as ut vals_list = [] def make_redirect(func): # PRESERVES ALL SIGNATURES WITH EXECS src_fmt = r''' def {funcname}{defsig}: """ {orig_docstr}""" return {orig_funcname}{callsig} ''' from utool._internal import meta_util_six orig_docstr = meta_util_six.get_funcdoc(func) funcname = meta_util_six.get_funcname(func) orig_funcname = modname.split('.')[-1] + '.' + funcname orig_docstr = '' if orig_docstr is None else orig_docstr import textwrap # Put wrapped function into a scope import inspect argspec = inspect.getargspec(func) (args, varargs, varkw, defaults) = argspec defsig = inspect.formatargspec(*argspec) callsig = inspect.formatargspec(*argspec[0:3]) src_fmtdict = dict(funcname=funcname, orig_funcname=orig_funcname, defsig=defsig, callsig=callsig, orig_docstr=orig_docstr) src = textwrap.dedent(src_fmt).format(**src_fmtdict) return src src_list = [] for classkey, vals in __CLASSTYPE_ATTRIBUTES__.items(): modname = classkey[1] if classkey[0] == classname: vals_list.append(vals) for func in vals: src = make_redirect(func) src = ut.indent(src) src = '\n'.join([_.rstrip() for _ in src.split('\n')]) src_list.append(src) if regen_command is None: regen_command = 'FIXME None given' module_header = ut.codeblock( """ # -*- coding: utf-8 -*- """ + ut.TRIPLE_DOUBLE_QUOTE + """ Static file containing autogenerated functions for {classname} Autogenerated on {autogen_time} RegenCommand: {regen_command} """ + ut.TRIPLE_DOUBLE_QUOTE + """ from __future__ import absolute_import, division, print_function import utool as ut """).format( autogen_time=ut.get_timestamp(), regen_command=regen_command, classname=classname) depends_module_block = autogen_import_list(classname, conditional_imports) inject_statement_fmt = ("print, rrr, profile = " "ut.inject2(__name__, '[autogen_explicit_inject_{classname}]')") inject_statement = inject_statement_fmt.format(classname=classname) source_block_lines = [ module_header, depends_module_block, inject_statement, '\n', 'class ExplicitInject' + classname + '(object):', ] + src_list source_block = '\n'.join(source_block_lines) source_block = ut.autoformat_pep8(source_block, aggressive=2) return source_block
[docs]def make_class_method_decorator(classkey, modname=None): """ register a class to be injectable classkey is a key that identifies the injected class REMEMBER to call inject_instance in __init__ Args: classkey : the class to be injected into modname : the global __name__ of the module youa re injecting from Returns: closure_decorate_class_method (func): decorator for injectable methods Example: >>> # ENABLE_DOCTEST >>> import utool as ut >>> class CheeseShop(object): ... def __init__(self): ... import utool as ut ... ut.inject_all_external_modules(self) >>> cheeseshop_method = ut.make_class_method_decorator(CheeseShop) >>> shop1 = CheeseShop() >>> assert not hasattr(shop1, 'has_cheese'), 'have not injected yet' >>> @cheeseshop_method >>> def has_cheese(self): >>> return False >>> shop2 = CheeseShop() >>> assert shop2.has_cheese() is False, 'external method not injected' >>> print('Cheese shop does not have cheese. All is well.') """ global __APP_MODNAME_REGISTER__ #if util_arg.VERBOSE or VERBOSE_CLASS: if VERBOSE_CLASS: print('[util_class] register via make_class_method_decorator classkey=%r, modname=%r' % (classkey, modname)) if modname == '__main__': # skips reinjects into main print('WARNING: cannot register classkey=%r functions as __main__' % (classkey,)) return lambda func: func # register that this module was injected into if isinstance(classkey, tuple): classname, _ = classkey __CLASSNAME_CLASSKEY_REGISTER__[classname].append(modname) elif isinstance(classkey, type): classname = classkey.__name__ if modname is not None: assert modname == classkey.__module__, ( 'modname=%r does not agree with __module__=%r' % ( modname, classkey.__module__)) modname = classkey.__module__ # Convert to new classkey format classkey = (classname, modname) __CLASSNAME_CLASSKEY_REGISTER__[classname].append(modname) else: print('Warning not using classkey for %r %r' % (classkey, modname)) raise AssertionError('classkey no longer supported. Use class_inject_key instead') closure_decorate_class_method = functools.partial(decorate_class_method, classkey=classkey) return closure_decorate_class_method
[docs]def make_class_postinject_decorator(classkey, modname=None): """ Args: classkey : the class to be injected into modname : the global __name__ of the module youa re injecting from Returns: closure_decorate_postinject (func): decorator for injectable methods SeeAlso: make_class_method_decorator """ if util_arg.VERBOSE or VERBOSE_CLASS: print('[util_class] register class_postinject classkey=%r, modname=%r' % (classkey, modname)) if modname == '__main__': print('WARNING: cannot register class functions as __main__') # skips reinjects into main return lambda func: func closure_decorate_postinject = functools.partial(decorate_postinject, classkey=classkey) return closure_decorate_postinject
[docs]def decorate_class_method(func, classkey=None, skipmain=False): """ Will inject all decorated function as methods of classkey classkey is some identifying string, tuple, or object func can also be a tuple """ #import utool as ut global __CLASSTYPE_ATTRIBUTES__ assert classkey is not None, 'must specify classkey' #if not (skipmain and ut.get_caller_modname() == '__main__'): __CLASSTYPE_ATTRIBUTES__[classkey].append(func) return func
[docs]def decorate_postinject(func, classkey=None, skipmain=False): """ Will perform func with argument self after inject_instance is called on classkey classkey is some identifying string, tuple, or object """ #import utool as ut global __CLASSTYPE_POSTINJECT_FUNCS__ assert classkey is not None, 'must specify classkey' #if not (skipmain and ut.get_caller_modname() == '__main__'): __CLASSTYPE_POSTINJECT_FUNCS__[classkey].append(func) return func
[docs]def inject_func_as_method(self, func, method_name=None, class_=None, allow_override=False, allow_main=False, verbose=True): """ Injects a function into an object as a method Wraps func as a bound method of self. Then injects func into self It is preferable to use make_class_method_decorator and inject_instance Args: self (object): class instance func : some function whos first arugment is a class instance method_name (str) : default=func.__name__, if specified renames the method class_ (type) : if func is an unbound method of this class References: http://stackoverflow.com/questions/1015307/python-bind-an-unbound-method """ if method_name is None: method_name = get_funcname(func) #printDBG('Injecting method_name=%r' % method_name) old_method = getattr(self, method_name, None) #import utool as ut #ut.embed() # Bind function to the class instance #new_method = types.MethodType(func, self, self.__class__) new_method = func.__get__(self, self.__class__) #new_method = profile(func.__get__(self, self.__class__)) if old_method is not None: if not allow_main and ( old_method.im_func.func_globals['__name__'] != '__main__' and new_method.im_func.func_globals['__name__'] == '__main__'): if True or VERBOSE_CLASS: print('[util_class] skipping re-inject of %r from __main__' % method_name) return if old_method is new_method or old_method.im_func is new_method.im_func: if verbose and util_arg.NOT_QUIET: print('WARNING: Injecting the same function twice: %r' % new_method) elif allow_override is False: raise AssertionError( 'Overrides are not allowed. Already have method_name=%r' % (method_name)) elif allow_override == 'warn': print( 'WARNING: Overrides are not allowed. Already have method_name=%r. Skipping' % (method_name)) return elif allow_override == 'override+warn': #import utool as ut #ut.embed() print('WARNING: Overrides are allowed, but dangerous. method_name=%r.' % (method_name)) print('old_method = %r, im_func=%s' % (old_method, str(old_method.im_func))) print('new_method = %r, im_func=%s' % (new_method, str(new_method.im_func))) print(old_method.im_func.func_globals['__name__']) print(new_method.im_func.func_globals['__name__']) # TODO: does this actually decrement the refcount enough? del old_method setattr(self, method_name, new_method)
[docs]def makeForwardingMetaclass(forwarding_dest_getter, whitelist, base_class=object): """ makes a metaclass that overrides __getattr__ and __setattr__ to forward some specific attribute references to a specified instance variable """ class ForwardingMetaclass(base_class.__class__): def __init__(metaself, name, bases, dct): # print('ForwardingMetaclass.__init__(): # {forwarding_dest_getter: %r; whitelist: %r}' % (forwarding_dest_getter, whitelist)) super(ForwardingMetaclass, metaself).__init__(name, bases, dct) old_getattr = metaself.__getattribute__ old_setattr = metaself.__setattr__ def new_getattr(self, item): if item in whitelist: #dest = old_getattr(self, forwarding_dest_name) dest = forwarding_dest_getter(self) try: val = dest.__class__.__getattribute__(dest, item) except AttributeError: val = getattr(dest, item) else: val = old_getattr(self, item) return val def new_setattr(self, name, val): if name in whitelist: #dest = old_getattr(self, forwarding_dest_name) dest = forwarding_dest_getter(self) dest.__class__.__setattr__(dest, name, val) else: old_setattr(self, name, val) metaself.__getattribute__ = new_getattr metaself.__setattr__ = new_setattr return ForwardingMetaclass
[docs]def test_reloading_metaclass(): r""" CommandLine: python -m utool.util_class --test-test_reloading_metaclass References: http://stackoverflow.com/questions/8122734/pythons-imp-reload-function-is-not-working Example: >>> # ENABLE_DOCTEST >>> from utool.util_class import * # NOQA >>> result = test_reloading_metaclass() >>> print(result) """ import utool as ut testdir = ut.ensure_app_resource_dir('utool', 'metaclass_tests') testfoo_fpath = ut.unixjoin(testdir, 'testfoo.py') # os.chdir(testdir) #with ut.ChdirContext(testdir, stay=ut.inIPython()): with ut.ChdirContext(testdir): foo_code1 = ut.codeblock( r''' # STARTBLOCK import utool as ut import six @six.add_metaclass(ut.ReloadingMetaclass) class Foo(object): def __init__(self): pass spamattr = 'version1' # ENDBLOCK ''' ) foo_code2 = ut.codeblock( r''' # STARTBLOCK import utool as ut import six @six.add_metaclass(ut.ReloadingMetaclass) class Foo(object): def __init__(self): pass def bar(self): return 'spam' eggsattr = 'version2' # ENDBLOCK ''' ) # Write a testclass to disk ut.delete(testfoo_fpath) ut.write_to(testfoo_fpath, foo_code1, verbose=True) testfoo = ut.import_module_from_fpath(testfoo_fpath) #import testfoo foo = testfoo.Foo() print('foo = %r' % (foo,)) assert not hasattr(foo, 'bar'), 'foo should not have a bar attr' ut.delete(testfoo_fpath + 'c') # remove the pyc file because of the identical creation time ut.write_to(testfoo_fpath, foo_code2, verbose=True) assert not hasattr(foo, 'bar'), 'foo should still not have a bar attr' foo.rrr() assert foo.bar() == 'spam' ut.delete(testfoo_fpath) print('Reloading worked nicely')
[docs]class ReloadingMetaclass(type): """ Classes with this metaclass will be able to reload themselves on a per-instance basis using the rrr function. If the functions _on_reload and _initialize_self exist they will be called after and before reload respectively. Any inject_instance functions should be handled there. SeeAlso: test_reloading_metaclass - shows a working example of this doctest Example: >>> # DIABLE_DOCTEST >>> from utool.util_class import * # NOQA >>> import utool as ut >>> @six.add_metaclass(ut.ReloadingMetaclass) >>> class Foo(object): ... def __init__(self): ... pass >>> # You can edit foo on disk and call rrr in ipython >>> # if you add a new function to it >>> foo = Foo() >>> # This will not work as a doctests because >>> # Foo's parent module will be __main__ but >>> # there will be no easy way to write to it. >>> # This does work when you run from ipython >>> @six.add_metaclass(ut.ReloadingMetaclass) >>> class Foo(object): ... def __init__(self): ... pass ... def bar(self): ... return 'spam' >>> foo.rrr() >>> result = foo.bar() >>> print(result) spam """ def __init__(metaself, name, bases, dct): super(ReloadingMetaclass, metaself).__init__(name, bases, dct) rrr = private_rrr_factory() metaself.rrr = rrr
[docs]def private_rrr_factory(): def rrr(self, verbose=True): """ special class reloading function """ import utool as ut verbose = verbose or VERBOSE_CLASS classname = self.__class__.__name__ try: modname = self.__class__.__module__ if verbose: print('[class] reloading ' + classname + ' from ' + modname) # --HACK-- if hasattr(self, '_on_reload'): self._on_reload() NEW = True if NEW: # Do for all inheriting classes def find_base_clases(_class, find_base_clases=None): class_list = [] for _baseclass in _class.__bases__: class_list.extend(find_base_clases(_baseclass, find_base_clases)) if _class is not object: class_list.append(_class) return class_list head_class = self.__class__ class_list = find_base_clases(head_class, find_base_clases) for _class in class_list: if verbose: print('[class] reloading parent ' + _class.__name__ + ' from ' + _class.__module__) if _class.__module__ != '__main__': module_ = sys.modules[_class.__module__] else: # Attempt to find the module that is the main module # This may be very hacky and potentially break main_module_ = sys.modules[_class.__module__] main_modname = ut.get_modname_from_modpath(main_module_.__file__) module_ = sys.modules[main_modname] #ut.embed() #print('[class!] CANT RELOAD CLASS FROM MAIN MODULE') if hasattr(module_, 'rrr'): module_.rrr(verbose=verbose) else: import imp if verbose: print('[class] reloading ' + _class.__module__ + ' with imp') try: imp.reload(module_) except (ImportError, AttributeError): print('[class] fallback reloading ' + _class.__module__ + ' with imp') # one last thing to try. probably used ut.import_module_from_fpath # when importing this module imp.load_source(module_.__name__, module_.__file__) _newclass = getattr(module_, _class.__name__) reload_class_methods(self, _newclass, verbose=verbose) else: # -------- # Reload the parent module if it is not main module = sys.modules[modname] if modname != '__main__': if hasattr(module, 'rrr'): module.rrr() else: import imp imp.reload(module) # -------- # Reload parent classes (if inherited) # TODO: figure out how to do this #for _baseclass in self.__class__.__bases__: # if hasattr(_baseclass, 'rrr'): # print('Reloading parent: %r' % (_baseclass)) # # make a bound rrr method that belongs to the parent instance # base_rrr = _baseclass.rrr.__get__(self, _baseclass) # base_rrr(verbose=verbose) # Get new class definition class_ = getattr(module, classname) reload_class_methods(self, class_, verbose=verbose) # --HACK-- # TODO: handle injected definitions if hasattr(self, '_initialize_self'): self._initialize_self() except Exception as ex: import utool as ut ut.printex(ex, 'Error Reloading Class', keys=[ 'modname', 'module', 'class_', 'class_list', 'self', ]) #print(ut.dict_str(module.__dict__)) raise return rrr
[docs]def reloading_meta_metaclass_factory(BASE_TYPE=type): """ hack for pyqt """ class ReloadingMetaclass2(BASE_TYPE): def __init__(metaself, name, bases, dct): super(ReloadingMetaclass2, metaself).__init__(name, bases, dct) #print('Making rrr for %r' % (name,)) rrr = private_rrr_factory() metaself.rrr = rrr return ReloadingMetaclass2
[docs]def reload_class_methods(self, class_, verbose=True): """ rebinds all class methods Args: self (object): class instance to reload class_ (type): type to reload as Example: >>> from utool.util_class import * # NOQA >>> self = '?' >>> class_ = '?' >>> result = reload_class_methods(self, class_) >>> print(result) """ if verbose: print('[util_class] Reloading self=%r as class_=%r' % (self, class_)) self.__class__ = class_ for key in dir(class_): # Get unbound reloaded method func = getattr(class_, key) if isinstance(func, types.MethodType): # inject it into the old instance inject_func_as_method(self, func, class_=class_, allow_override=True, verbose=verbose)
[docs]def get_comparison_methods(): """ makes methods for >, <, =, etc... """ method_list = [] def _register(func): method_list.append(func) return func # Comparison operators for sorting and uniqueness @_register def __lt__(self, other): return self.__hash__() < (other.__hash__()) @_register def __le__(self, other): return self.__hash__() <= (other.__hash__()) @_register def __eq__(self, other): return self.__hash__() == (other.__hash__()) @_register def __ne__(self, other): return self.__hash__() != (other.__hash__()) @_register def __gt__(self, other): return self.__hash__() > (other.__hash__()) @_register def __ge__(self, other): return self.__hash__() >= (other.__hash__()) return method_list
[docs]class HashComparableMetaclass(type): """ Defines extra methods for Configs """ def __new__(cls, name, bases, dct): """ Args: cls (type): meta name (str): classname supers (list): bases dct (dict): class dictionary """ method_list = get_comparison_methods() for func in method_list: if get_funcname(func) not in dct: funcname = get_funcname(func) dct[funcname] = func else: funcname = get_funcname(func) dct['meta_' + funcname] = func #ut.inject_func_as_method(metaself, func) return type.__new__(cls, name, bases, dct)
@six.add_metaclass(HashComparableMetaclass)
[docs]class HashComparable(object): pass
[docs]def reloadable_class(cls): """ convinience decorator instead of @six.add_metaclass(ReloadingMetaclass) """ return six.add_metaclass(ReloadingMetaclass)(cls)
[docs]class KwargsWrapper(collections.Mapping): """ Allows an arbitrary object attributes to be passed as a **kwargs argument """ def __init__(self, obj): self.obj = obj def __getitem__(self, key): return self.obj.__dict__[key] def __iter__(self): return iter(self.obj.__dict__) def __len__(self): return len(self.obj.__dict__)
[docs]def remove_private_obfuscation(self): """ removes the python obfuscation of class privates so they can be executed as they appear in class source. Useful when playing with IPython. """ classname = self.__class__.__name__ attrlist = [attr for attr in dir(self) if attr.startswith('_' + classname + '__')] for attr in attrlist: method = getattr(self, attr) truename = attr.replace('_' + classname + '__', '__') setattr(self, truename, method)
[docs]def get_classname(class_, local=False): r""" Args: class_ (type): local (bool): (default = False) Returns: str: classname CommandLine: python -m utool.util_class --exec-get_classname --show Example: >>> # DISABLE_DOCTEST >>> from utool.util_class import * # NOQA >>> import utool as ut >>> class_ = ReloadingMetaclass >>> local = False >>> assert get_classname(class_, local) == 'utool.util_class.ReloadingMetaclass' >>> assert get_classname(class_, local=True) == 'ReloadingMetaclass' """ if not local: classname = class_.__module__ + '.' + class_.__name__ else: classname = class_.__name__ return classname
if __name__ == '__main__': """ python -c "import utool; utool.doctest_funcs(utool.util_class, allexamples=True)" python -m utool.util_class --allexamples """ import multiprocessing multiprocessing.freeze_support() import utool as ut ut.doctest_funcs()