AVT相机arm版本SDK
This commit is contained in:
72
Vimba_6_0/VimbaPython/Source/vimba/util/__init__.py
Normal file
72
Vimba_6_0/VimbaPython/Source/vimba/util/__init__.py
Normal 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
|
||||
96
Vimba_6_0/VimbaPython/Source/vimba/util/context_decorator.py
Normal file
96
Vimba_6_0/VimbaPython/Source/vimba/util/context_decorator.py
Normal 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
|
||||
295
Vimba_6_0/VimbaPython/Source/vimba/util/log.py
Normal file
295
Vimba_6_0/VimbaPython/Source/vimba/util/log.py
Normal 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)
|
||||
223
Vimba_6_0/VimbaPython/Source/vimba/util/runtime_type_check.py
Normal file
223
Vimba_6_0/VimbaPython/Source/vimba/util/runtime_type_check.py
Normal 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
|
||||
80
Vimba_6_0/VimbaPython/Source/vimba/util/scoped_log.py
Normal file
80
Vimba_6_0/VimbaPython/Source/vimba/util/scoped_log.py
Normal 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
|
||||
136
Vimba_6_0/VimbaPython/Source/vimba/util/tracer.py
Normal file
136
Vimba_6_0/VimbaPython/Source/vimba/util/tracer.py
Normal 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
|
||||
Reference in New Issue
Block a user