AVT相机arm版本SDK

This commit is contained in:
zhangpeng
2025-04-30 09:26:04 +08:00
parent 837c870f18
commit 78a1c63a95
705 changed files with 148770 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
# Suppress 'imported but unused' - Error from static style checker.
# flake8: noqa: F401
__all__ = [
'LogLevel',
'LogConfig',
'Log',
'LOG_CONFIG_TRACE_CONSOLE_ONLY',
'LOG_CONFIG_TRACE_FILE_ONLY',
'LOG_CONFIG_TRACE',
'LOG_CONFIG_INFO_CONSOLE_ONLY',
'LOG_CONFIG_INFO_FILE_ONLY',
'LOG_CONFIG_INFO',
'LOG_CONFIG_WARNING_CONSOLE_ONLY',
'LOG_CONFIG_WARNING_FILE_ONLY',
'LOG_CONFIG_WARNING',
'LOG_CONFIG_ERROR_CONSOLE_ONLY',
'LOG_CONFIG_ERROR_FILE_ONLY',
'LOG_CONFIG_ERROR',
'LOG_CONFIG_CRITICAL_CONSOLE_ONLY',
'LOG_CONFIG_CRITICAL_FILE_ONLY',
'LOG_CONFIG_CRITICAL',
# Decorators
'TraceEnable',
'ScopedLogEnable',
'RuntimeTypeCheckEnable',
'EnterContextOnCall',
'LeaveContextOnCall',
'RaiseIfInsideContext',
'RaiseIfOutsideContext'
]
from .log import Log, LogLevel, LogConfig, LOG_CONFIG_TRACE_CONSOLE_ONLY, \
LOG_CONFIG_TRACE_FILE_ONLY, LOG_CONFIG_TRACE, LOG_CONFIG_INFO_CONSOLE_ONLY, \
LOG_CONFIG_INFO_FILE_ONLY, LOG_CONFIG_INFO, LOG_CONFIG_WARNING_CONSOLE_ONLY, \
LOG_CONFIG_WARNING_FILE_ONLY, LOG_CONFIG_WARNING, LOG_CONFIG_ERROR_CONSOLE_ONLY, \
LOG_CONFIG_ERROR_FILE_ONLY, LOG_CONFIG_ERROR, LOG_CONFIG_CRITICAL_CONSOLE_ONLY, \
LOG_CONFIG_CRITICAL_FILE_ONLY, LOG_CONFIG_CRITICAL
from .tracer import TraceEnable
from .scoped_log import ScopedLogEnable
from .runtime_type_check import RuntimeTypeCheckEnable
from .context_decorator import EnterContextOnCall, LeaveContextOnCall, RaiseIfInsideContext, \
RaiseIfOutsideContext

View File

@@ -0,0 +1,96 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import functools
__all__ = [
'EnterContextOnCall',
'LeaveContextOnCall',
'RaiseIfInsideContext',
'RaiseIfOutsideContext'
]
class EnterContextOnCall:
"""Decorator setting/injecting flag used for checking the context."""
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
args[0]._context_entered = True
return func(*args, **kwargs)
return wrapper
class LeaveContextOnCall:
"""Decorator clearing/injecting flag used for checking the context."""
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
args[0]._context_entered = False
return result
return wrapper
class RaiseIfInsideContext:
"""Raising RuntimeError is decorated Method is called inside with-statement.
Note This Decorator shall work only on Object implementing a Context Manger.
For this to work object must offer a boolean attribute called _context_entered
"""
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if args[0]._context_entered:
msg = 'Called \'{}()\' inside of \'with\' - statement scope.'
msg = msg.format('{}'.format(func.__qualname__))
raise RuntimeError(msg)
return func(*args, **kwargs)
return wrapper
class RaiseIfOutsideContext:
"""Raising RuntimeError is decorated Method is called outside with-statement.
Note This Decorator shall work only on Object implementing a Context Manger.
For this to work object must offer a boolean attribute called __context_entered
"""
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if not args[0]._context_entered:
msg = 'Called \'{}()\' outside of \'with\' - statement scope.'
msg = msg.format('{}'.format(func.__qualname__))
raise RuntimeError(msg)
return func(*args, **kwargs)
return wrapper

View File

