示例#1
0
def test_midway_branch():
    # midway branch, but then continues
    actual = _make_stack(Match(Switch([(1, 1), ('a', 'a'), ([None], T.a)])))
    expected = """\
Traceback (most recent call last):
  File "test_error.py", line ___, in _make_stack
    glom(target, spec)
  File "core.py", line ___, 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], T.a)]))
 + 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'")
"""
    if _PY2:  # see https://github.com/pytest-dev/pytest/issues/1347
        assert len(actual.split("\n")) == len(expected.split("\n"))
    else:
        assert actual == expected
    # branch and another branch
    actual = _make_stack(
        Match(
            Switch([(1, 1), ('a', 'a'),
                    ([None], Switch([(1, 1), ('a', 'a'), ([None], T.a)]))])))
    expected = """\
Traceback (most recent call last):
  File "test_error.py", line ___, in _make_stack
    glom(target, spec)
  File "core.py", line ___, 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'")
"""
    if _PY2:  # see https://github.com/pytest-dev/pytest/issues/1347
        assert len(actual.split("\n")) == len(expected.split("\n"))
    else:
        assert actual == expected
示例#2
0
def test_nested_struct():
    """adapted from use case"""
    import json

    def _json(spec):
        return Auto((json.loads, _str_json, Match(spec)))

    _str_json = Ref(
        'json',
        Match(
            Or(And(dict, {Ref('json'): Ref('json')}), And(list, [Ref('json')]),
               And(type(u''), Auto(str)), object)))

    rule_spec = Match({
        'rule_id':
        Or('', Regex(r'\d+')),
        'rule_name':
        str,
        'effect':
        Or('approve', 'custom_approvers'),
        'rule_approvers':
        _json([{
            'pk': int,
            'level': int
        }]),
        'rule_data':
        _json([  # list of condition-objects
            {
                Optional('value', 'null'):
                _json(Or(None, int, float, str, [int, float, str])),
                'field':
                Auto(int),  # id of row from FilterField
                'operator':
                str,  # corresponds to FilterOperator.display_name
            }
        ]),
        Optional('save_as_new', False):
        Or(str, bool),
    })

    rule = dict(rule_id='1',
                rule_name='test rule',
                effect='approve',
                rule_approvers=json.dumps([{
                    'pk': 2,
                    'level': 1
                }]),
                rule_data=json.dumps([{
                    'value': json.dumps([1, 2]),
                    'field': 2,
                    'operator': '>'
                }, {
                    'field': 2,
                    'operator': '=='
                }]))

    glom(rule, rule_spec)
    rule['save_as_new'] = 'true'
    glom(rule, rule_spec)
示例#3
0
def test_cruddy_json():
    _chk(Match({'int_id?': Auto((int, (M > 0)))}), {'int_id?': '1'},
         {'int_id?': '-1'})
    # embed a build
    squished_json = Match({
        'smooshed_json':
        Auto((json.loads, Match({'sub': Auto((json.loads, M == 1))})))
    })
    glom({'smooshed_json': json.dumps({'sub': json.dumps(1)})}, squished_json)
示例#4
0
def test_sample():
    """
    test meant to cover a more realistic use
    """
    import datetime

    data = {
        'name': 'item',
        'date_added': datetime.datetime.now(),
        'desc': 'a data item',
        'tags': ['data', 'new'],
    }

    spec = Match({
        'name': str,
        Optional('date_added'): datetime.datetime,
        'desc': str,
        'tags': [str]
    })

    def good():
        glom(data, spec)

    def bad():
        with pytest.raises(MatchError):
            glom(data, spec)

    good()  # should match
    del data['date_added']
    good()  # should still match w/out optional
    del data['desc']
    bad()
    data['desc'] = 'a data item'
    data['extra'] = 'will fail on extra'
    bad()
    spec.spec[str] = str  # now extra str-key/str-val are okay
    good()
    data['extra2'] = 2  # but extra str-key/non-str-val are bad
    bad()
    # reset data
    data = {
        'name': 'item',
        'date_added': datetime.datetime.now(),
        'desc': 'a data item',
        'tags': ['data', 'new'],
    }
    del spec.spec[str]
    spec.spec[Required(str)] = str  # now there MUST be at least one str
    bad()
    data['extra'] = 'extra'
    good()
