예제 #1
0
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
예제 #2
0
    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])
예제 #3
0
    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()
예제 #4
0
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])
예제 #5
0
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
예제 #6
0
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
예제 #7
0
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
예제 #8
0
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
예제 #9
0
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
예제 #10
0
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
예제 #11
0
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
예제 #12
0
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('')