@@ -0,0 +1,295 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import os
import enum
import datetime
import logging
from typing import List, Optional
__all__ = [
'LogLevel',
'LogConfig',
'Log',
'LOG_CONFIG_TRACE_CONSOLE_ONLY',
'LOG_CONFIG_TRACE_FILE_ONLY',
'LOG_CONFIG_TRACE',
'LOG_CONFIG_INFO_CONSOLE_ONLY',
'LOG_CONFIG_INFO_FILE_ONLY',
'LOG_CONFIG_INFO',
'LOG_CONFIG_WARNING_CONSOLE_ONLY',
'LOG_CONFIG_WARNING_FILE_ONLY',
'LOG_CONFIG_WARNING',
'LOG_CONFIG_ERROR_CONSOLE_ONLY',
'LOG_CONFIG_ERROR_FILE_ONLY',
'LOG_CONFIG_ERROR',
'LOG_CONFIG_CRITICAL_CONSOLE_ONLY',
'LOG_CONFIG_CRITICAL_FILE_ONLY',
'LOG_CONFIG_CRITICAL'
]
class LogLevel(enum.IntEnum):
"""Enum containing all LogLevels.
Enum values are:
Trace - Show Tracing information. Show all messages.
Info - Show Informational, Warning, Error, and Critical Events.
Warning - Show Warning, Error, and Critical Events.
Error - Show Errors and Critical Events.
Critical - Show Critical Events only.
"""
Trace = logging.DEBUG
Info = logging.INFO
Warning = logging.WARNING
Error = logging.ERROR
Critical = logging.CRITICAL
def __str__(self):
return self._name_
def as_equal_len_str(self) -> str:
return _LEVEL_TO_EQUAL_LEN_STR[self]
_LEVEL_TO_EQUAL_LEN_STR = {
LogLevel.Trace: 'Trace ',
LogLevel.Info: 'Info ',
LogLevel.Warning: 'Warning ',
LogLevel.Error: 'Error ',
LogLevel.Critical: 'Critical'
}
class LogConfig:
"""The LogConfig is a builder to configure various specialized logging configurations.
The constructed LogConfig must set via vimba.Vimba or the ScopedLogEnable Decorator
to start logging.
"""
__ENTRY_FORMAT = logging.Formatter('%(asctime)s | %(message)s')
def __init__(self):
self.__handlers: List[logging.Handler] = []
self.__max_msg_length: Optional[int] = None
def add_file_log(self, level: LogLevel) -> 'LogConfig':
"""Add a new Log file to the Config Builder.
Arguments:
level: LogLevel of the added log file.
Returns:
Reference to the LogConfig instance (builder pattern).
"""
log_ts = datetime.datetime.today().strftime('%Y-%m-%d_%H-%M-%S')
log_file = 'VimbaPython_{}_{}.log'.format(log_ts, str(level))
log_file = os.path.join(os.getcwd(), log_file)
handler = logging.FileHandler(log_file, delay=True)
handler.setLevel(level)
handler.setFormatter(LogConfig.__ENTRY_FORMAT)
self.__handlers.append(handler)
return self
def add_console_log(self, level: LogLevel) -> 'LogConfig':
"""Add a new Console Log to the Config Builder.
Arguments:
level: LogLevel of the added console log file.
Returns:
Reference to the LogConfig instance (builder pattern).
"""
handler = logging.StreamHandler()
handler.setLevel(level)
handler.setFormatter(LogConfig.__ENTRY_FORMAT)
self.__handlers.append(handler)
return self
def set_max_msg_length(self, max_msg_length: int):
"""Set max length of a log entry. Messages longer than this entry will be cut off."""
self.__max_msg_length = max_msg_length
def get_max_msg_length(self) -> Optional[int]:
"""Get configured max message length"""
return self.__max_msg_length
def get_handlers(self) -> List[logging.Handler]:
"""Get all configured log handlers"""
return self.__handlers
class Log:
class __Impl:
"""This class is wraps the logging Facility. Since this is as Singleton
Use Log.get_instace(), to access the log.
"""
def __init__(self):
"""Do not call directly. Use Log.get_instance() instead."""
self.__logger: Optional[logging.Logger] = None
self.__config: Optional[LogConfig] = None
self._test_buffer: Optional[List[str]] = None
def __bool__(self):
return bool(self.__logger)
def enable(self, config: LogConfig):
"""Enable global VimbaPython logging mechanism.
Arguments:
config: The configuration to apply.
"""
self.disable()
logger = logging.getLogger('VimbaPythonLog')
logger.setLevel(logging.DEBUG)
for handler in config.get_handlers():
logger.addHandler(handler)
self.__config = config
self.__logger = logger
def disable(self):
"""Disable global VimbaPython logging mechanism."""
if self.__logger and self.__config:
for handler in self.__config.get_handlers():
handler.close()
self.__logger.removeHandler(handler)
self.__logger = None
self.__config = None
def get_config(self) -> Optional[LogConfig]:
""" Get log configuration
Returns:
Configuration if the log is enabled. In case the log is disabled return None.
"""
return self.__config
def trace(self, msg: str):
"""Add an entry of LogLevel.Trace to the log. Does nothing is the log is disabled.
Arguments:
msg - The message that should be added to the Log.
"""
if self.__logger:
self.__logger.debug(self.__build_msg(LogLevel.Trace, msg))
def info(self, msg: str):
"""Add an entry of LogLevel.Info to the log. Does nothing is the log is disabled.
Arguments:
msg - The message that should be added to the Log.
"""
if self.__logger:
self.__logger.info(self.__build_msg(LogLevel.Info, msg))
def warning(self, msg: str):
"""Add an entry of LogLevel.Warning to the log. Does nothing is the log is disabled.
Arguments:
msg - The message that should be added to the Log.
"""
if self.__logger:
self.__logger.warning(self.__build_msg(LogLevel.Warning, msg))
def error(self, msg: str):
"""Add an entry of LogLevel.Error to the log. Does nothing is the log is disabled.
Arguments:
msg - The message that should be added to the Log.
"""
if self.__logger:
self.__logger.error(self.__build_msg(LogLevel.Error, msg))
def critical(self, msg: str):
"""Add an entry of LogLevel.Critical to the log. Does nothing is the log is disabled.
Arguments:
msg - The message that should be added to the Log.
"""
if self.__logger:
self.__logger.critical(self.__build_msg(LogLevel.Critical, msg))
def __build_msg(self, loglevel: LogLevel, msg: str) -> str:
msg = '{} | {}'.format(loglevel.as_equal_len_str(), msg)
max_len = self.__config.get_max_msg_length() if self.__config else None
if max_len and (max_len < len(msg)):
suffix = ' ...'
msg = msg[:max_len - len(suffix)] + suffix
if self._test_buffer is not None:
self._test_buffer.append(msg)
return msg
__instance = __Impl()
@staticmethod
def get_instance() -> '__Impl':
"""Get Log instance."""
return Log.__instance
def _build_cfg(console_level: Optional[LogLevel], file_level: Optional[LogLevel]) -> LogConfig:
cfg = LogConfig()
cfg.set_max_msg_length(200)
if console_level:
cfg.add_console_log(console_level)
if file_level:
cfg.add_file_log(file_level)
return cfg
# Exported Default Log configurations.
LOG_CONFIG_TRACE_CONSOLE_ONLY = _build_cfg(LogLevel.Trace, None)
LOG_CONFIG_TRACE_FILE_ONLY = _build_cfg(None, LogLevel.Trace)
LOG_CONFIG_TRACE = _build_cfg(LogLevel.Trace, LogLevel.Trace)
LOG_CONFIG_INFO_CONSOLE_ONLY = _build_cfg(LogLevel.Info, None)
LOG_CONFIG_INFO_FILE_ONLY = _build_cfg(None, LogLevel.Info)
LOG_CONFIG_INFO = _build_cfg(LogLevel.Info, LogLevel.Info)
LOG_CONFIG_WARNING_CONSOLE_ONLY = _build_cfg(LogLevel.Warning, None)
LOG_CONFIG_WARNING_FILE_ONLY = _build_cfg(None, LogLevel.Warning)
LOG_CONFIG_WARNING = _build_cfg(LogLevel.Warning, LogLevel.Warning)
LOG_CONFIG_ERROR_CONSOLE_ONLY = _build_cfg(LogLevel.Error, None)
LOG_CONFIG_ERROR_FILE_ONLY = _build_cfg(None, LogLevel.Error)
LOG_CONFIG_ERROR = _build_cfg(LogLevel.Error, LogLevel.Error)
LOG_CONFIG_CRITICAL_CONSOLE_ONLY = _build_cfg(LogLevel.Critical, None)
LOG_CONFIG_CRITICAL_FILE_ONLY = _build_cfg(None, LogLevel.Critical)
LOG_CONFIG_CRITICAL = _build_cfg(LogLevel.Critical, LogLevel.Critical)

View File

@@ -0,0 +1,223 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import collections.abc
from inspect import isfunction, ismethod, signature
from functools import wraps
from typing import get_type_hints, Union
from .log import Log
__all__ = [
'RuntimeTypeCheckEnable'
]
class RuntimeTypeCheckEnable:
"""Decorator adding runtime type checking to the wrapped callable.
Each time the callable is executed, all arguments are checked if they match with the given
type hints. If all checks are passed, the wrapped function is executed, if the given
arguments to not match a TypeError is raised.
Note: This decorator is no replacement for a feature complete TypeChecker. It supports only
a subset of all types expressible by type hints.
"""
_log = Log.get_instance()
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
full_args, hints = self.__dismantle_sig(func, *args, **kwargs)
for arg_name in hints:
self.__verify_arg(func, hints[arg_name], (arg_name, full_args[arg_name]))
return func(*args, **kwargs)
return wrapper
def __dismantle_sig(self, func, *args, **kwargs):
# Get merge args, kwargs and defaults to complete argument list.
full_args = signature(func).bind(*args, **kwargs)
full_args.apply_defaults()
# Get available type hints, remove return value.
hints = get_type_hints(func)
hints.pop('return', None)
return (full_args.arguments, hints)
def __verify_arg(self, func, type_hint, arg_spec):
arg_name, arg = arg_spec
if (self.__matches(type_hint, arg)):
return
msg = '\'{}\' called with unexpected argument type. Argument\'{}\'. Expected type: {}.'
msg = msg.format(func.__qualname__, arg_name, type_hint)
RuntimeTypeCheckEnable._log.error(msg)
raise TypeError(msg)
def __matches(self, type_hint, arg) -> bool:
if self.__matches_base_types(type_hint, arg):
return True
elif self.__matches_type_types(type_hint, arg):
return True
elif self.__matches_union_types(type_hint, arg):
return True
elif self.__matches_tuple_types(type_hint, arg):
return True
elif self.__matches_dict_types(type_hint, arg):
return True
else:
return self.__matches_callable(type_hint, arg)
def __matches_base_types(self, type_hint, arg) -> bool:
return type_hint == type(arg)
def __matches_type_types(self, type_hint, arg) -> bool:
try:
if not type_hint.__origin__ == type:
return False
hint_args = type_hint.__args__
except AttributeError:
return False
return arg in hint_args
def __matches_union_types(self, type_hint, arg) -> bool:
try:
if not type_hint.__origin__ == Union:
return False
except AttributeError:
return False
# If Matches if true for an Union hint:
for hint in type_hint.__args__:
if self.__matches(hint, arg):
return True
return False
def __matches_tuple_types(self, type_hint, arg) -> bool:
try:
if not (type_hint.__origin__ == tuple and type(arg) == tuple):
return False
except AttributeError:
return False
if arg == ():
return True
if Ellipsis in type_hint.__args__:
fn = self.__matches_var_length_tuple
else:
fn = self.__matches_fixed_size_tuple
return fn(type_hint, arg)
def __matches_fixed_size_tuple(self, type_hint, arg) -> bool:
# To pass, the entire tuple must match in length and all types
expand_hint = type_hint.__args__
if len(expand_hint) != len(arg):
return False
for hint, value in zip(expand_hint, arg):
if not self.__matches(hint, value):
return False
return True
def __matches_var_length_tuple(self, type_hint, arg) -> bool:
# To pass a tuple can be empty or all contents must match the given type.
hint, _ = type_hint.__args__
for value in arg:
if not self.__matches(hint, value):
return False
return True
def __matches_dict_types(self, type_hint, arg) -> bool:
# To pass the hint must be a Dictionary and arg must match the given types.
try:
if not (type_hint.__origin__ == dict and type(arg) == dict):
return False
except AttributeError:
return False
key_type, val_type = type_hint.__args__
for k, v in arg.items():
if type(k) != key_type or type(v) != val_type:
return False
return True
def __matches_callable(self, type_hint, arg) -> bool:
# Return if the given hint is no callable
try:
if not type_hint.__origin__ == collections.abc.Callable:
return False
except AttributeError:
return False
# Verify that are is some form of callable.:
# 1) Check if it is either a function or a method
# 2) If it is an object, check if it has a __call__ method. If so use call for checks.
if not (isfunction(arg) or ismethod(arg)):
try:
arg = getattr(arg, '__call__')
except AttributeError:
return False
# Examine signature of given callable
sig_args = signature(arg).parameters
hint_args = type_hint.__args__
# Verify Parameter list length
if len(sig_args) != len(hint_args[:-1]):
return False
return True

View File

@@ -0,0 +1,80 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
from functools import wraps
from typing import Any, Callable, Tuple, Optional
from .log import LogConfig, Log
__all__ = [
'ScopedLogEnable'
]
class _ScopedLog:
__log = Log.get_instance()
def __init__(self, config: LogConfig):
self.__config: LogConfig = config
self.__old_config: Optional[LogConfig] = None
def __enter__(self):
self.__old_config = _ScopedLog.__log.get_config()
_ScopedLog.__log.enable(self.__config)
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
if self.__old_config:
_ScopedLog.__log.enable(self.__old_config)
else:
_ScopedLog.__log.disable()
class ScopedLogEnable:
"""Decorator: Enables logging facility before execution of the wrapped function
and disables logging after exiting the wrapped function. This allows more specific
logging of a code section compared to enabling or disabling the global logging mechanism.
Arguments:
config: The configuration the log should be enabled with.
"""
def __init__(self, config: LogConfig):
"""Add scoped logging to a Callable.
Arguments:
config: The configuration the log should be enabled with.
"""
self.__config = config
def __call__(self, func: Callable[..., Any]):
@wraps(func)
def wrapper(*args: Tuple[Any, ...]):
with _ScopedLog(self.__config):
return func(*args)
return wrapper

View File

@@ -0,0 +1,136 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
from functools import reduce, wraps
from inspect import signature
from .log import Log
__all__ = [
'TraceEnable'
]
_FMT_MSG_ENTRY: str = 'Enter | {}'
_FMT_MSG_LEAVE: str = 'Leave | {}'
_FMT_MSG_RAISE: str = 'Raise | {}, {}'
_FMT_ERROR: str = 'ErrorType: {}, ErrorValue: {}'
_INDENT_PER_LEVEL: str = ' '
def _args_to_str(func, *args, **kwargs) -> str:
# Expand function signature
sig = signature(func).bind(*args, **kwargs)
sig.apply_defaults()
full_args = sig.arguments
# Early return if there is nothing to print
if not full_args:
return '(None)'
def fold(args_as_str: str, arg):
name, value = arg
if name == 'self':
arg_str = 'self'
else:
arg_str = str(value)
return '{}{}, '.format(args_as_str, arg_str)
return '({})'.format(reduce(fold, full_args.items(), '')[:-2])
def _get_indent(level: int) -> str:
return _INDENT_PER_LEVEL * level
def _create_enter_msg(name: str, level: int, args_str: str) -> str:
msg = '{}{}{}'.format(_get_indent(level), name, args_str)
return _FMT_MSG_ENTRY.format(msg)
def _create_leave_msg(name: str, level: int, ) -> str:
msg = '{}{}'.format(_get_indent(level), name)
return _FMT_MSG_LEAVE.format(msg)
def _create_raise_msg(name: str, level: int, exc_type: Exception, exc_value: str) -> str:
msg = '{}{}'.format(_get_indent(level), name)
exc = _FMT_ERROR.format(exc_type, exc_value)
return _FMT_MSG_RAISE.format(msg, exc)
class _Tracer:
__log = Log.get_instance()
__level: int = 0
@staticmethod
def is_log_enabled() -> bool:
return bool(_Tracer.__log)
def __init__(self, func, *args, **kwargs):
self.__full_name: str = '{}.{}'.format(func.__module__, func.__qualname__)
self.__full_args: str = _args_to_str(func, *args, **kwargs)
def __enter__(self):
msg = _create_enter_msg(self.__full_name, _Tracer.__level, self.__full_args)
_Tracer.__log.trace(msg)
_Tracer.__level += 1
def __exit__(self, exc_type, exc_value, exc_traceback):
_Tracer.__level -= 1
if exc_type:
msg = _create_raise_msg(self.__full_name, _Tracer.__level, exc_type, exc_value)
else:
msg = _create_leave_msg(self.__full_name, _Tracer.__level)
_Tracer.__log.trace(msg)
class TraceEnable:
"""Decorator: Adds an entry of LogLevel. Trace on entry and exit of the wrapped function.
On exit, the log entry contains information if the function was left normally or with an
exception.
"""
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
if _Tracer.is_log_enabled():
with _Tracer(func, *args, **kwargs):
result = func(*args, **kwargs)
return result
else:
return func(*args, **kwargs)
return wrapper