def _to_string_key_value(key: object, value: object, print_options: _PrintOptions) -> LogBuffer: ''' Returns a LogBuffer containing a string representation of the the key and value. Args: key (object): The key related to the value value (object): The value print_options (_PrintOptions): Output options Returns: LogBuffer: a LogBuffer ''' buff = LogBuffer(_maximum_data_output_width) key_buff = _to_string(None, key, print_options) value_buff = _to_string(None, value, print_options) buff.append_buffer(None, key_buff).append_buffer(_key_value_separator, value_buff) return buff
def __del__(self): ''' Called when the instance is about to be destroyed. ''' global _last_print_buff if not _is_enabled: return time = datetime.datetime.now() - self.enter_time if _last_print_buff.is_multi_lines: _logger.print(_get_indent_string()) # Empty Line _down_nest() _last_print_buff = LogBuffer(_maximum_data_output_width) _last_print_buff.no_break_append( _leave_format.format(self.name, self.filename, self.lineno, time)) _last_print_buff.line_feed() _logger.print(_get_indent_string() + _last_print_buff.lines[0][1])
def __init__(self, invoker: object) -> None: ''' Initializes this object. Args: invoker (object): The object or class that invoked this method. ''' global _nest_level global _previous_nest_level global _last_print_buff if not _is_enabled: return if invoker is None: self.name = '' else: self.name = type(invoker).__name__ if self.name == 'type': self.name = invoker.__name__ self.name += '.' frame_summary = _get_frame_summary(4) self.name += frame_summary.name self.filename = os.path.basename(frame_summary.filename) self.lineno = frame_summary.lineno indent_string = _get_indent_string() if _nest_level < _previous_nest_level or _last_print_buff.is_multi_lines: _logger.print(indent_string) # Empty Line _last_print_buff = LogBuffer(_maximum_data_output_width) _last_print_buff.no_break_append( _enter_format.format(self.name, self.filename, self.lineno)) _last_print_buff.line_feed() _logger.print(indent_string + _last_print_buff.lines[0][1]) _up_nest() self.enter_time = datetime.datetime.now()
def print(name: str, value: object = _DO_NOT_OUTPUT, *, force_reflection: bool = False, output_private: bool = False, output_method: bool = False, collection_limit: int = None, string_limit: int = None, bytes_limit: int = None, reflection_nest_limit: int = None) -> None: ''' Outputs the name and value. Args: name (str): The name of the value (simply output message if the value is omitted). value (object, optional): The value to output if not omitted. force_reflection (bool, optional): If true, outputs using reflection even if it has a __str__ or __repr__ method. Defaults to None output_private (bool, optional): If true, also outputs private members when using reflection. Defaults to None output_method (bool, optional): If true, also outputs method members when using reflection. Defaults to None collection_limit (int, optional): Output limit of collection elements (overrides debugtarace.ini value). Defaults to None string_limit (int, optional): Output limit of string characters (overrides debugtarace.ini value). Defaults to None bytes_limit (int, optional): Output limit of byte array elements (overrides debugtarace.ini value). Defaults to None reflection_nest_limit (int, optional): Nest limits when using reflection (overrides debugtarace.ini value). Defaults to None The following is in Japanese. 名前と値を出力します。 引数: name (str): 出力する名前 (valueが省略されている場合は、単に出力するメッセージ) value (object, optional): 出力する値 (省略されていなければ) force_reflection (bool, optional): Trueなら __str__ または __repr__ メソッドが定義されていてもリフレクションを使用する。デフォルトは指定なし output_private (bool, optional): Trueならプライベートメンバーも出力する。デフォルトは指定なし output_method (bool, optional): Trueならメソッドも出力する。デフォルトは指定なし collection_limit (int, optional): コレクションの要素の出力数の制限 (debugtarace.iniの値より優先)。デフォルトは指定なし string_limit (int, optional): 文字列値の出力文字数の制限 (debugtarace.iniの値より優先)。デフォルトは指定なし bytes_limit (int, optional): バイト配列bytesの内容の出力数の制限 (debugtarace.iniの値より優先)。デフォルトは指定なし reflection_nest_limit (int, optional): リフレクションのネスト数の制限 (debugtarace.iniの値より優先)。デフォルトは指定なし ''' global _last_print_buff global _reflected_objects if not _is_enabled: return _reflected_objects.clear() last_is_multi_lines = _last_print_buff.is_multi_lines if value is _DO_NOT_OUTPUT: # without value _last_print_buff = LogBuffer(_maximum_data_output_width) _last_print_buff.no_break_append(name) else: # with value print_options = _PrintOptions(force_reflection, output_private, output_method, collection_limit, string_limit, bytes_limit, reflection_nest_limit) _last_print_buff = _to_string(name, value, print_options) # append print suffix frame_summary = _get_frame_summary(3) _last_print_buff.no_break_append( _print_suffix_format.format(frame_summary.name, os.path.basename(frame_summary.filename), frame_summary.lineno)) _last_print_buff.line_feed() if last_is_multi_lines or _last_print_buff.is_multi_lines: _logger.print(_get_indent_string()) # Empty Line lines = _last_print_buff.lines for line in lines: _logger.print(_get_indent_string(line[0]) + line[1])
def _to_string_iterable_body(values: object, print_options: _PrintOptions) -> LogBuffer: ''' Returns a LogBuffer containing the body of a string representation of the iterable value. Args: value (object): The iterable value to append print_options (_PrintOptions): Output options Returns: LogBuffer: a LogBuffer ''' buff = LogBuffer(_maximum_data_output_width) was_multi_lines = False index = 0 for element in values: if index > 0: buff.no_break_append(', ') if index >= print_options.collection_limit: buff.append(_limit_string) break element_buff = LogBuffer(_maximum_data_output_width) if isinstance(values, dict): # dictionary element_buff = _to_string_key_value(element, values[element], print_options) else: # list, set, frozenset or tuple element_buff = _to_string(None, element, print_options) if index > 0 and (was_multi_lines or element_buff.is_multi_lines): buff.line_feed() buff.append_buffer(None, element_buff) was_multi_lines = element_buff.is_multi_lines index += 1 # 1.1.0 if isinstance(values, dict) and len(values) == 0: buff.no_break_append(':') #### return buff
def _to_string_iterable(values: object, print_options: _PrintOptions) -> LogBuffer: ''' Returns a LogBuffer containing a string representation of the iterable value. Args: value (object): The iterable value to append print_options (_PrintOptions): Output options Returns: LogBuffer: a LogBuffer ''' open_char = '{' # set, frozenset, dict close_char = '}' if isinstance(values, list): # list open_char = '[' close_char = ']' elif isinstance(values, tuple): # tuple open_char = '(' close_char = ')' buff = LogBuffer(_maximum_data_output_width) buff.append(_get_type_name(values, len(values))) buff.no_break_append(open_char) body_buff = _to_string_iterable_body(values, print_options) # 1.1.0 if open_char == '(' and len(values) == 1: # A tuple with 1 element body_buff.no_break_append(',') #### is_multi_lines = body_buff.is_multi_lines or buff.length + body_buff.length > _maximum_data_output_width if is_multi_lines: buff.line_feed() buff.up_nest() buff.append_buffer(None, body_buff) if is_multi_lines: buff.line_feed() buff.down_nest() buff.no_break_append(close_char) return buff
def _to_string_refrection_body(value: object, print_options: _PrintOptions) -> LogBuffer: ''' Returns a LogBuffer containing the body of a string representation of the value with reflection. Args: value (bytes): The value to append print_options (_PrintOptions): Output options Returns: LogBuffer: a LogBuffer ''' buff = LogBuffer(_maximum_data_output_width) members = [] try: base_members = inspect.getmembers( value, lambda v: not inspect.isclass(v) and (print_options.output_method or not inspect.ismethod(v) ) and not inspect.isbuiltin(v)) members = [ m for m in base_members if (not m[0].startswith('__') or not m[0].endswith('__')) and ( print_options.output_private or not m[0].startswith('_')) ] except BaseException as ex: buff.append(str(ex)) return buff was_multi_lines = False index = 0 for member in members: if index > 0: buff.no_break_append(', ') name = member[0] value = member[1] member_buff = LogBuffer(_maximum_data_output_width) member_buff.append(name) member_buff.append_buffer(_key_value_separator, _to_string(None, value, print_options)) if index > 0 and (was_multi_lines or member_buff.is_multi_lines): buff.line_feed() buff.append_buffer(None, member_buff) was_multi_lines = member_buff.is_multi_lines index += 1 return buff
def _to_string_refrection(value: object, print_options: _PrintOptions) -> LogBuffer: ''' Returns a LogBuffer containing a string representation of the value with reflection. Args: value (bytes): The value to append print_options (_PrintOptions): Output options Returns: LogBuffer: a LogBuffer ''' buff = LogBuffer(_maximum_data_output_width) buff.append(_get_type_name(value)) body_buff = _to_string_refrection_body(value, print_options) is_multi_lines = body_buff.is_multi_lines or buff.length + body_buff.length > _maximum_data_output_width buff.no_break_append('{') if is_multi_lines: buff.line_feed() buff.up_nest() buff.append_buffer(None, body_buff) if is_multi_lines: if buff.length > 0: buff.line_feed() buff.down_nest() buff.no_break_append('}') return buff
def _to_string_bytes(value: bytes, print_options: _PrintOptions) -> LogBuffer: ''' Returns a LogBuffer containing a string representation of the bytes value. Args: value (bytes): The bytes value print_options (_PrintOptions): Output options Returns: LogBuffer: a LogBuffer ''' bytes_length = len(value) buff = LogBuffer(_maximum_data_output_width) buff.no_break_append('(') if type(value) == bytes: buff.no_break_append('bytes') elif type(value) == bytearray: buff.no_break_append('bytearray') if bytes_length >= _minimum_output_length: buff.no_break_append(' ') buff.no_break_append(_length_format.format(bytes_length)) buff.no_break_append(')[') is_multi_lines = bytes_length >= _bytes_count_in_line if is_multi_lines: buff.line_feed() buff.up_nest() chars = '' count = 0 for element in value: if count != 0 and count % _bytes_count_in_line == 0: if is_multi_lines: buff.no_break_append('| ') buff.no_break_append(chars) buff.line_feed() chars = '' if (count >= print_options.bytes_limit): buff.no_break_append(_limit_string) break buff.no_break_append('{:02X} '.format(element)) chars += chr(element) if element >= 0x20 and element <= 0x7E else '.' count += 1 if is_multi_lines: # padding full_length = 3 * _bytes_count_in_line current_length = buff.length if current_length == 0: current_length = full_length buff.no_break_append(' ' * (full_length - current_length)) buff.no_break_append('| ') buff.no_break_append(chars) if is_multi_lines: buff.line_feed() buff.down_nest() buff.no_break_append(']') return buff
def _to_string_str(value: str, print_options: _PrintOptions) -> LogBuffer: ''' Returns a LogBuffer containing a string representation of the string value. Args: value (str): The string value print_options (_PrintOptions): Output options Returns: LogBuffer: a LogBuffer ''' has_single_quote = False has_double_quote = False single_quote_buff = LogBuffer(_maximum_data_output_width) double_quote_buff = LogBuffer(_maximum_data_output_width) if len(value) >= _minimum_output_length: single_quote_buff.no_break_append('(') single_quote_buff.no_break_append(_length_format.format(len(value))) single_quote_buff.no_break_append(')') double_quote_buff.no_break_append('(') double_quote_buff.no_break_append(_length_format.format(len(value))) double_quote_buff.no_break_append(')') single_quote_buff.no_break_append("'") double_quote_buff.no_break_append('"') count = 1 for char in value: if count > print_options.string_limit: single_quote_buff.no_break_append(_limit_string) double_quote_buff.no_break_append(_limit_string) break if char == "'": single_quote_buff.no_break_append("\\'") double_quote_buff.no_break_append(char) has_single_quote = True elif char == '"': single_quote_buff.no_break_append(char) double_quote_buff.no_break_append('\\"') has_double_quote = True elif char == '\\': single_quote_buff.no_break_append('\\\\') double_quote_buff.no_break_append('\\\\') elif char == '\n': single_quote_buff.no_break_append('\\n') double_quote_buff.no_break_append('\\n') elif char == '\r': single_quote_buff.no_break_append('\\r') double_quote_buff.no_break_append('\\r') elif char == '\t': single_quote_buff.no_break_append('\\t') double_quote_buff.no_break_append('\\t') elif char < ' ': num_str = format(ord(char), '02X') single_quote_buff.no_break_append('\\x' + num_str) double_quote_buff.no_break_append('\\x' + num_str) else: single_quote_buff.no_break_append(char) double_quote_buff.no_break_append(char) count += 1 double_quote_buff.no_break_append('"') single_quote_buff.no_break_append("'") if has_single_quote and not has_double_quote: return double_quote_buff return single_quote_buff
def _to_string(name: str, value: object, print_options: _PrintOptions) -> LogBuffer: ''' Returns a LogBuffer containing a string representation of the the name and value. Args: name (str): The name related to the value value (object): The value print_options (_PrintOptions): Output options Returns: LogBuffer: a LogBuffer ''' buff = LogBuffer(_maximum_data_output_width) separator = None if name is not None: buff.append(name) separator = _varname_value_separator if value is None: # None buff.no_break_append(separator).append('None') elif isinstance(value, str): # str value_buff = _to_string_str(value, print_options) buff.append_buffer(separator, value_buff) elif isinstance(value, bytes) or isinstance(value, bytearray): # bytes value_buff = _to_string_bytes(value, print_options) buff.append_buffer(separator, value_buff) elif isinstance(value, int) or isinstance(value, float) or \ isinstance(value, datetime.date) or isinstance(value, datetime.time) or \ isinstance(value, datetime.datetime): # int, float, datetime.date, datetime.time, datetime.datetime buff.no_break_append(separator).append(str(value)) elif isinstance(value, list) or \ isinstance(value, set) or isinstance(value, frozenset) or \ isinstance(value, tuple) or \ isinstance(value, dict): # list, set, frozenset, tuple, dict value_buff = _to_string_iterable(value, print_options) buff.append_buffer(separator, value_buff) else: has_str, has_repr = _has_str_repr_method(value) value_buff = LogBuffer(_maximum_data_output_width) if not print_options.force_reflection and (has_str or has_repr): # has __str__ or __repr__ method if has_repr: value_buff.append('repr(): ') value_buff.no_break_append(repr(value)) else: value_buff.append('str(): ') value_buff.no_break_append(str(value)) buff.append_buffer(separator, value_buff) else: # use refrection if any(map(lambda obj: value is obj, _reflected_objects)): # cyclic reference value_buff.no_break_append(_cyclic_reference_string) elif len(_reflected_objects) > print_options.reflection_nest_limit: # over reflection level limitation value_buff.no_break_append(_limit_string) else: _reflected_objects.append(value) value_buff = _to_string_refrection(value, print_options) _reflected_objects.pop() buff.append_buffer(separator, value_buff) return buff
def init(config_path: str = './debugtrace.ini'): ''' Initialize debugtrace. Args: config_path (str): The path of the configuration file. ''' global _config_path global _config global _logger_name global _logging_config_file global _logging_logger_name global _logging_level global _is_enabled global _enter_format global _leave_format global _maximum_indents global _indent_string global _data_indent_string global _limit_string global _non_output_string global _cyclic_reference_string global _varname_value_separator global _key_value_separator global _print_suffix_format global _count_format global _minimum_output_count global _length_format global _minimum_output_length global _log_datetime_format global _maximum_data_output_width global _bytes_count_in_line global _collection_limit global _bytes_limit global _string_limit global _reflection_nest_limit global _last_print_buff global _logger # Read a configuration file (debugtrace.ini) _config_path = config_path _config = configparser.ConfigParser() if os.path.exists(_config_path): _config.read(_config_path) else: _config_path = '<No config file>' _logger_name = _get_config_value('logger', 'stderr').lower() _logging_config_file = _get_config_value('logging_config_file', 'logging.conf') _logging_logger_name = _get_config_value('logging_logger_name', 'debugtrace') _logging_level = _get_config_value('logging_level', 'DEBUG').upper() _is_enabled = _get_config_value('is_enabled', True) _enter_format = _get_config_value('enter_format', 'Enter {0} ({1}:{2})') _leave_format = _get_config_value('leave_format', 'Leave {0} ({1}:{2}) duration: {3}') _maximum_indents = _get_config_value('maximum_indents', 32) _indent_string = _get_config_value('indent_string', '| ') _data_indent_string = _get_config_value('data_indent_string', ' ') _limit_string = _get_config_value('limit_string', '...') _non_output_string = _get_config_value('non_output_string', '...') _cyclic_reference_string = _get_config_value('cyclic_reference_string', '*** Cyclic Reference ***') _varname_value_separator = _get_config_value('varname_value_separator', ' = ') _key_value_separator = _get_config_value('key_value_separator', ': ') _print_suffix_format = _get_config_value('print_suffix_format', ' ({1}:{2})') _count_format = _get_config_value('count_format', 'count:{}') _minimum_output_count = _get_config_value('minimum_output_count', 5) _length_format = _get_config_value('length_format', 'length:{}') _minimum_output_length = _get_config_value('minimum_output_length', 5) _log_datetime_format = _get_config_value('log_datetime_format', '%Y-%m-%d %H:%M:%S.%f') _maximum_data_output_width = _get_config_value('maximum_data_output_width', 70) _bytes_count_in_line = _get_config_value('bytes_count_in_line', 16) _collection_limit = _get_config_value('collection_limit', 512) _bytes_limit = _get_config_value('bytes_limit', 8192) _string_limit = _get_config_value('string_limit', 8192) _reflection_nest_limit = _get_config_value('reflection_nest_limit', 4) _last_print_buff = LogBuffer(_maximum_data_output_width) # Decides the logger class _logger = StdErr() if _logger_name == 'stdout': _logger = StdOut() elif _logger_name == 'stderr': _logger = StdErr() elif _logger_name == 'logger': _logger = Logger() else: pr._print( 'debugtrace: (' + _config_path + ') logger = ' + _logger_name + ' (Unknown)', sys.stderr) if _is_enabled: _logger.print('DebugTrace-py ' + version.VERSION) _logger.print(' config file path: ' + _config_path) _logger.print(' logger: ' + str(_logger)) _logger.print('')