meerschaum.utils.warnings

Handle all things warnings and errors here

  1#! /usr/bin/env python
  2# -*- coding: utf-8 -*-
  3# vim:fenc=utf-8
  4
  5"""
  6Handle all things warnings and errors here
  7"""
  8
  9from __future__ import annotations
 10from meerschaum.utils.typing import Any, Union
 11
 12import sys
 13import warnings
 14
 15warnings.filterwarnings(
 16    "always",
 17    category = UserWarning
 18)
 19warnings.filterwarnings(
 20    "always",
 21    category = ImportWarning
 22)
 23warnings.filterwarnings(
 24    "ignore",
 25    category = RuntimeWarning
 26)
 27
 28
 29def enable_depreciation_warnings(name) -> None:
 30    """Enable depreciation warnings in the warnings module."""
 31    import meerschaum.actions
 32    warnings.filterwarnings(
 33        "always",
 34        category = DeprecationWarning,
 35        module = name
 36    )
 37
 38def warn(*args, stacklevel=2, stack=True, color: bool = True, **kw) -> None:
 39    """
 40    Print a stylized warning message.
 41    May be captured by `warnings.filterwarnings()`.
 42    """
 43    if stacklevel is None:
 44        stacklevel = 1
 45        stack = False
 46    _old_sw = warnings.showwarning
 47
 48    get_config = None
 49    if color:
 50        try:
 51            from meerschaum.utils.formatting import (
 52                CHARSET, ANSI, colored, fill_ansi, highlight_pipes, _init
 53            )
 54        except ImportError:
 55            CHARSET = 'ascii'
 56            ANSI = False
 57        try:
 58            from meerschaum.config import get_config as _get_config
 59            from meerschaum.config import _config
 60            cf = _config()
 61            get_config = _get_config
 62        except ImportError:
 63            get_config = None
 64
 65    if get_config is None and color:
 66        try:
 67            warn_config = cf['formatting']['warnings']
 68        except KeyError:
 69            warn_config = {
 70                'ansi' : {'color' : []},
 71                'unicode' : {'icon' : ''},
 72                'ascii' : {'icon' : ''},
 73            }
 74    elif color:
 75        warn_config = get_config('formatting', 'warnings', patch=True)
 76    a = list(args)
 77    a[0] = ' ' + (warn_config[CHARSET]['icon'] if color else '') + ' ' + str(a[0])
 78    if color:
 79        if ANSI:
 80            _init()
 81            a[0] = fill_ansi(highlight_pipes(a[0]), **warn_config['ansi']['rich'])
 82
 83    ### Optionally omit the warning location.
 84    def _no_stack_sw(message, category, filename, lineno, file=None, line=None):
 85        sys.stderr.write(str(message) + '\n')
 86
 87    if not stack:
 88        warnings.showwarning = _no_stack_sw
 89    warnings.warn(*a, stacklevel=stacklevel, **kw)
 90    if not stack:
 91        warnings.showwarning = _old_sw
 92
 93
 94def exception_with_traceback(
 95        message: str,
 96        exception_class = Exception, 
 97        stacklevel = 1,
 98        tb_type = 'single'
 99    ):
