diff --git a/glom/core.py b/glom/core.py index a903b91..f06732a 100644 --- a/glom/core.py +++ b/glom/core.py @@ -775,9 +775,8 @@ def __init__(self, spec, scope=None): def glom(self, target, **kw): scope = dict(self.scope) scope.update(kw.get('scope', {})) - kw['scope'] = ChainMap(scope) - glom_ = scope.get(glom, glom) - return glom_(target, self.spec, **kw) + kw['scope'] = scope + return glom(target, self.spec, **kw) def glomit(self, target, scope): scope.update(self.scope) @@ -1983,7 +1982,7 @@ def _get_iterable_handler(type_obj): self.register_op('get', lambda _: getattr) -_DEFAULT_SCOPE = ChainMap({}) +_DEFAULT_SCOPE = ChainMap({CHILD_ERRORS: []}) def glom(target, spec, **kwargs): diff --git a/glom/streaming.py b/glom/streaming.py index 9b83abf..20d5064 100644 --- a/glom/streaming.py +++ b/glom/streaming.py @@ -21,7 +21,7 @@ from boltons.iterutils import split_iter, chunked_iter, windowed_iter, unique_iter, first from boltons.funcutils import FunctionBuilder -from .core import glom, T, STOP, SKIP, _MISSING, Path, TargetRegistry, Call, Spec, S, bbrepr, format_invocation +from .core import glom, T, STOP, SKIP, _MISSING, Path, TargetRegistry, Call, Spec, S, bbrepr, format_invocation, GlomError, Pipe from .matching import Check class Iter(object): @@ -332,7 +332,7 @@ def all(self): Note that this spec will always consume the whole iterable, and as such, the spec returned is *not* an :class:`Iter()` instance. """ - return (self, list) + return Pipe(self, list) def first(self, key=T, default=None): """A convenience method for lazily yielding a single truthy item from @@ -349,7 +349,7 @@ def first(self, key=T, default=None): As this spec yields at most one item, and not an iterable, the spec returned from this method is not an :class:`Iter()` instance. """ - return (self, First(key=key, default=default)) + return Pipe(self, First(key=key, default=default)) class First(object): @@ -372,7 +372,10 @@ def __init__(self, key=T, default=None): self._spec = key self._default = default - spec_glom = Spec(Call(partial, args=(Spec(self._spec).glom,), kwargs={'scope': S})) + spec_glom = Spec(Call(partial, + args=(Spec(self._spec).glom,), + kwargs={'scope': S, 'skip_exc': GlomError + })) self._first = Call(first, args=(T,), kwargs={'default': default, 'key': spec_glom}) def glomit(self, target, scope): @@ -383,3 +386,15 @@ def __repr__(self): if self._default is None: return '%s(%s)' % (cn, bbrepr(self._spec)) return '%s(%s, default=%s)' % (cn, bbrepr(self._spec), bbrepr(self._default)) + + +""" +Issues: + +* Spec.glom() impl was way obsolete for all the other advancements +* glom not "reentrant" (calling glom from inside glom, passing scope along) breaks bc DEFAULT_SCOPE didn't have CHILD_ERRORS and the way error handling is written requires all maps to have it (.maps[1]) +* Should glom() respect if people have passed in their own MODE in scope? right now it always sets it to AUTO +* The whole "terminus spec" approach to Iter().first() and Iter().all() assumes that tuple=Pipe +* In matching.py, _glom_match should be MATCH to mirror AUTO/FILL +* Also in matching.py, _glom_match seems to handle type (non-instance) specs, we should try to move away from those. +""" diff --git a/glom/test/test_streaming.py b/glom/test/test_streaming.py index 18f94c5..7c9dc85 100644 --- a/glom/test/test_streaming.py +++ b/glom/test/test_streaming.py @@ -4,7 +4,7 @@ from itertools import count, dropwhile, chain from glom import Iter -from glom import glom, SKIP, STOP, T, Call, Spec, Glommer, Check, SKIP +from glom import glom, SKIP, STOP, T, Call, Spec, Glommer, Check, SKIP, Pipe RANGE_5 = list(range(5)) @@ -192,13 +192,20 @@ def test_first(): out = glom(target, spec) assert out == 3j assert next(target) == 4 - assert repr(spec) == '(Iter(), First(T.imag))' + assert repr(spec) == 'Pipe(Iter(), First(T.imag))' spec = Iter().first(T.imag, default=0) target = iter([1, 2, 4]) out = glom(target, spec) assert out == 0 - assert repr(spec) == '(Iter(), First(T.imag, default=0))' + assert repr(spec) == 'Pipe(Iter(), First(T.imag, default=0))' + + target = [{}, {}, {'user': {'client': 123}}, {}] + + output = glom(target, Iter().first('user.client')) + + assert output['user']['client'] == 123 + def test_all(): @@ -207,4 +214,4 @@ def test_all(): out = glom(int_iter, Iter().all()) assert out == list(range(10)) assert next(int_iter, None) is None - assert repr(Iter().all()) == repr((Iter(), list)) + assert repr(Iter().all()) == repr(Pipe(Iter(), list))