meerschaum.utils.debug

Functions to handle debug statements

  1#! /usr/bin/env python
  2# -*- coding: utf-8 -*-
  3# vim:fenc=utf-8
  4
  5"""
  6Functions to handle debug statements
  7"""
  8
  9from __future__ import annotations
 10
 11from datetime import datetime, timezone
 12import meerschaum as mrsm
 13from meerschaum.utils.typing import Union, Optional, List
 14
 15
 16_rich_text = None
 17def _import_rich_text_for_dprint():
 18    """
 19    Avoid calling `attempt_import()` on every dprint.
 20    """
 21    global _rich_text
 22    if _rich_text is not None:
 23        return _rich_text
 24
 25    from meerschaum.utils.packages import import_rich, attempt_import
 26    _ = import_rich()
 27    _rich_text = attempt_import('rich.text', lazy=False)
 28    return _rich_text
 29
 30
 31def dprint(
 32    msg: str,
 33    leader: bool = True,
 34    timestamp: bool = True,
 35    package: bool = True,
 36    color: Optional[Union[str, List[str]], bool] = None,
 37    attrs: Optional[List[str]] = None,
 38    nopretty: bool = False,
 39    _progress: Optional['rich.progress.Progress'] = None,
 40    _task: Optional[int] = None,
 41    _package: Optional[str] = None,
 42    _lineno: Optional[int] = None,
 43    **kw
 44) -> None:
 45    """
 46    Print a debug message to stderr.
 47
 48    The header names the calling module and line. The Rust core passes its own module path and
 49    line as `_package` and `_lineno` (its debug events have no Python caller).
 50    """
 51    import meerschaum.config.paths as paths
 52    if attrs is None:
 53        attrs = []
 54
 55    if not isinstance(color, bool) and not nopretty:
 56        try:
 57            from meerschaum.utils.formatting import CHARSET, ANSI, colored
 58        except Exception as e:
 59            CHARSET, ANSI, colored = 'ascii', False, None
 60        from meerschaum.config import _config
 61        cf = _config('formatting')
 62        _color = color
 63    else:
 64        CHARSET, ANSI, colored, _color, cf = 'ascii', False, None, None, None
 65
 66    if timestamp:
 67        from meerschaum.utils.dtypes import get_current_timestamp
 68        now = get_current_timestamp('ms').replace(tzinfo=None)
 69    else:
 70        now = None
 71
 72    import logging, sys, inspect
 73    logging.basicConfig(format='%(message)s')
 74    log = logging.getLogger(__name__)
 75
 76    if _package is not None:
 77        parent_package = _package
 78        parent_lineno = _lineno
 79    else:
 80        parent_frame = inspect.stack()[1][0]
 81        parent_info = inspect.getframeinfo(parent_frame)
 82        parent_lineno = parent_info.lineno
 83        parent_globals = parent_frame.f_globals
 84        parent_package = parent_globals['__name__']
 85    msg = str(msg)
 86    premsg = ""
 87
 88    if now:
 89        premsg = now.isoformat().split('T', maxsplit=1)[-1][:-3] + (' | ' if package else ':')
 90    if package:
 91        premsg = premsg + parent_package + (
 92            (':' + str(parent_lineno)) if parent_lineno is not None else ''
 93        )
 94    if premsg:
 95        premsg += "\n"
 96    if leader and cf is not None:
 97        try:
 98            debug_leader = cf['formatting']['debug'][CHARSET]['icon'] if cf is not None else ''
 99        except KeyError:
100            print(
101                "Failed to load config. " +
102                "Please delete the following directories and restart Meerschaum:"
103            )
104            for p in [paths.CONFIG_DIR_PATH, paths.PERMANENT_PATCH_DIR_PATH]:
105                print('  - ' + str(p))
106            debug_leader = ''
107        premsg = ' ' + debug_leader + ' ' + premsg
108
109    if ANSI:
110        if _color is not None:
111            if isinstance(_color, str):
112                _color = [_color]
113        else:
114            if cf is not None and not nopretty:
115                try:
116                    _color = cf['formatting']['debug']['ansi']['rich'] if cf is not None else {}
117                except KeyError:
118                    _color = {}
119            else:
120                _color = {}
121        if colored is not None:
122            premsg = colored(premsg, **_color)
123
124    if _progress is not None:
125        rich_text = _import_rich_text_for_dprint()
126        text = rich_text.Text.from_ansi(premsg + msg)
127        _progress.console.log(text)
128    else:
129        ### v5: debug output goes to stderr (v4 printed to stdout), like warnings and the
130        ### Rust core's debug output. Job logs capture both streams.
131        print(premsg + msg, file=sys.stderr)
132
133
134def _checkpoint(
135    _progress: Optional['rich.progress.Progress'] = None,
136    _task: Optional[int] = None,
137    _total: Optional[int] = None,
138    **kw
139) -> None:
140    """If the `_progress` and `_task` objects are provided, increment the task by one step.
141    If `_total` is provided, update the total instead.
142    """
143    if _progress is not None and _task is not None:
144        _kw = {'total': _total} if _total is not None else {'advance': 1}
145        _progress.update(_task, **_kw)
146
147
148def trace(browser: bool = True):
149    """
150    Open a web-based debugger to trace the execution of the program.
151    """
152    from meerschaum.utils.packages import attempt_import
153    heartrate = attempt_import('heartrate')
154    heartrate.trace(files=heartrate.files.all, browser=browser)
def dprint( msg: str, leader: bool = True, timestamp: bool = True, package: bool = True, color: 'Optional[Union[str, List[str]], bool]' = None, attrs: List[str] | None = None, nopretty: bool = False, _progress: rich.progress.Progress | None = None, _task: int | None = None, _package: str | None = None, _lineno: int | None = None, **kw) -> None:
 32def dprint(
 33    msg: str,
 34    leader: bool = True,
 35    timestamp: bool = True,
 36    package: bool = True,
 37    color: Optional[Union[str, List[str]], bool] = None,
 38    attrs: Optional[List[str]] = None,
 39    nopretty: bool = False,
 40    _progress: Optional['rich.progress.Progress'] = None,
 41    _task: Optional[int] = None,
 42    _package: Optional[str] = None,
 43    _lineno: Optional[int] = None,
 44    **kw
 45) -> None:
 46    """
 47    Print a debug message to stderr.
 48
 49    The header names the calling module and line. The Rust core passes its own module path and
 50    line as `_package` and `_lineno` (its debug events have no Python caller).
 51    """
 52    import meerschaum.config.paths as paths
 53    if attrs is None:
 54        attrs = []
 55
 56    if not isinstance(color, bool) and not nopretty:
 57        try:
 58            from meerschaum.utils.formatting import CHARSET, ANSI, colored
 59        except Exception as e:
 60            CHARSET, ANSI, colored = 'ascii', False, None
 61        from meerschaum.config import _config
 62        cf = _config('formatting')
 63        _color = color
 64    else:
 65        CHARSET, ANSI, colored, _color, cf = 'ascii', False, None, None, None
 66
 67    if timestamp:
 68        from meerschaum.utils.dtypes import get_current_timestamp
 69        now = get_current_timestamp('ms').replace(tzinfo=None)
 70    else:
 71        now = None
 72
 73    import logging, sys, inspect
 74    logging.basicConfig(format='%(message)s')
 75    log = logging.getLogger(__name__)
 76
 77    if _package is not None:
 78        parent_package = _package
 79        parent_lineno = _lineno
 80    else:
 81        parent_frame = inspect.stack()[1][0]
 82        parent_info = inspect.getframeinfo(parent_frame)
 83        parent_lineno = parent_info.lineno
 84        parent_globals = parent_frame.f_globals
 85        parent_package = parent_globals['__name__']
 86    msg = str(msg)
 87    premsg = ""
 88
 89    if now:
 90        premsg = now.isoformat().split('T', maxsplit=1)[-1][:-3] + (' | ' if package else ':')
 91    if package:
 92        premsg = premsg + parent_package + (
 93            (':' + str(parent_lineno)) if parent_lineno is not None else ''
 94        )
 95    if premsg:
 96        premsg += "\n"
 97    if leader and cf is not None:
 98        try:
 99            debug_leader = cf['formatting']['debug'][CHARSET]['icon'] if cf is not None else ''
100        except KeyError:
101            print(
102                "Failed to load config. " +
103                "Please delete the following directories and restart Meerschaum:"
104            )
105            for p in [paths.CONFIG_DIR_PATH, paths.PERMANENT_PATCH_DIR_PATH]:
106                print('  - ' + str(p))
107            debug_leader = ''
108        premsg = ' ' + debug_leader + ' ' + premsg
109
110    if ANSI:
111        if _color is not None:
112            if isinstance(_color, str):
113                _color = [_color]
114        else:
115            if cf is not None and not nopretty:
116                try:
117                    _color = cf['formatting']['debug']['ansi']['rich'] if cf is not None else {}
118                except KeyError:
119                    _color = {}
120            else:
121                _color = {}
122        if colored is not None:
123            premsg = colored(premsg, **_color)
124
125    if _progress is not None:
126        rich_text = _import_rich_text_for_dprint()
127        text = rich_text.Text.from_ansi(premsg + msg)
128        _progress.console.log(text)
129    else:
130        ### v5: debug output goes to stderr (v4 printed to stdout), like warnings and the
131        ### Rust core's debug output. Job logs capture both streams.
132        print(premsg + msg, file=sys.stderr)

Print a debug message to stderr.

The header names the calling module and line. The Rust core passes its own module path and line as _package and _lineno (its debug events have no Python caller).

def trace(browser: bool = True):
149def trace(browser: bool = True):
150    """
151    Open a web-based debugger to trace the execution of the program.
152    """
153    from meerschaum.utils.packages import attempt_import
154    heartrate = attempt_import('heartrate')
155    heartrate.trace(files=heartrate.files.all, browser=browser)

Open a web-based debugger to trace the execution of the program.