示例#5
0
def test_pattern_matching():
    pattern_matcher = Or(And(Match(1), Val('one')), And(Match(2), Val('two')),
                         And(Match(float), Val('float')))
    assert glom(1, pattern_matcher) == 'one'
    assert glom(1.1, pattern_matcher) == 'float'

    # obligatory fibonacci

    fib = (M > 2) & (lambda n: glom(n - 1, fib) + glom(n - 2, fib)) | T

    assert glom(5, fib) == 8

    factorial = (lambda t: t + 1,
                 Ref('fact', (lambda t: t - 1, (M == 0) & Fill(1) |
                              (S(r=Ref('fact')), S, lambda s: s['r'] * s[T]))))

    assert glom(4, factorial) == 4 * 3 * 2
示例#6
0
def test_json_ref():
    assert glom(
        {'a': {'b': [0, 1]}},
        Ref('json',
            Match(Or(
                And(dict, {Ref('json'): Ref('json')}),
                And(list, [Ref('json')]),
                And(0, Val(None)),
                object)))) == {'a': {'b': [None, 1]}}
示例#7
0
def test_precedence():
    """test corner cases of dict key precedence"""
    glom({(0, 1): 3},
        Match({
            (0, 1): Val(1),  # this should match
            (0, int): Val(2),  # optional
            (0, M == 1): Val(3),  # optional
        })
    )
    with pytest.raises(ValueError):
        Optional(int)  # int is already optional so not valid to wrap
示例#8
0
def test_check_ported_tests():
    """
    Tests ported from Check() to make sure all the functionality has an analogue.
    """
    target = [{'id': 0}, {'id': 1}, {'id': 2}]

    # check that skipping non-passing values works
    assert glom(target, [Coalesce(M(T['id']) == 0, default=SKIP)]) == [{'id': 0}]

    # TODO: should M(subspec, default='') work? I lean no.
    # NB: this is not a very idiomatic use of Match, just brought over for Check reasons
    assert glom(target, [Match({'id': And(int, M == 1)}, default=SKIP)]) == [{'id': 1}]
    assert glom(target, [Match({'id': And(int, M <= 1)}, default=STOP)]) == [{'id': 0}, {'id': 1}]

    # check that stopping chain execution on non-passing values works
    spec = (Or(Match(len), Val(STOP)), T[0])
    assert glom('hello', spec, glom_debug=True) == 'h'
    assert glom('', spec) == ''  # would fail with IndexError if STOP didn't work

    target = [1, u'a']
    assert glom(target, [Match(unicode, default=SKIP)]) == ['a']
    assert glom(target, Match([Or(unicode, int)])) == [1, 'a']

    target = ['1']
    assert glom(target, [(M(T), int)]) == [1]
    assert glom(target, M(T)) == ['1']

    failing_checks = [({'a': {'b': 1}}, {'a': ('a', 'b', Match(str))},
                       '''expected type str, not int'''),  # TODO: bbrepr at least, maybe include path like Check did
                      ({'a': {'b': 1}}, {'a': ('a', Match({'b': str}))},
                       '''expected type str, not int'''),  # TODO: include subspec path ('b')
                      (1, Match(Or(unicode, bool))),
                      (1, Match(unicode)),
                      (1, Match(0)),
                      (1, Match(Or(0, 2))),
                      ('-3.14', Match(lambda x: int(x) > 0)),
                      # ('-3.14', M(lambda x: int(x) > 0)),
                      # TODO: M doesn't behave quite like Match because it's mode-free
    ]

    for fc in failing_checks:
        if len(fc) == 2:
            target, check = fc
            msg = None
        else:
            target, check, msg = fc

        with pytest.raises(MatchError) as exc_info:
            glom(target, check)

        if msg is not None:
            actual_msg = str(exc_info.value)
            assert actual_msg.find(msg) != -1
        assert repr(exc_info.value)

    return
示例#9
0
 def nullable_list_of(*items):
     return default_if_none(Match(list(items)), list)
示例#10
0
 def in_range(sub_schema, _min, _max):
     'check that sub_schema is between _min and _max'
     return Match(And(sub_schema, _min < M, M < _max))
示例#11
0
 def none_or(sub_schema):
     'allow None or sub_schema'
     return Match(Or(None, sub_schema))
示例#12
0
def test_sets():
    with pytest.raises(MatchError):
        glom({1}, Match({}))
    with pytest.raises(MatchError):
        glom(frozenset([1]), Match(frozenset()))
示例#13
0
def test_match_default():
    default = []
    res = glom(None, Match(list, default=default))
    assert res is default
示例#14
0
 def _json(spec):
     return Auto((json.loads, _str_json, Match(spec)))
示例#15
0
def test_nested_dict():
    assert glom({1: 2}, Match({A.t: S.t})) == {1: 1}
示例#16
0
def test_defaults():
    assert glom(1, Match(2, default=3)) == 3
    assert glom(1, Or(M == 2, default=3)) == 3
    assert glom(1, And(M == 2, default=3)) == 3
示例#17
0
def test_basic():
    _chk(Match(1), 1, 2)
    _chk(Match(int), 1, 1.0)
    # test unordered sequence comparisons
    _chk(Match([int]), [1], ["1"])
    _chk(Match({int}), {1}, [1])
    _chk(Match(frozenset({float})), frozenset({}), frozenset({"1"}))
    _chk(Match(len), [1], [])
    with pytest.raises(MatchError):
        glom(None, Match(len))
    with pytest.raises(MatchError):
        glom([1], Match([]))  # empty shouldn't match
    glom({"a": 1, "b": 2}, Match({str: int}))
    glom(2, M == 2)
    glom(int, M == int)
    glom(1.0, M > 0)
    glom(1.0, M >= 1)
    glom(1.0, M < 2)
    glom(1.0, M <= 1)
    glom(1.0, M != None)
    glom(1.0, (M > 0) & float)
    glom(1.0, (M > 100) | float)

    assert Match(('a', 'b')).matches(('a', 'b', 'c')) is False

    # test idiom for enum
    with pytest.raises(MatchError):
        glom("c", Match("a"))
    glom("c", Not(Match("a")))
    with pytest.raises(MatchError):
        glom("c", Match(Or("a", "b")))

    with pytest.raises(ValueError):
        And()

    with pytest.raises(TypeError):
        And('a', bad_kwarg=True)

    with pytest.raises(ValueError):
        Or()

    with pytest.raises(TypeError):
        Or('a', bad_kwarg=True)


    _chk(Match(Or("a", "b")), "a", "c")
    glom({None: 1}, Match({object: object}))
    _chk(Match((int, str)), (1, "cat"), (1, 2))
    with pytest.raises(MatchError):
        glom({1: 2}, Match({(): int}))
    with pytest.raises(MatchError):
        glom(1, Match({}))
    Match(M > 0).verify(1.0)

    assert Match(M).matches(False) is False
    assert Match(M).matches(True) is True
示例#18
0
def test_all_public_errors():
    """test that all errors importable from the top-level glom module
    pass a basic set of standards.

    When adding a new public error type, this test will be fail unless
    that type is also tested below.
    """
    import glom
    import copy

    err_types = [
        t for t in [getattr(glom, name) for name in dir(glom)]
        if isinstance(t, type) and issubclass(t, Exception)
    ]
    non_glomerrors = [
        t for t in err_types if not issubclass(t, glom.GlomError)
    ]
    assert not non_glomerrors, "expected all public exception types to subclass GlomError"

    err_types = sorted([t for t in err_types if not t is glom.GlomError],
                       key=lambda t: t.__name__)

    results = []

    def _test_exc(exc_type, target, spec):
        with pytest.raises(exc_type) as exc_info:
            glom.glom(target, spec)
        results.append((target, spec, exc_info.value))
        return exc_info.value

    _test_exc(glom.CheckError, {}, glom.Check(equal_to=[]))

    _test_exc(glom.FoldError, 2, glom.Flatten())

    _test_exc(glom.BadSpec, range(5), glom.grouping.Group([{T: T}]))

    _test_exc(glom.PathAccessError, {}, 'a.b.c')

    _test_exc(glom.UnregisteredTarget, 'kurt', [glom.T])

    _test_exc(glom.CoalesceError, {}, glom.Coalesce('a', 'b'))

    _test_exc(glom.PathAssignError, object(), glom.Assign('a', 'b'))

    _test_exc(glom.PathDeleteError, object(), glom.Delete('a'))

    _test_exc(MatchError, 1, M == 2)

    _test_exc(TypeMatchError, 1, Match(str))

    for (target, spec, exc) in results:
        assert copy.copy(exc) is not exc
        exc_str = str(exc)
        exc_type_name = exc.__class__.__name__
        assert exc_type_name in exc_str
        assert bbrepr(exc).startswith(exc_type_name)

        assert bbrepr(target)[:80] in exc_str
        assert bbrepr(spec)[:80] in exc_str

    tested_types = [type(exc) for _, _, exc in results]
    untested_types = set(err_types) - set(tested_types)

    assert not untested_types, "did not test all public exception types"
示例#19
0
def test_ternary():
    assert glom('abc', Match(Or(None, 'abc'))) == 'abc'