100    """Traceback construction help found here:
101    https://stackoverflow.com/questions/27138440/how-to-create-a-traceback-object
102    """
103    import types
104    tb, depth = None, 0
105    while True:
106        try:
107            frame = sys._getframe(depth)
108            depth += 1
109        except ValueError as e:
110            break
111
112        tb = types.TracebackType(tb, frame, frame.f_lasti, frame.f_lineno)
113
114    tbs, _tb = [], tb
115    while True:
116        if _tb is None:
117            break
118        tbs.append(_tb)
119        _tb = _tb.tb_next
120
121    found_main, main_i = False, 0
122    first_mrsm_after_main = None
123    last_mrsm_i = None
124    tbs[(-1 * stacklevel)].tb_next = None
125    for i, _tb in enumerate([_tb for _tb in tbs]):
126        if 'meerschaum' in str(_tb.tb_frame) and '__main__.py' in str(_tb.tb_frame):
127            found_main = True
128            main_i = i
129            continue
130        if i >= (len(tbs) - (stacklevel - 1)):
131            tbs[i] = None
132        elif (
133                found_main and 'meerschaum' in str(_tb.tb_frame)
134                and first_mrsm_after_main is None
135                and 'Shell' not in str(_tb.tb_frame)
136            ):
137            first_mrsm_after_main = i
138        elif 'meerschaum' in str(_tb.tb_frame):
139            last_mrsm_i = i
140
141    tbs = [_tb for _tb in tbs if tb is not None]
142
143    if tb_type == 'single':
144        return exception_class(message).with_traceback(tbs[-3])
145    return exception_class(message).with_traceback(tbs[first_mrsm_after_main])
146
147
148def error(
149        message: str,
150        exception_class = Exception,
151        nopretty: bool = False,
152        silent: bool = True,
153        stack: bool = True,
154        raise_: bool = True,
155    ):
156    """
157    Raise an exception with supressed traceback.
158    """
159    from meerschaum.utils.formatting import (
160        CHARSET, ANSI, get_console, fill_ansi, highlight_pipes, _init
161    )
162    from meerschaum.utils.packages import import_rich, attempt_import
163    from meerschaum.config import get_config
164    import types, inspect
165    rich = import_rich()
166    rich_traceback = attempt_import('rich.traceback')
167    error_config = get_config('formatting', 'errors', patch=True)
168    message = ' ' + error_config[CHARSET]['icon'] + ' ' + str(message)
169    exception = exception_with_traceback(message, exception_class, stacklevel=3)
170    color_message = str(message)
171    color_exception = exception_with_traceback(color_message, exception_class, stacklevel=3)
172    if ANSI and not nopretty:
173        _init()
174        color_message = '\n' + fill_ansi(highlight_pipes(message), **error_config['ansi']['rich'])
175        color_exception = exception_with_traceback(color_message, exception_class, stacklevel=3)
176    try:
177        trace = rich_traceback.Traceback.extract(
178            exception_class, exception, exception.__traceback__
179        )
180        rtb = rich_traceback.Traceback(trace)
181    except Exception as e:
182        trace, rtb = None, None
183    if trace is None or rtb is None:
184        nopretty = True
185    if not nopretty and stack:
186        if get_console() is not None:
187            get_console().print(rtb)
188    frame = sys._getframe(len(inspect.stack()) - 1)
189    if raise_:
190        raise color_exception
191
192
193def info(message: str, icon: bool = True, **kw):
194    """Print an informative message."""
195    from meerschaum.utils.packages import import_rich, attempt_import
196    from meerschaum.utils.formatting import (
197        CHARSET, ANSI, highlight_pipes, fill_ansi, _init
198    )
199    from meerschaum.config import get_config
200    info_config = get_config('formatting', 'info', patch=True)
201    if icon:
202        message = ' ' + info_config[CHARSET]['icon'] + ' ' + message
203    if ANSI:
204        _init()
205        message = highlight_pipes(message)
206        lines = message.split('\n')
207        message = fill_ansi(lines[0], **info_config['ansi']['rich']) + (
208            '\n' + '\n'.join(lines[1:]) if len(lines) > 1 else ''
209        )
210    print(message)
def enable_depreciation_warnings(name) -> None:
30def enable_depreciation_warnings(name) -> None:
31    """Enable depreciation warnings in the warnings module."""
32    import meerschaum.actions
33    warnings.filterwarnings(
34        "always",
35        category = DeprecationWarning,
36        module = name
37    )

Enable depreciation warnings in the warnings module.

def warn(*args, stacklevel=2, stack=True, color: bool = True, **kw) -> None:
39def warn(*args, stacklevel=2, stack=True, color: bool = True, **kw) -> None:
40    """
41    Print a stylized warning message.
42    May be captured by `warnings.filterwarnings()`.
43    """
44    if stacklevel is None:
45        stacklevel = 1
46        stack = False
47    _old_sw = warnings.showwarning
48
49    get_config = None
50    if color:
51        try:
52            from meerschaum.utils.formatting import (
53                CHARSET, ANSI, colored, fill_ansi, highlight_pipes, _init
54            )
55        except ImportError:
56            CHARSET = 'ascii'
57            ANSI = False
58        try:
59            from meerschaum.config import get_config as _get_config
60            from meerschaum.config import _config
61            cf = _config()
62            get_config = _get_config
63        except ImportError:
64            get_config = None
65
66    if get_config is None and color:
67        try:
68            warn_config = cf['formatting']['warnings']
69        except KeyError:
70            warn_config = {
71                'ansi' : {'color' : []},
72                'unicode' : {'icon' : ''},
73                'ascii' : {'icon' : ''},
74            }
75    elif color:
76        warn_config = get_config('formatting', 'warnings', patch=True)
77    a = list(args)
78    a[0] = ' ' + (warn_config[CHARSET]['icon'] if color else '') + ' ' + str(a[0])
79    if color:
80        if ANSI:
81            _init()
82            a[0] = fill_ansi(highlight_pipes(a[0]), **warn_config['ansi']['rich'])
83
84    ### Optionally omit the warning location.
85    def _no_stack_sw(message, category, filename, lineno, file=None, line=None):
86        sys.stderr.write(str(message) + '\n')
87
88    if not stack:
89        warnings.showwarning = _no_stack_sw
90    warnings.warn(*a, stacklevel=stacklevel, **kw)
91    if not stack:
92        warnings.showwarning = _old_sw

