Skip to content

Instantly share code, notes, and snippets.

@mahmoud
Last active July 28, 2020 08:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mahmoud/ce593a4736b9d410669a3dbf5492950e to your computer and use it in GitHub Desktop.
Save mahmoud/ce593a4736b9d410669a3dbf5492950e to your computer and use it in GitHub Desktop.
improved branched glom error ascii design
before = \
'''
Traceback (most recent call last):
File "tmp.py", line 9, in _make_stack
glom(target, spec)
File "/home/mahmoud/projects/glom/glom/core.py", line 2024, in glom
raise err
glom.core.PathAccessError: error raised while processing, details below.
Target-spec trace (most recent last):
- Target: [None]
- Spec: Match(Switch([(1, 1), ('a', 'a'), ([None], Switch([(1, 1), ('a', 'a'), ([None], T.a)]))]))
+ Spec: Switch([(1, 1), ('a', 'a'), ([None], Switch([(1, 1), ('a', 'a'), ([None], T.a)]))])
> Failed Branch:
|- Spec: 1
|- glom.matching.MatchError: [None] does not match 1
> Failed Branch:
|- Spec: 'a'
|- glom.matching.MatchError: [None] does not match 'a'
> Failed Branch:
|- Spec: [None]
|+ Spec: Switch([(1, 1), ('a', 'a'), ([None], T.a)])
|> Failed Branch:
||- Spec: 1
||- glom.matching.MatchError: [None] does not match 1
|> Failed Branch:
||- Spec: 'a'
||- glom.matching.MatchError: [None] does not match 'a'
|> Failed Branch:
||- Spec: [None]
||- Spec: T.a
glom.core.PathAccessError: could not access 'a', part 0 of T.a, got error: AttributeError("'list' object has no attribute 'a'")
'''
after = \
'''
Traceback (most recent call last):
File "tmp.py", line 9, in _make_stack
glom(target, spec)
File "/home/mahmoud/projects/glom/glom/core.py", line 2024, in glom
raise err
glom.core.PathAccessError: error raised while processing, details below.
Target-spec trace (most recent last):
- Target: [None]
- Spec: Match(Switch([(1, 1), ('a', 'a'), ([None], Switch([(1, 1), ('a', 'a'), ([None], T.a)]))]))
+ Spec: Switch([(1, 1), ('a', 'a'), ([None], Switch([(1, 1), ('a', 'a'), ([None], T.a)]))])
|\ Spec: 1
|X glom.matching.MatchError: [None] does not match 1
|\ Spec: 'a'
|X glom.matching.MatchError: [None] does not match 'a'
|\ Spec: [None]
|+ Spec: Switch([(1, 1), ('a', 'a'), ([None], T.a)])
||\ Spec: 1
||X glom.matching.MatchError: [None] does not match 1
||\ Spec: 'a'
||X glom.matching.MatchError: [None] does not match 'a'
||\ Spec: [None]
||| Spec: T.a
glom.core.PathAccessError: could not access 'a', part 0 of T.a, got error: AttributeError("'list' object has no attribute 'a'")
'''
steps = \
'''
1. Eliminate "Failed Branch"
2. Instead, "X" on the exceptions
3. And \ on the first spec after exceptions and switches
'''
import traceback
from glom import Match, Switch, T, glom, GlomError, Coalesce
def _make_stack(spec, **kwargs):
target = kwargs.pop('target', [None])
assert not kwargs
try:
glom(target, spec)
except GlomError as e:
stack = traceback.format_exc()
return stack
switch_spec = Match(Switch(
[(1, 1), ('a', 'a'), ([None], Switch(
[(1, 1), ('a', 'a'), ([None], T.a)]))]))
stack = _make_stack(switch_spec)
print(stack)
val = {'a': {'b': 'c'}, # basic dictionary nesting
'd': {'e': ['f'], # list in dictionary
'g': 'h'},
'i': [{'j': 'k', 'l': 'm'}], # list of dictionaries
'n': 'o',
'xxx': {'z': {'v': 0}}}
actual = _make_stack(Coalesce(('xxx', 'z', 'n'), 'yyy'), target=val)
print(actual)
# or let, delete, assign?
# or let, assign (overwrite), delete, assign (if fail, reassign back)
Traceback (most recent call last):
File "tmp.py", line 9, in _make_stack
glom(target, spec)
File "/home/mahmoud/projects/glom/glom/core.py", line 2068, in glom
raise err
glom.core.PathAccessError: error raised while processing, details below.
Target-spec trace (most recent last):
- Target: [None]
- Spec: Match(Switch([(1, 1), ('a', 'a'), ([None], Switch([(1, 1), ('a', '...
+ Spec: Switch([(1, 1), ('a', 'a'), ([None], Switch([(1, 1), ('a', 'a'), (...
|\ Spec: 1
|X glom.matching.MatchError: [None] does not match 1
|\ Spec: 'a'
|X glom.matching.MatchError: [None] does not match 'a'
|\ Spec: [None]
|+ Spec: Switch([(1, 1), ('a', 'a'), ([None], T.a)])
||\ Spec: 1
||X glom.matching.MatchError: [None] does not match 1
||\ Spec: 'a'
||X glom.matching.MatchError: [None] does not match 'a'
||\ Spec: [None]
||| Spec: T.a
glom.core.PathAccessError: could not access 'a', part 0 of T.a, got error: AttributeError("'list' object has no attribute 'a'")
Traceback (most recent call last):
File "tmp.py", line 9, in _make_stack
glom(target, spec)
File "/home/mahmoud/projects/glom/glom/core.py", line 2068, in glom
raise err
glom.core.CoalesceError: error raised while processing, details below.
Target-spec trace (most recent last):
- Target: {'a': {'b': 'c'}, 'd': {'e': ['f'], 'g': 'h'}, 'i': [{'j... (len=5)
+ Spec: Coalesce(('xxx', 'z', 'n'), 'yyy')
|\ Spec: ('xxx', 'z', 'n')
|| Spec: 'xxx'
|| Target: {'z': {'v': 0}}
|| Spec: 'z'
|| Target: {'v': 0}
|| Spec: 'n'
|X glom.core.PathAccessError: could not access 'n', part 0 of Path('n'), got error: KeyError('n')
|\ Spec: 'yyy'
|X glom.core.PathAccessError: could not access 'yyy', part 0 of Path('yyy'), got error: KeyError('yyy')
glom.core.CoalesceError: no valid values found. Tried (('xxx', 'z', 'n'), 'yyy') and got (PathAccessError, PathAccessError) (at path ['xxx', 'z'])
@mahmoud
Copy link
Author

mahmoud commented Jul 28, 2020

Actually did get it working shortly after saying I wouldn't. It's a toy/hack, but it works (as seen above).

def format_target_spec_trace(scope, root_error, width=TRACE_WIDTH, depth=0, prev_target=_MISSING):
    """
    unpack a scope into a multi-line but short summary
    """
    segments = []
    indent = " " + "|" * depth

    hack = []
    def mk_fmt(label, tick="- "):
        def _fmt(v):
            _tick = tick
            if not hack and depth > 0:
                _tick = '\ '
                hack.append(True)
            if _tick == '- ' and depth > 0:
                _tick = '| '

            pre = indent + _tick + label + ": "
            fmt_width = width - len(pre)
            return pre + _format_trace_value(v, fmt_width)

        return _fmt
    fmt_t = mk_fmt("Target")
    fmt_s = mk_fmt("Spec")
    fmt_b = mk_fmt("Spec", "+ ")
    recurse = lambda s: format_target_spec_trace(s, root_error, width, depth + 1, prev_target)
    tb_exc_line = lambda e: "".join(traceback.format_exception_only(type(e), e))[:-1]
    fmt_e = lambda e: indent + "X " + tb_exc_line(e)

    for scope, spec, target, error, branches in _unpack_stack(scope):
        if target is not prev_target:
            segments.append(fmt_t(target))
        prev_target = target
        if branches:
            segments.append(fmt_b(spec))
            segments.extend([recurse(s) for s in branches])
        else:
            segments.append(fmt_s(spec))
        if error is not None and error is not root_error:
            segments.append(fmt_e(error))
    return "\n".join(segments)

(don't judge lol)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment