"""
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
[docs]def print_args(function):
"""
Prints all the arguments of the decorated function.
"""
def wrapper(*args, **kwargs):
print ('Arguments:', args, kwargs)
return function(*args, **kwargs)
return wrapper
#####################################
@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)