Print a stylized warning message. May be captured by warnings.filterwarnings().

def exception_with_traceback( message: str, exception_class=<class 'Exception'>, stacklevel=1, tb_type='single'):
 95def exception_with_traceback(
 96        message: str,
 97        exception_class = Exception, 
 98        stacklevel = 1,
 99        tb_type = 'single'
100    ):
101    """Traceback construction help found here:
102    https://stackoverflow.com/questions/27138440/how-to-create-a-traceback-object
103    """
104    import types
105    tb, depth = None, 0
106    while True:
107        try:
108            frame = sys._getframe(depth)
109            depth += 1
110        except ValueError as e:
111            break
112
113        tb = types.TracebackType(tb, frame, frame.f_lasti, frame.f_lineno)
114
115    tbs, _tb = [], tb
116    while True:
117        if _tb is None:
118            break
119        tbs.append(_tb)
120        _tb = _tb.tb_next
121
122    found_main, main_i = False, 0
123    first_mrsm_after_main = None
124    last_mrsm_i = None
125    tbs[(-1 * stacklevel)].tb_next = None
126    for i, _tb in enumerate([_tb for _tb in tbs]):
127        if 'meerschaum' in str(_tb.tb_frame) and '__main__.py' in str(_tb.tb_frame):
128            found_main = True
129            main_i = i
130            continue
131        if i >= (len(tbs) - (stacklevel - 1)):
132            tbs[i] = None
133        elif (
134                found_main and 'meerschaum' in str(_tb.tb_frame)
135                and first_mrsm_after_main is None
136                and 'Shell' not in str(_tb.tb_frame)
137            ):
138            first_mrsm_after_main = i
139        elif 'meerschaum' in str(_tb.tb_frame):
140            last_mrsm_i = i
141
142    tbs = [_tb for _tb in tbs if tb is not None]
143
144    if tb_type == 'single':
145        return exception_class(message).with_traceback(tbs[-3])
146    return exception_class(message).with_traceback(tbs[first_mrsm_after_main])
def error( message: str, exception_class=<class 'Exception'>, nopretty: bool = False, silent: bool = True, stack: bool = True, raise_: bool = True):
149def error(
150        message: str,
151        exception_class = Exception,
152        nopretty: bool = False,
153        silent: bool = True,
154        stack: bool = True,
155        raise_: bool = True,
156    ):
157    """
158    Raise an exception with supressed traceback.
159    """
160    from meerschaum.utils.formatting import (
161        CHARSET, ANSI, get_console, fill_ansi, highlight_pipes, _init
162    )
163    from meerschaum.utils.packages import import_rich, attempt_import
164    from meerschaum.config import get_config
165    import types, inspect
166    rich = import_rich()
167    rich_traceback = attempt_import('rich.traceback')
168    error_config = get_config('formatting', 'errors', patch=True)
169    message = ' ' + error_config[CHARSET]['icon'] + ' ' + str(message)
170    exception = exception_with_traceback(message, exception_class, stacklevel=3)
171    color_message = str(message)
172    color_exception = exception_with_traceback(color_message, exception_class, stacklevel=3)
173    if ANSI and not nopretty:
174        _init()
175        color_message = '\n' + fill_ansi(highlight_pipes(message), **error_config['ansi']['rich'])
176        color_exception = exception_with_traceback(color_message, exception_class, stacklevel=3)
177    try:
178        trace = rich_traceback.Traceback.extract(
179            exception_class, exception, exception.__traceback__
180        )
181        rtb = rich_traceback.Traceback(trace)
182    except Exception as e:
183        trace, rtb = None, None
184    if trace is None or rtb is None:
185        nopretty = True
186    if not nopretty and stack:
187        if get_console() is not None:
188            get_console().print(rtb)
189    frame = sys._getframe(len(inspect.stack()) - 1)
190    if raise_:
191        raise color_exception

Raise an exception with supressed traceback.

def info(message: str, icon: bool = True, **kw):
194def info(message: str, icon: bool = True, **kw):
195    """Print an informative message."""
196    from meerschaum.utils.packages import import_rich, attempt_import
197    from meerschaum.utils.formatting import (
198        CHARSET, ANSI, highlight_pipes, fill_ansi, _init
199    )
200    from meerschaum.config import get_config
201    info_config = get_config('formatting', 'info', patch=True)
202    if icon:
203        message = ' ' + info_config[CHARSET]['icon'] + ' ' + message
204    if ANSI:
205        _init()
206        message = highlight_pipes(message)
207        lines = message.split('\n')
208        message = fill_ansi(lines[0], **info_config['ansi']['rich']) + (
209            '\n' + '\n'.join(lines[1:]) if len(lines) > 1 else ''
210        )
211    print(message)

Print an informative message.