Skip to content

Instantly share code, notes, and snippets.

@feisuzhu
Created September 29, 2019 07:39
Show Gist options
  • Save feisuzhu/39c07a3d1fb3ca9160a2e3e939922b4e to your computer and use it in GitHub Desktop.
Save feisuzhu/39c07a3d1fb3ca9160a2e3e939922b4e to your computer and use it in GitHub Desktop.
JSON check
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals
# -- stdlib --
import types
import re
import arrow
# -- third party --
# -- own --
# -- code --
def instantiate(f):
return f()
def select_keys(d, keys):
return {k: d.get(k) for k in keys}
class CheckTypeFailed(Exception):
def __init__(self):
Exception.__init__(self)
self.path = []
def path_string(self):
return ''.join([
self._path_fragment(v)
for v in self.path
])
def _path_fragment(self, v):
if isinstance(v, int):
return '[%s]' % v
elif isinstance(v, str):
return '.%s' % v
def finalize(self):
self.args = (self.path_string(), )
def _check(cond):
if not cond:
raise CheckTypeFailed
def _check_isinstance(obj, cls):
try:
_check(isinstance(obj, cls))
except TypeError as e:
raise CheckTypeFailed from e
_check_key_not_exists = object()
def check_type_exc(pattern, obj, path=None):
try:
if isinstance(pattern, (list, tuple)):
_check_isinstance(obj, (list, tuple))
if len(pattern) == 2 and pattern[-1] is ...:
cls = pattern[0]
for i, v in enumerate(obj):
check_type_exc(cls, v, i)
else:
_check(len(pattern) == len(obj))
for i, (cls, v) in enumerate(zip(pattern, obj)):
check_type_exc(cls, v, i)
elif isinstance(pattern, dict):
_check_isinstance(obj, dict)
if ... in pattern:
pattern = dict(pattern)
match = pattern.pop(...)
else:
match = '!'
if match in set('?!='):
lkeys = set(pattern.keys())
rkeys = set(obj.keys())
if match == '!':
iterkeys = lkeys
elif match == '?':
iterkeys = lkeys & rkeys
elif match == '=':
_check(lkeys == rkeys)
iterkeys = lkeys
else:
assert False, 'WTF?!'
for k in iterkeys:
check_type_exc(pattern[k], obj.get(k, _check_key_not_exists), k)
elif match is ...:
assert len(pattern) == 1, 'Invalid dict pattern'
kt, vt = list(pattern.items())[0]
for k in obj:
check_type_exc(kt, k, '<%s>' % kt.__name__)
check_type_exc(vt, obj[k], k)
else:
assert False, 'Invalid dict match type'
else:
if issubclass(type(pattern), types.FunctionType):
try:
_check(pattern(obj))
except Exception as e:
raise CheckTypeFailed from e
elif issubclass(type(pattern), (int, str, bytes, tuple)):
_check(obj == pattern)
else:
_check_isinstance(obj, pattern)
except CheckTypeFailed as e:
if path is not None:
e.path.insert(0, path)
else:
e.finalize()
raise
def check_type(pattern, obj):
try:
check_type_exc(pattern, obj)
return None
except CheckTypeFailed as e:
return e.path_string()
class validators(object):
def phone(v):
return v.isdigit() and re.match(r'^1[3-9][0-9]{9}$', v)
def gender(v):
return v in ('male', 'female')
def datetime(v):
return bool(arrow.get(v))
@instantiate
class length(object):
def __getitem__(self, sl):
def _length_checker(v):
if sl.start is not None and not len(v) >= sl.start:
return False
if sl.stop is not None and not len(v) <= sl.stop:
return False
return True
return _length_checker
def maybe(cls):
def _maybe_checker(obj):
if obj in (_check_key_not_exists, None):
return True
return isinstance(obj, cls)
return _maybe_checker
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals
# -- stdlib --
# -- third party --
from nose.tools import eq_
# -- own --
# -- code --
class TestUtilsMisc(object):
def test_check_type(self):
from utils.misc import check_type
eq_(check_type(int, 1), None)
eq_(check_type(str, 'asdf'), None)
eq_(check_type([int, str], [1, 'foo']), None)
eq_(check_type([int, ...], [1, 2, 3, 4]), None)
eq_(check_type([[int, str], ...], [[1, 'foo'], [2, 'bar']]), None)
eq_(check_type([[int, lambda v: v == 1], ...], [[1, 1], [2, 1]]), None)
eq_(check_type({'foo': int, 'bar': str, ...: '='}, {'foo': 1, 'bar': 'asdf'}), None)
eq_(check_type({'foo': int, 'bar': str}, {'foo': 1, 'bar': 'asdf', 'baz': 1}), None)
eq_(check_type({'foo': int, 'bar': str, ...: '?'}, {'foo': 1, 'baz': 1}), None)
eq_(check_type({str: int, ...: ...}, {'foo': 1, 'baz': 1}), None)
eq_(check_type({'foo': 1, 'baz': 2}, {'foo': 1, 'baz': 2}), None)
eq_(check_type(int, 'asdf'), '')
eq_(check_type(str, 1), '')
eq_(check_type([int, str], ['foo', 1]), '[0]')
eq_(check_type([int, ...], [1, 2, None, 4]), '[2]')
eq_(check_type([[int, str], ...], [[1, 'foo'], [2, None]]), '[1][1]')
eq_(check_type([[int, lambda v: v == 1], ...], [[1, 2], [2, 1]]), '[0][1]')
eq_(check_type({'foo': int, 'bar': str, ...: '='}, {'foo': 1, 'bar': 1}), '.bar')
eq_(check_type({'foo': int, 'bar': str}, {'foo': 1, 'baz': 1}), '.bar')
eq_(check_type({'foo': int, 'bar': str, ...: '?'}, {'foo': None, 'baz': 1}), '.foo')
eq_(check_type({str: int, ...: ...}, {'foo': 1, 'baz': 'meh'}), '.baz') # UGC! DANGER!
eq_(check_type({str: int, ...: ...}, {'foo': 1, 2: 1}), '.<str>')
eq_(check_type({'foo': 1, 'baz': 1}, {'foo': 1, 'baz': 2}), '.baz')
eq_(check_type({'foo': lambda v: 1 / 0}, {'foo': 1}), '.foo')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment