Skip to content

Commit

Permalink
Merge pull request #108 from mahmoud/capture
Browse files Browse the repository at this point in the history
Capture
  • Loading branch information
mahmoud committed Oct 24, 2019
2 parents fca96d8 + 0034b2b commit 84d35e0
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 13 deletions.
2 changes: 2 additions & 0 deletions glom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
OMIT, # backwards compat
SKIP,
STOP,
UP,
ROOT,
MODE,
Check,
Path,
Expand Down
49 changes: 40 additions & 9 deletions glom/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -830,13 +830,6 @@ def __getattr__(self, name):
return _t_child(self, '.', name)

def __getitem__(self, item):
if item is UP:
newpath = _T_PATHS[self][:-2]
if not newpath:
return T
t = TType()
_T_PATHS[t] = _T_PATHS[self][:-2]
return t
return _t_child(self, '[', item)

def __call__(self, *args, **kwargs):
Expand Down Expand Up @@ -874,7 +867,7 @@ def _t_eval(target, _t, scope):
raise ValueError('TType instance with invalid root object')
while i < len(t_path):
op, arg = t_path[i], t_path[i + 1]
if type(arg) in (Spec, TType):
if type(arg) in (Spec, TType, Literal):
arg = scope[glom](target, arg, scope)
if op == '.':
try:
Expand Down Expand Up @@ -913,6 +906,40 @@ def _t_eval(target, _t, scope):
_T_PATHS[T] = (T,)
_T_PATHS[S] = (S,)
UP = make_sentinel('UP')
ROOT = make_sentinel('ROOT')


class Let(object):
"""
This specifier type assigns variables to the scope.
>>> target = {'data': {'val': 9}}
>>> spec = Let(value=T['data']['val']).over({'val': S['value']})
>>> glom(target, spec)
{'val': 9}
Other form is Let(foo=spec1, bar=spec2).over(subspec)
"""
def __init__(self, **kw):
if not kw:
raise TypeError('expected at least one keyword argument')
self._binding = kw

def over(self, subspec):
return _LetOver(self, subspec)

def _write_to(self, target, scope):
scope.update({
k: scope[glom](target, v, scope) for k, v in self._binding.items()})


class _LetOver(object):
def __init__(self, let, subspec):
self.let, self.subspec = let, subspec

def glomit(self, target, scope):
self.let._write_to(target, scope)
return scope[glom](target, self.subspec, scope)


def _format_t(path, root=T):
Expand Down Expand Up @@ -1463,6 +1490,9 @@ def glom(target, spec, **kwargs):
Inspect: kwargs.pop('inspector', None),
MODE: _glom_build,
})
scope[UP] = scope
scope[ROOT] = scope
scope[T] = target
scope.update(kwargs.pop('scope', {}))
if kwargs:
raise TypeError('unexpected keyword args: %r' % sorted(kwargs.keys()))
Expand All @@ -1476,9 +1506,10 @@ def glom(target, spec, **kwargs):


def _glom(target, spec, scope):
scope = scope.new_child()
scope, parent = scope.new_child(), scope
scope[T] = target
scope[Spec] = spec
scope[UP] = parent

if isinstance(spec, TType): # must go first, due to callability
return _t_eval(target, spec, scope)
Expand Down
20 changes: 16 additions & 4 deletions glom/test/test_basic.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@

import pytest

from glom import glom, SKIP, STOP, Path, Inspect, Coalesce, CoalesceError, Literal, Call, T, S
from glom import glom, SKIP, STOP, Path, Inspect, Coalesce, CoalesceError, Literal, Call, T, S, Spec
import glom.core as glom_core
from glom.core import Spec, UP # probationary
from glom.core import UP, ROOT, Let

from glom import OMIT # backwards compat

Expand Down Expand Up @@ -193,8 +193,8 @@ def __init__(s, a): s.a = a
assert glom([1], Call(F, args=T)).a == 1
assert glom(F, T(T)).a == F
assert glom([F, 1], T[0](T[1]).a) == 1
assert glom([[1]], T[0][0][0][UP]) == 1
assert glom([[1]], T[0][UP][UP][UP]) == [[1]] # tops out at just T
assert glom([[1]], S[UP][Literal(T)][0][0]) == 1
assert glom([[1]], S[UP][UP][UP][Literal(T)]) == [[1]] # tops out

assert list(glom({'a': 'b'}, Call(T.values))) == ['b']

Expand Down Expand Up @@ -321,3 +321,15 @@ def test_inspect():

assert glom(target, spec, default='default') == 'default'
assert len(tracker) == 1


def test_let():
data = {'a': 1, 'b': [{'c': 2}, {'c': 3}]}
output = [{'a': 1, 'c': 2}, {'a': 1, 'c': 3}]
assert glom(data, Let(a='a').over(('b', [{'a': S['a'], 'c': 'c'}]))) == output
assert glom(data, ('b', [{'a': S[ROOT][Literal(T)]['a'], 'c': 'c'}])) == output

with pytest.raises(TypeError):
Let('posarg')
with pytest.raises(TypeError):
Let()

0 comments on commit 84d35e0

Please sign in to comment.