def test_assign(): class Foo(object): pass assert glom({}, Assign(T['a'], 1)) == {'a': 1} assert glom({'a': {}}, Assign(T['a']['a'], 1)) == {'a': {'a': 1}} assert glom({'a': {}}, Assign('a.a', 1)) == {'a': {'a': 1}} assert glom(Foo(), Assign(T.a, 1)).a == 1 assert glom({}, Assign('a', 1)) == {'a': 1} assert glom(Foo(), Assign('a', 1)).a == 1 assert glom({'a': Foo()}, Assign('a.a', 1))['a'].a == 1 def r(): r = {} r['r'] = r return r assert glom(r(), Assign('r.r.r.r.r.r.r.r.r', 1)) == {'r': 1} assert glom(r(), Assign(T['r']['r']['r']['r'], 1)) == {'r': 1} assert glom(r(), Assign(Path('r', 'r', T['r']), 1)) == {'r': 1} assert assign(r(), Path('r', 'r', T['r']), 1) == {'r': 1} with pytest.raises(TypeError, match='path argument must be'): Assign(1, 'a') with pytest.raises(ValueError, match='path must have at least one element'): Assign(T, 1) assert repr(Assign(T.a, 1)) == 'Assign(T.a, 1)' assign_spec = Assign(T.a, 1, missing=dict) assert repr(assign_spec) == "Assign(T.a, 1, missing=dict)" assert repr(assign_spec) == repr(eval(repr(assign_spec)))
def test_path_access_error_message(): # test fuzzy access with raises(GlomError) as exc_info: glom({}, 'a.b') assert ( "PathAccessError: could not access 'a', part 0 of Path('a', 'b'), got error: KeyError" in exc_info.exconly()) ke = repr(KeyError('a')) # py3.7+ changed the keyerror repr assert repr( exc_info.value) == "PathAccessError(" + ke + ", Path('a', 'b'), 0)" # test multi-part Path with T, catchable as a KeyError with raises(KeyError) as exc_info: # don't actually use glom to copy your data structures please glom({'a': {'b': 'c'}}, Path('a', T.copy(), 'd')) assert ( "PathAccessError: could not access 'd', part 3 of Path('a', T.copy(), 'd'), got error: KeyError" in exc_info.exconly()) ke = repr(KeyError('d')) # py3.7+ changed the keyerror repr assert repr(exc_info.value ) == "PathAccessError(" + ke + ", Path('a', T.copy(), 'd'), 3)" # test AttributeError with raises(GlomError) as exc_info: glom({'a': {'b': 'c'}}, Path('a', T.b)) assert ( "PathAccessError: could not access 'b', part 1 of Path('a', T.b), got error: AttributeError" in exc_info.exconly()) ae = repr(AttributeError("'dict' object has no attribute 'b'")) assert repr( exc_info.value) == "PathAccessError(" + ae + ", Path(\'a\', T.b), 1)"
def test_delete(): class Foo(object): def __init__(self, d=None): for k, v in d.items(): setattr(self, k, v) assert glom({'a': 1}, Delete(T['a'])) == {} assert glom({'a': {'a': 1}}, Delete(T['a']['a'])) == {'a': {}} assert glom({'a': {'a': 1}}, Delete('a.a')) == {'a': {}} assert not hasattr(glom(Foo({'a': 1}), Delete(T.a)), 'a') assert glom({'a': 1}, Delete('a')) == {} assert not hasattr(glom(Foo({'a': 1}), Delete('a')), 'a') assert not hasattr(glom({'a': Foo({'a': 1})}, Delete('a.a'))['a'], 'a') def r(): r = {} r['r'] = r return r assert glom(r(), Delete('r.r.r.r.r.r.r.r.r')) == {} assert glom(r(), Delete(T['r']['r']['r']['r'])) == {} assert glom(r(), Delete(Path('r', 'r', T['r']))) == {} assert delete(r(), Path('r', 'r', T['r'])) == {} with pytest.raises(TypeError, match='path argument must be'): Delete(1, 'a') with pytest.raises(ValueError, match='path must have at least one element'): Delete(T, 1) assert repr(Delete(T.a)) == 'Delete(T.a)'
def test_path_items(): path = Path(T.a, 1, 2, T(test='yes')) assert path.items() == (('.', 'a'), ('P', 1), ('P', 2), ('(', ((), {'test': 'yes'}))) assert Path().items() == ()
def test_path_items(): path = Path(T.a, 1, 2, T(test='yes')) assert path.items() == (('.', 'a'), ('P', 1), ('P', 2), ('(', ((), { 'test': 'yes' }))) assert Path().items() == ()
def test_delete(): class Foo(object): def __init__(self, d=None): for k, v in d.items(): setattr(self, k, v) assert glom({'a': 1}, Delete(T['a'])) == {} assert glom({'a': {'a': 1}}, Delete(T['a']['a'])) == {'a': {}} assert glom({'a': {'a': 1}}, Delete('a.a')) == {'a': {}} assert not hasattr(glom(Foo({'a': 1}), Delete(T.a)), 'a') assert glom({'a': 1}, Delete('a')) == {} assert not hasattr(glom(Foo({'a': 1}), Delete('a')), 'a') assert not hasattr(glom({'a': Foo({'a': 1})}, Delete('a.a'))['a'], 'a') def r(): r = {} r['r'] = r return r assert glom(r(), Delete('r.r.r.r.r.r.r.r.r')) == {} assert glom(r(), Delete(T['r']['r']['r']['r'])) == {} assert glom(r(), Delete(Path('r', 'r', T['r']))) == {} assert delete(r(), Path('r', 'r', T['r'])) == {} with pytest.raises(TypeError, match='path argument must be'): Delete(1, 'a') with pytest.raises(ValueError, match='path must have at least one element'): Delete(T, 1) assert repr(Delete(T.a)) == 'Delete(T.a)' # test delete from scope assert glom(1, (S(x=T), S['x'])) == 1 with pytest.raises(PathAccessError): glom(1, (S(x=T), Delete(S['x']), S['x'])) # test raising on missing parent with pytest.raises(PathAccessError): glom({}, Delete(T['a']['b'])) # test raising on missing index with pytest.raises(PathDeleteError): glom([], Delete(T[0])) target = [] assert glom(target, Delete(T[0], ignore_missing=True)) is target # test raising on missing attr with pytest.raises(PathDeleteError): glom(object(), Delete(T.does_not_exist)) target = object() assert glom(target, Delete(T.does_not_exist, ignore_missing=True)) is target
def test_s_scope_assign(): data = {'a': 1, 'b': [{'c': 2}, {'c': 3}]} output = [{'a': 1, 'c': 2}, {'a': 1, 'c': 3}] assert glom(data, (S(a='a'), ('b', [{'a': S['a'], 'c': 'c'}]))) == output assert glom(data, ('b', [{'a': S[ROOT][Val(T)]['a'], 'c': 'c'}])) == output with pytest.raises(TypeError): S('posarg') with pytest.raises(TypeError): S() assert glom([[1]], (S(v=Vars()), [[A.v.a]], S.v.a)) == 1 assert glom(1, (S(v=lambda t: {}), A.v['a'], S.v['a'])) == 1 with pytest.raises(GlomError): glom(1, (S(v=lambda t: 1), A.v.a)) class FailAssign(object): def __setattr__(self, name, val): raise Exception('nope') with pytest.raises(PathAssignError): glom(1, (S(v=lambda t: FailAssign()), Path(A.v, 'a'))) assert repr(S(a=T.a.b)) == 'S(a=T.a.b)' spec = (S(a='x'), S.a) assert glom({'x': 'y'}, spec) == 'y' return
def test_path_t_roundtrip(): # check that T repr roundrips assert repr(T['a'].b.c()) == "T['a'].b.c()" assert repr(T[1:]) == "T[1:]" assert repr(T[::3, 1:, 1:2, :2:3]) == "T[::3, 1:, 1:2, :2:3]" # check that Path repr roundtrips assert repr(Path('a', 1, 'b.b', -1.0)) == "Path('a', 1, 'b.b', -1.0)" # check that Path repr roundtrips when it contains Ts assert repr(Path(T['a'].b, 'c', T['d'].e)) == "Path(T['a'].b, 'c', T['d'].e)" # check that T instances containing Path access revert to repring with Path assert repr(Path(T['a'].b, 'c', T['d'].e).path_t) == "Path(T['a'].b, 'c', T['d'].e)" # check that Paths containing only T objects reduce to a T (joining the T objects) assert repr(Path(T['a'].b, T.c())) == "T['a'].b.c()" # check that multiple nested paths reduce assert repr(Path(Path(Path('a')))) == "Path('a')" # check builtin repr assert repr(T[len]) == 'T[len]' assert repr(T.func(len, sum)) == 'T.func(len, sum)'
def test_path_len(): assert len(Path()) == 0 assert len(Path('a', 'b', 'c')) == 3 assert len(Path.from_text('1.2.3.4')) == 4 assert len(Path(T)) == 0 assert len(Path(T.a.b.c)) == 3 assert len(Path(T.a()['b'].c.d)) == 5
def test_pae_api(): target = {'value': {'data': [0, 1, 2]}} with pytest.raises(GlomError) as exc_info: glom(target, (T['value'], 'data.3')) assert exc_info.value.path == Path('data', '3') assert exc_info.value.path.__class__ is Path assert exc_info.value.exc.__class__ is IndexError assert exc_info.value.part_idx == 1
def mw_get_target(next_, posargs_, target_file, target_format, spec_file, spec_format): spec_text, target_text = None, None if len(posargs_) == 2: spec_text, target_text = posargs_ elif len(posargs_) == 1: spec_text, target_text = posargs_[0], None if spec_text and spec_file: _error('expected spec file or spec argument, not both') elif spec_file: try: with open(spec_file, 'r') as f: spec_text = f.read() except IOError as ose: _error('could not read spec file %r, got: %s' % (spec_file, ose)) if not spec_text: spec = Path() elif spec_format == 'python': if spec_text[0] not in ('"', "'", "[", "{", "("): # intention: handle trivial path access, assume string spec_text = repr(spec_text) spec = ast.literal_eval(spec_text) elif spec_format == 'json': spec = json.loads(spec_text) else: _error('expected spec-format to be one of python or json') if target_text and target_file: _error('expected target file or target argument, not both') elif target_text == '-' or target_file == '-': with sys.stdin as f: target_text = f.read() elif target_file: try: target_text = open(target_file, 'r').read() except IOError as ose: _error('could not read target file %r, got: %s' % (target_file, ose)) elif not target_text and not os.isatty(sys.stdin.fileno()): with sys.stdin as f: target_text = f.read() if not target_text: target = {} elif target_format == 'json': try: target = json.loads(target_text) except Exception as e: _error('could not load target data, got: %s' % e) else: _error('expected spec-format to be one of python or json') return next_(spec=spec, target=target)
def set_data(obj: Dict, value: Union[Dict, List, str], *path: str) -> None: """ Update data from the object with given value. e.g: dependencies.cuda."11.3.1" """ try: _ = glom(obj, Assign(Path(*path), value)) except PathAssignError: log.exception("Exception occurred in " "update_data, unable to update " f"{Path(*path)} with {value}") exit(1)
def test_startswith(): ref = T.a.b[1] assert Path(ref).startswith(T) assert Path(ref).startswith(T.a.b) assert Path(ref).startswith(ref) assert Path(ref).startswith(ref.c) is False assert Path('a.b.c').startswith(Path()) assert Path('a.b.c').startswith('a.b.c') with raises(TypeError): assert Path('a.b.c').startswith(None) return
def test_path_slices(): path = Path(T.a.b, 1, 2, T(test='yes')) assert path[::] == path # positive indices assert path[3:] == Path(2, T(test='yes')) assert path[1:3] == Path(T.b, 1) assert path[:3] == Path(T.a.b, 1) # positive indices backwards assert path[2:1] == Path() # negative indices forward assert path[-1:] == Path(T(test='yes')) assert path[:-2] == Path(T.a.b, 1) assert path[-3:-1] == Path(1, 2) # negative indices backwards assert path[-1:-3] == Path() # slicing and stepping assert path[1::2] == Path(T.b, 2)
def get_data(obj: Union[Dict, MutableMapping], *path: str) -> Any: """ Get data from the object by dotted path. e.g: dependencies.cuda."11.3.1" """ try: data = glom(obj, Path(*path)) except PathAccessError: log.exception("Exception occurred in " "get_data, unable to retrieve " f"{'.'.join([*path])} from {repr(obj)}") exit(1) else: return data
def test_empty_path_access(): target = {} assert glom(target, Path()) is target assert glom(target, (Path(), Path(), Path())) is target dup_dict = glom(target, {'target': Path(), 'target2': Path()}) dup_dict['target'] is target dup_dict['target2'] is target
def test_path_len(): assert len(Path()) == 0 assert len(Path('a', 'b', 'c')) == 3 assert len(Path.from_text('1.2.3.4')) == 4 assert len(Path(T)) == 0 assert len(Path(T.a.b.c)) == 3 assert len(Path(T.a()['b'].c.d)) == 5
def mw_get_target(next_, posargs_, target_file, target_format, spec_file, spec_format): spec_text, target_text = None, None if len(posargs_) == 2: spec_text, target_text = posargs_ elif len(posargs_) == 1: spec_text, target_text = posargs_[0], None if spec_text and spec_file: raise UsageError('expected spec file or spec argument, not both') elif spec_file: try: with open(spec_file, 'r') as f: spec_text = f.read() except IOError as ose: raise UsageError('could not read spec file %r, got: %s' % (spec_file, ose)) if not spec_text: spec = Path() elif spec_format == 'python': if spec_text[0] not in ('"', "'", "[", "{", "("): # intention: handle trivial path access, assume string spec_text = repr(spec_text) spec = ast.literal_eval(spec_text) elif spec_format == 'json': spec = json.loads(spec_text) elif spec_format == 'python-full': spec = _eval_python_full_spec(spec_text) else: raise UsageError( 'expected spec-format to be one of json, python, or python-full') if target_text and target_file: raise UsageError('expected target file or target argument, not both') elif target_text == '-' or target_file == '-': target_text = sys.stdin.read() elif target_file: try: target_text = open(target_file, 'r').read() except IOError as ose: raise UsageError('could not read target file %r, got: %s' % (target_file, ose)) elif not target_text and not isatty(sys.stdin): target_text = sys.stdin.read() target = mw_handle_target(target_text, target_format) return next_(spec=spec, target=target)
def test_path_getitem(): path = Path(T.a.b.c) assert path[0] == Path(T.a) assert path[1] == Path(T.b) assert path[2] == Path(T.c) assert path[-1] == Path(T.c) assert path[-2] == Path(T.b) with raises(IndexError, match='Path index out of range'): path[4] with raises(IndexError, match='Path index out of range'): path[-14] return
def test_path_t_roundtrip(): # check that T repr roundrips assert repr(T['a'].b.c()) == "T['a'].b.c()" # check that Path repr roundtrips assert repr(Path('a', 1, 'b.b', -1.0)) == "Path('a', 1, 'b.b', -1.0)" # check that Path repr roundtrips when it contains Ts assert repr(Path(T['a'].b, 'c', T['d'].e)) == "Path(T['a'].b, 'c', T['d'].e)" # check that T instances containing Path access revert to repring with Path assert repr(Path(T['a'].b, 'c', T['d'].e).path_t) == "Path(T['a'].b, 'c', T['d'].e)" # check that Paths containing only T objects reduce to a T (joining the T objects) assert repr(Path(T['a'].b, T.c())) == "T['a'].b.c()" # check that multiple nested paths reduce assert repr(Path(Path(Path('a')))) == "Path('a')"
def test_vars(): assert glom(1, A.a) == 1 # A should not change the target assert glom(1, (A.a, S.a)) == 1 # check that tuple vars don't "leak" into parent tuple assert glom(1, (A.t, Val(2), A.t, S.t)) == 2 assert glom(1, (A.t, (Val(2), A.t), S.t)) == 1 let = S(v=Vars({'b': 2}, c=3)) assert glom(1, (let, A.v.a, S.v.a)) == 1 with pytest.raises(AttributeError): glom( 1, (let, S.v.a)) # check that Vars() inside a spec doesn't hold state assert glom(1, (let, Path(A, 'v', 'a'), S.v.a)) == 1 assert glom(1, (let, S.v.b)) == 2 assert glom(1, (let, S.v.c)) == 3 assert repr(let) == "S(v=Vars({'b': 2}, c=3))" assert repr(Vars(a=1, b=2)) in ("Vars(a=1, b=2)", "Vars(b=2, a=1)") assert repr(Vars(a=1, b=2).glomit(None, None)) in ("ScopeVars({'a': 1, 'b': 2})", "Vars({'b': 2, 'a': 1})") assert repr(A.b["c"]) == "A.b['c']"
def test_let(): # backwards compat 2020-07 data = {'a': 1, 'b': [{'c': 2}, {'c': 3}]} output = [{'a': 1, 'c': 2}, {'a': 1, 'c': 3}] assert glom(data, (Let(a='a'), ('b', [{'a': S['a'], 'c': 'c'}]))) == output assert glom(data, ('b', [{'a': S[ROOT][Val(T)]['a'], 'c': 'c'}])) == output with pytest.raises(TypeError): Let('posarg') with pytest.raises(TypeError): Let() assert glom([[1]], (Let(v=Vars()), [[A.v.a]], S.v.a)) == 1 assert glom(1, (Let(v=lambda t: {}), A.v['a'], S.v['a'])) == 1 with pytest.raises(GlomError): glom(1, (Let(v=lambda t: 1), A.v.a)) class FailAssign(object): def __setattr__(self, name, val): raise Exception('nope') with pytest.raises(PathAssignError): glom(1, (Let(v=lambda t: FailAssign()), Path(A.v, 'a'))) assert repr(Let(a=T.a.b)) == 'Let(a=T.a.b)'
def test_path_eq(): assert Path('a', 'b') == Path('a', 'b') assert Path('a') != Path('b') assert Path() != object()
def test_list_path_access(): assert glom(list(range(10)), Path(1)) == 1
def test_path(): _obj = object() target = {'a': {'b.b': [None, {_obj: [None, None, 'd']}]}} assert glom(target, Path('a', 'b.b', 1, _obj, -1)) == 'd'
def _format_vcap_path(self, path: str) -> Path: subpath = path.split(".") return Path(Path(self.vcap_service_prefix), *subpath)
def test_path_cache(): assert Path.from_text('a.b.c') is Path.from_text('a.b.c') pre = Path._MAX_CACHE Path._MAX_CACHE = 0 assert Path.from_text('d.e.f') is not Path.from_text('d.e.f')
def test_format_vcap_path(self, cfenv): path = cfenv._format_vcap_path("0.credentials.uri") assert path == Path("p-config-server", "0", "credentials", "uri")
def test_from_t_identity(): ref = Path(T.a.b) assert ref.from_t() == ref assert ref.from_t() is ref
def test_path_values(): path = Path(T.a.b, 1, 2, T(test='yes')) assert path.values() == ('a', 'b', 1, 2, ((), {'test': 'yes'})) assert Path().values() == ()
def test_path_values(): path = Path(T.a.b, 1, 2, T(test='yes')) assert path.values() == ('a', 'b', 1, 2, ((), {'test': 'yes'})) assert Path().values() == ()
def test_path_eq_t(): assert Path(T.a.b) == T.a.b assert Path(T.a.b.c) != T.a.b
def test_from_t_identity(): ref = Path(T.a.b) assert ref.from_t() == ref assert ref.from_t() is ref