Source code for mcgdb.toolbox.python_utils

"""
Module with Python utility functions.
"""

import inspect
import itertools
import traceback
import logging;
log = logging.getLogger(__name__)
log_import = logging.getLogger("mcgdb.log.structure.import")
log_user = logging.getLogger("mcgdb.log.user")

import sys

import gdb
import mcgdb.interaction

try:
    from . import colors
except ImportError:    
    pass

try:
    from profilehooks import profile as profile_decorator # https://github.com/mgedmin/profilehooks
except ImportError as e:
    log_import.error("Cannot import profilehooks package, mcgdb profiling will not be available. ({})".format(e))
    profile_decorator = None

DO_PROFILE = False
PROFILE_TO_STDOUT = True

if DO_PROFILE and profile_decorator:
    profile_to_stdout = PROFILE_TO_STDOUT
    def profile(*args, **kw):
        class my_bool():
            def __init__(self): pass
            
            def __bool__(self):
                return profile_to_stdout
            
        def inner(fn):
            print("###################")
            kw["stdout"] = my_bool()
            pr = profile_decorator(fn, *args, **kw)
            
            return pr
        return inner
else:
[docs] def profile(*args, **kw): #NOP decorator return lambda x : x
[docs]def error(msg): """ Raise a GDB error. :param msg: Reason of the error. """ raise gdb.Error(string)
[docs]def docstring_extension(doc_ext): def inner(decoratee): if hasattr(decoratee, "func_doc"): # function decoratee.func_doc = (doc_ext or "") + (decoratee.func_doc or "") else: # class try: decoratee.__doc__ = (doc_ext or "") + (decoratee.__doc__ or "") except: # AttributeError: attribute '__doc__' of 'type' objects is not writable pass return decoratee return inner
[docs]def class_decorator(deco): def inner(clazz): try: clazz.__doc__ = "{}\nClass decorator *{}*:{}".format( clazz.__doc__ or "", deco.__name__, deco.func_doc) except AttributeError: # attribute '__doc__' of 'type' objects is not writable pass return deco(clazz) inner.__doc__ = deco.__doc__ return inner
virtual = docstring_extension("""Virtual method.""") internal = docstring_extension("""Internal function.""") deprecated = docstring_extension("""Deprecated function.""") fct_decorator = docstring_extension("""Decorator.""") hugly = docstring_extension("""Hugly implementation, please forgive me.""") generator = docstring_extension("""Generator.""")
[docs]def refer_to(obj): def inner(clazz): try: clazz.__doc__ = (clazz.__doc__ or "") + "Refer to " + str(obj) except AttributeError: # attribute '__doc__' of 'type' objects is not writable pass return clazz return inner
@fct_decorator ##################################### @internal
[docs]class Enum(set): def __getattr__(self, name): if name in self: return name elif name == set.add: return set.add raise AttributeError
@class_decorator
[docs]def Numbered(clazz): """ Gives a unique number identifier to class instances. See :py:class:`numbered_example`. """ clazz.last_number = 0 orig_init = clazz.__init__ def new_init (self, *args, **kws): clazz.last_number += 1 if not hasattr(self, 'numbers'): self.numbers = {} self.numbers[clazz] = clazz.last_number if self.__class__ is clazz: self.number = clazz.last_number orig_init(self, *args, **kws) clazz.__init__ = new_init return clazz
@internal @Numbered
[docs]class SimpleClass(dict): """ A simple class / advanced dictionnary class that provided its elements through named parameters. """ def __init__(self, *args, **kwargs): super(SimpleClass, self).__init__(*args, **kwargs) number = self.number self.__dict__ = self self.number = number # otherwise it gets lost def __hash__(self): return self.number
@class_decorator
[docs]def Switchable(clazz): """ Adds generic switching capability to the class. See :py:class:`switchable_example`. """ @virtual def switch(self, force=False): """ Switch context to this entity. """ return None @virtual def is_current(self): """ :returns: True if the entity is active in the context. """ return False if not hasattr(clazz, "switch"): setattr(clazz, "switch", switch) if not hasattr(clazz, "is_current"): setattr(clazz, "is_current", is_current) return clazz
@class_decorator
[docs]def Listed(clazz): """ Adds a listing capability to the class. See :py:class:`listed_example`. """ clazz.list_ = [] orig_init = clazz.__init__ def new_init (self, *args, **kws): clazz.list_.append(self) orig_init(self, *args, **kws) clazz.__init__ = new_init return clazz
@class_decorator
[docs]def Dicted(clazz): """ Adds dictionnary capabilities to the class. See :py:class:`dicted_example`. """ clazz.dict_ = {} @classmethod def init_dict (cls, key, self): """ Add the current object to its class dictionnary (in `__init__`). :param key: Unique id of this object instance. :param self: Object instance put in the dict. """ cls.dict_[key] = self if not hasattr(clazz, "init_dict"): setattr(clazz, "init_dict", init_dict) @classmethod def key_to_value(cls, key): """ Get object identified with key `key`. :param key: key to lookup. """ if key in cls.dict_.keys(): return cls.dict_[key] else: return None if not hasattr(clazz, "key_to_value"): setattr(clazz, "key_to_value", key_to_value) return clazz
################## # examples for generated documentation @Dicted
[docs]class dicted_example(): pass
@Listed
[docs]class listed_example(): def __init__(self): pass
@Numbered
[docs]class numbered_example(): def __init__(self): pass
@Switchable
[docs]class switchable_example(): pass
################## @internal
[docs]def colored(string, color): """ Colorize a string. :param string: the text to colorize. :param color: the color to apply to the text. :returns: the colored string if we managed to, or the original string. """ try: return colors.colored(string, color) except Exception: return string
@internal
[docs]class Observer: def __init__(self): self.list_ = []
[docs] def connect(self, handler): self.list_.append(handler)
[docs] def disconnect(self, handler): self.list_.remove(handler)
[docs] def trigger(self, arg=None): for handler in self.list_: handler(arg)
@internal
[docs]class Events(): def __init__(self): pass bp_stop = Observer() cont = Observer()
events = Events() ################################################ @internal
[docs]def info(_type, value, tb): if _type != AssertionError: # we are in interactive mode or we don't have a tty-like # device, so we call the default hook sys.__excepthook__(_type, value, tb) else: import traceback, pdb # we are NOT in interactive mode, print the exception... traceback.print_exception(_type, value, tb) pdb.pm()
################################################ package = None initialized = set() initializing = set()
[docs]def initializePackage(pkg): global package package = pkg initialized.add(pkg) initializeSubmodules(pkg)
@internal
[docs]def initializeSubmodules(currentModule): for name, mod in inspect.getmembers(currentModule): if not inspect.ismodule(mod): continue if not mod.__name__.startswith(package.__name__): continue initializeModule(mod)
@internal
[docs]def initializeModule(mod): if mod in initialized: log_import.debug("{} already innited".format(mod.__name__)) return if mod in initializing: log_import.debug("Loop detected in initialization chain ... ({})".format(mod.__name__)) return initializing.add(mod) name = mod.__name__ if not (hasattr(mod, "preInitialize") \ or hasattr(mod, "postInitialize") \ or hasattr(mod, "initialize")): if not name.startswith(package.__name__): return try: if hasattr(mod, "preInitialize"): log_import.debug("preInitialize module {}".format(name)) getattr(mod, "preInitialize")() log_import.info("Initialize module {}".format(name)) if hasattr(mod, "initialize"): getattr(mod, "initialize")() initializeSubmodules(mod) if hasattr(mod, "postInitialize"): log_import.debug("postInitialize module {}".format(name)) getattr(mod, "postInitialize")() except Exception as e: log_import.error("Could not initialize module '{}': {}".format(name, e)) initializing.remove(mod) initialized.add(mod)