Skip to content

Commit

Permalink
tmp commit
Browse files Browse the repository at this point in the history
Signed-off-by: jparisu <[email protected]>
  • Loading branch information
jparisu committed Jun 26, 2023
1 parent 4631f5a commit fc1a5dc
Show file tree
Hide file tree
Showing 15 changed files with 801 additions and 39 deletions.
1 change: 1 addition & 0 deletions cmake_utils/cmake/compilation/compile_library.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ function(compile_library _SOURCE_PATH _INCLUDE_PATH)
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include/${MODULE_NAME}>
$<BUILD_INTERFACE:${_SOURCE_PATH}>
$<INSTALL_INTERFACE:include>
${MODULE_INCLUDE_LIBRARIES}
)

target_link_libraries(${MODULE_NAME}
Expand Down
2 changes: 1 addition & 1 deletion py_utils/py_utils/debugging/debug_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,4 @@ def wrapper(*args, **kwargs):
def debug_separator(
debug_level: int = logging.DEBUG):
print()
logger.log(debug_level, '####################################################################\n')
logger.log(debug_level, '##################################################################\n')
102 changes: 102 additions & 0 deletions py_utils/py_utils/debugging/inspection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima).
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
This file contains utils to get information regarding the stack of the execution.
It allows to get the module, class or function name where the execution currently is.
"""

import inspect


def get_calling_frame(depth: int = 1):
# Get the calling frame
calling_frame = inspect.currentframe()

# Move up the stack frames based on depth
for _ in range(depth):
calling_frame = calling_frame.f_back

return calling_frame


def get_function_name(depth: int = 1, extend: bool = False) -> str:

if not extend:
# Get the calling frame
calling_frame = get_calling_frame(depth=depth + 1)

# Retrieve the function name
return inspect.getframeinfo(calling_frame).function

else:
return get_context(depth + 1)


def is_inside_class(depth: int = 1) -> bool:
# Get the calling frame
calling_frame = get_calling_frame(depth=depth + 1)

# Check if the frame is there is a self or a cls variable
return "self" in calling_frame.f_locals or "cls" in calling_frame.f_locals


def get_class_name(depth: int = 1, extend: bool = False) -> str:
# Get the calling frame
calling_frame = get_calling_frame(depth=depth + 1)

result = None

# Retrieve the class name if there is a self variable
self_var = calling_frame.f_locals.get("self")
cls_var = calling_frame.f_locals.get("cls")
if self_var is not None:
result = self_var.__class__.__name__

# Retrieve the class name if there is a cls variable
elif cls_var is not None:
result = cls_var.__name__

if result is None or not extend:
return result

else:
return get_module_name(depth=depth + 1) + result


def get_module_name(depth: int = 1) -> str:
# Get the calling frame
calling_frame = get_calling_frame(depth=depth + 1)

# Retrieve the module name
module_name = inspect.getmodule(calling_frame).__name__
return module_name


def get_context(depth: int = 1) -> str:
# Retrieve the module name
ctx_name = get_module_name(depth + 1)

# If it is in a class, return its class name
class_name = get_class_name(depth + 1)
if class_name is not None:
ctx_name += '.' + class_name

ctx_name += '.' + get_function_name(depth + 1)

return ctx_name


def get_variable_module(v) -> str:
return inspect.getmodule(v).__name__
143 changes: 143 additions & 0 deletions py_utils/py_utils/debugging/introspection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima).
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
This file contains utils to debug python code.
"""

import sys

from py_utils.decorator.oop import pure_virtual
from py_utils.logging.log_utils import logger, logging


class IntrospectionInformation():

@pure_virtual
def __init__(self, obj, recursive: int, privates: bool):
pass

@pure_virtual
def type(self):
pass

@pure_virtual
def __str__(self):
pass

@pure_virtual
def pretty_print(self, indent: int):
pass


class ModuleIntrospectionInformation(IntrospectionInformation):

def __init__(self, module):

if not isinstance(module, type(sys)):
raise TypeError(f'Object {module} not of Module type.')

self.name_ = module.__name__
self.importables_ = []
for name in dir(module):
self.importables_.append((name, type(getattr(module, name))))

def __str__(self):



def get_module_introspection(obj) -> dict:
result = {}

if not isinstance(obj, type(sys)):
return result

result['name'] = obj.__name__
result['importables'] = []
for name in dir(obj):
importables.append((name, type(getattr(obj, name))))

logger.log(debug_level, f'{{MODULE: <{module_name}>; Importables: <{importables}>}}')


def debug_module_introspection(obj, debug_level: int = logging.DEBUG):
if not isinstance(obj, type(sys)):
return

module_name = obj.__name__
importables = []
for name in dir(obj):
importables.append((name, type(getattr(obj, name))))

logger.log(debug_level, f'{{MODULE: <{module_name}>; Importables: <{importables}>}}')


def debug_class_introspection(obj, debug_level: int = logging.DEBUG):
"""Log in debug introspection information regarding an object."""
if not isinstance(obj, type):
return

# If module
class_name = obj.__name__
methods = [method_name for method_name in dir(obj) if callable(getattr(obj, method_name))]
attributes = [method_name for method_name in dir(obj) if not callable(getattr(obj, method_name))]
parent_classes = obj.__bases__
logger.log(debug_level, f'{{CLASS: <{class_name}>; Methods: <{methods}>; Attributes: <{attributes}>}}; Bases: <{parent_classes}>')


def debug_object_introspection(obj, debug_level: int = logging.DEBUG):
"""Log in debug introspection information regarding an object."""
# If module
class_name = type(obj).__name__
methods = [method_name for method_name in dir(obj) if callable(getattr(obj, method_name))]
attributes = [method_name for method_name in dir(obj) if not callable(getattr(obj, method_name))]
to_str = obj.__str__()
logger.log(debug_level, f'{{OBJECT: <{to_str}>; Class: <{class_name}>; Methods: <{methods}>; Attributes: <{attributes}>}}')


def debug_variable_introspection(obj, debug_level: int = logging.DEBUG):
"""Log in debug introspection information regarding an object."""
# If module
if isinstance(obj, type(sys)):
debug_module_introspection(obj, debug_level)
elif isinstance(obj, type):
debug_class_introspection(obj, debug_level)
else:
debug_object_introspection(obj, debug_level)


def debug_function_decorator(
debug_level: int = logging.DEBUG):
"""Decorator to debug information regarding start, arguments and finish of a function."""
def decorator(func):
def wrapper(*args, **kwargs):
logger.log(debug_level, f'Function <{func.__name__}> called with arguments: <{args}>, <{kwargs}>')
result = func(*args, **kwargs)
logger.log(debug_level, f'Function <{func.__name__}> finished with return: <{result}>')
return result
return wrapper

if callable(debug_level):
func = debug_level
debug_level = logging.DEBUG
return decorator(func)

else:
return decorator


def debug_separator(
debug_level: int = logging.DEBUG):
print()
logger.log(debug_level, '####################################################################\n')
53 changes: 53 additions & 0 deletions py_utils/py_utils/decorators/CustomFunctionDecorator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima).
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from functools import wraps


class CustomFunctionDecorator():

def __init__(self, *args, **kwargs):
self.func_ = None
self.ctor_args_ = args
self.ctor_kwargs_ = kwargs

# If the decorator has no arguments, use the func argument passed
if (len(args) == 1 and callable(args[0]) and len(kwargs) == 0):
self.func_ = args[0]
self.ctor_args_ = () # No arguments passed, so remove

# If the decorator has arguments, check if any kwargs sets any of the internal attributes
else:
for attr in dir(self):
if attr in kwargs.keys():
setattr(self, attr, kwargs[attr])

def __call__(self, *args, **kwargs):
# If decorator created without arguments, function already available
# Also it means this is calling the actual function already
if self.func_ is not None:
return self.wrapper(self.func_, *args, **kwargs)

# If created with args, this is only calling to set the function
else:
self.func_ = args[0]
return self.__call__

def wrapper(self, func, *args, **kwargs):
# Call the function with the provided arguments
return func(*args, **kwargs)

def ctor_had_arguments(self):
# Call the function with the provided arguments
return len(self.ctor_args_) > 0 or len(self.ctor_kwargs_) > 0
Empty file.
77 changes: 77 additions & 0 deletions py_utils/py_utils/decorators/debugging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima).
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
This file contains decorators for debug.
"""

from functools import wraps

from py_utils.logging.log_utils import logger, logging
from py_utils.decorators.CustomFunctionDecorator import CustomFunctionDecorator
from py_utils.debugging.inspection import get_variable_module


def debug_function_decorator(
debug_level: int = logging.DEBUG):
"""Decorator to debug information regarding start, arguments and finish of a function."""
@wraps(debug_level)
def decorator(func):

@wraps(func)
def wrapper(*args, **kwargs):
logger.log(
debug_level,
f'Function <{func.__name__}> called with arguments: <{args}>, <{kwargs}>')
result = func(*args, **kwargs)
logger.log(
debug_level,
f'Function <{func.__name__}> finished with return: <{result}>')
return result
return wrapper

if callable(debug_level):
func = debug_level
debug_level = logging.DEBUG
return decorator(func)

else:
return decorator


class DebugFunctionDecorator(CustomFunctionDecorator):

def __init__(self, *args, **kwargs):

# Default values (may be changed by kwargs)
self.debug_level = logging.DEBUG
self.add_module = True

super().__init__(*args, **kwargs)

def wrapper(self, func, *args, **kwargs):

if self.add_module:
fun_str = get_variable_module(func) + '.' + func.__name__
else:
fun_str = func.__name__

logger.log(
self.debug_level,
f'Function <{fun_str}> called with arguments: <{args}>, <{kwargs}>')
result = func(*args, **kwargs)
logger.log(
self.debug_level,
f'Function <{fun_str}> finished with return: <{result}>')
return result
Loading

0 comments on commit fc1a5dc

Please sign in to comment.