def test_required(): sp = SchemaParser(shared={'a': {'x?int': 'x'}, 'b': {'y?int': 'y'}}) f = sp.parse({'$self@a@b': 'required'}) assert f({'x': 1, 'y': 2}) == {'x': 1, 'y': 2} with pytest.raises(Invalid) as exinfo: f(None) assert 'required' in exinfo.value.message
def test_merge_required(): sp = SchemaParser(shared={"a": {"x?int": "x"}, "b": {"y?int": "y"}}) f = sp.parse({"$self@a@b": "required"}) assert f({"x": 1, "y": 2}) == {"x": 1, "y": 2} with pytest.raises(Invalid) as exinfo: f(None) assert "required" in exinfo.value.message
def test_basic(schema, value): sp = SchemaParser(shared={ 'user_name': { 'name?str': 'name' }, 'user_age': { 'age?int': 'age' }, }) f = sp.parse(schema) assert f(value) == value
def test_error_position(value, expect): sp = SchemaParser(shared={ 'the_tags': ['int'], 'user': { 'userid?int': 'UserID' } }) f = sp.parse({'user@user': '******', 'tags@the_tags': 'Tags'}) with pytest.raises(Invalid) as exinfo: f(value) assert exinfo.value.position == expect
def test_optional(schema): sp = SchemaParser(shared={ 'user_name': { 'name?str': 'name' }, 'user_age': { 'age?int': 'age' }, }) f = sp.parse(schema) assert f(None) is None
def test_custom_validator(): @handle_default_optional_desc() def choice_validator(): def validator(value): if value in "ABCD": return value raise Invalid("invalid choice") return validator sp = SchemaParser(validators={"choice": choice_validator}) for value in "ABCD": assert sp.parse("choice")(value) == value assert sp.parse("choice&optional")(None) is None
def test_error_position(value, expect): sp = SchemaParser(shared={ 'user_name': { 'name?str': 'name' }, 'user_age': { 'age?int': 'age' }, }) f = sp.parse({'$self@user_name@user_age': 'User'}) with pytest.raises(Invalid) as exinfo: f(value) assert exinfo.value.position == expect
def test_custom_validator(): @validator(string=False) def choice_validator(value, *choices): try: if value in choices: return value except: pass raise Invalid('invalid choice') sp = SchemaParser(validators={'choice': choice_validator}) for value in 'ABCD': assert sp.parse('choice("A","B","C","D")')(value) == value assert sp.parse('choice&optional')(None) is None
def __init__(self, app, validators=None, metafile=None, docs=""): self.before_request_funcs = [] self.after_request_funcs = [] self.handle_error_func = None self.app = app if validators: self.validators = validators else: self.validators = {} if metafile is None: self.meta = {} else: with open(metafile) as f: self.meta = json.load(f) meta_api = parse_docs(docs, ["$shared", "$error"]) self.meta["$desc"] = meta_api.get("$desc", "") self.meta["$title"] = get_title(self.meta.get('$desc'), 'Document') self.meta["$shared"] = meta_api.get("$shared", OrderedDict()) self.meta["$error"] = BUILTIN_ERROR.copy() self.meta["$error"].update(meta_api.get("$error", {})) # check shared is valid or not if self.meta["$shared"]: with MarkKey("$shared"): SchemaParser(shared=self.meta["$shared"]) auth = DEFAULT_AUTH.copy() auth.update(self.meta.get("$auth", {})) self.meta["$auth"] = auth # TODO self.requires = {} for k, v in self.meta.get("$requires", {}).items(): self.requires[k] = Res(v) self._resjs_cache = None
def validr_simple_validate(): sp = SchemaParser() schema = { "user": {"userid?int(0,9)": "UserID"}, "tags": ["int&min=0"], "style": { "width?int": "width", "height?int": "height", "border-width?int": "border-width", "border-style?str": "border-style", "border-color?str": "border-color", "color?str": "Color" }, "unknown?str&optional": "unknown value" } return sp.parse(schema)
def test_custom_validator(): with pytest.warns(DeprecationWarning): @handle_default_optional_desc() def choice_validator(): def validator(value): if value in 'ABCD': return value raise Invalid('invalid choice') return validator sp = SchemaParser(validators={'choice': choice_validator}) for value in 'ABCD': assert sp.parse('choice')(value) == value assert sp.parse('choice&optional')(None) is None
def default(): sp = SchemaParser() schema = { 'user': {'userid?int(0,9)': 'UserID'}, 'tags': ['int&min=0'], 'style': { 'width?int': 'width', 'height?int': 'height', 'border_width?int': 'border_width', 'border_style?str': 'border_style', 'border_color?str': 'border_color', 'color?str': 'Color' }, 'optional?str&optional': 'unknown value' } return sp.parse(schema)
def add_resource(self, resource, *class_args, **class_kwargs): """ Add resource Parse resource and it's actions, route actions by naming rule. Args: resource: resource class class_args: class_args class_kwargs: class_kwargs """ name = resource.__name__.lower() meta_resource = parse_docs(resource.__doc__, ["$shared"]) self.meta[name] = meta_resource shared = self.meta["$shared"].copy() shared.update(meta_resource.get("$shared", {})) with MarkKey("%s.$shared" % resource.__name__): sp = SchemaParser(validators=self.validators, shared=shared) with MarkKey(resource.__name__): resource = resource(*class_args, **class_kwargs) # group actions by it's name, and # make action group a view function actions = defaultdict(lambda: {}) for action in dir(resource): find = PATTERN_ACTION.findall(action) if not find: continue httpmethod, action_name = find[0] action_group = actions[action_name] fn = getattr(resource, action) meta_action = parse_docs( fn.__doc__, ["$input", "$output", "$error"]) meta_resource[action] = meta_action with MarkKey(fn.__name__): action_group[httpmethod] = \ self.make_action(fn, sp, meta_action) for action_name in actions: if action_name == "": url = "/" + name endpoint = name else: url = "/{0}/{1}".format(name, action_name) endpoint = "{0}@{1}".format(name, action_name) action_group = actions[action_name] self.app.add_url_rule( url, endpoint=endpoint, view_func=self.make_view(action_group), methods=set(action_group) )
def validr_validate(): shared = { "size": { "width?int": "width", "height?int": "height" }, "border": { "border-width?int": "border-width", "border-style?str": "border-style", "border-color?str": "border-color" }, "user": {"userid?int(0,9)": "UserID"}, } sp = SchemaParser(shared=shared) schema = { "user@user": "******", "tags": ["int&min=0"], "style": { "$self@size@border": "style", "color?str": "Color" }, "unknown?str&optional": "unknown value" } return sp.parse(schema)
def use_refer_merge(): shared = { 'size': { 'width?int': 'width', 'height?int': 'height' }, 'border': { 'border_width?int': 'border_width', 'border_style?str': 'border_style', 'border_color?str': 'border_color' }, 'user': {'userid?int(0,9)': 'UserID'}, } sp = SchemaParser(shared=shared) schema = { 'user@user': '******', 'tags': ['int&min=0'], 'style': { '$self@size@border': 'style', 'color?str': 'Color' }, 'optional?str&optional': 'unknown value' } return sp.parse(schema)
def __init__(self, *args, validators=None, directives=None, **kwargs): super().__init__(*args, **kwargs) self.config['JSON_AS_ASCII'] = False self._directives = { 'input': input_directive, 'output': output_directive } if directives: self._directives.update(directives) self._resources = {} self._desc, self._shared = parse_shared( __import__(self.import_name).__doc__) self.schema_parser = SchemaParser(validators=validators, shared=self._shared) self.route('/')(self._doc)
def test_ordered(): """shared should keep ordered""" shared = OrderedDict([ ('user_id', 'int'), ('user', { 'user_id@user_id': 'desc' }), ('group', { 'user@user': '******' }), ('team', { 'group@group': 'desc' }), ]) for i in range(100): SchemaParser(shared=shared)
def test_optional(schema, value): sp = SchemaParser(shared={'userid': 'int'}) f = sp.parse(schema) assert f(value) == value
import pytest from validr import Invalid, SchemaError, SchemaParser sp = SchemaParser() def test_basic(): f = sp.parse('int(0,9)') assert f(3) == 3 with pytest.raises(Invalid): f(-1) def test_optional_int(): f = sp.parse('int&optional') assert f(None) is None with pytest.raises(Invalid): f('') def test_optional_str(): f = sp.parse('str&optional') assert f(None) == '' assert f('') == '' @pytest.mark.parametrize('schema', ['int&default=5', 'int&default=5&optional']) def test_default_int(schema): f = sp.parse(schema) assert f(None) == 5 with pytest.raises(Invalid):
def test_merge_non_dict_value_error(): sp = SchemaParser(shared={"a": "int", "b": "str"}) f = sp.parse({"key": {"$self@a@b": "invalid mixins"}}) with pytest.raises(SchemaError) as exinfo: f({"key": "123"}) assert exinfo.value.position == "key"
def test_shared_not_found(): sp = SchemaParser(shared={'user': {'userid?int': 'userid'}}) with pytest.raises(SchemaError): sp.parse({'$self@unknown@user': '******'})
def test_merge_non_dict_value_error(): sp = SchemaParser(shared={'a': 'int', 'b': {'k?str': 'v'}}) with pytest.raises(SchemaError) as exinfo: sp.parse({'key': {'$self@a@b': 'invalid merges'}}) assert exinfo.value.position == 'key' assert '@a' in exinfo.value.message
def test_refer_dict(schema, value): sp = SchemaParser(shared={'user': {'userid?int': 'UserID'}}) f = sp.parse(schema) assert f(value) == value
def test_refer_list(value, expect): sp = SchemaParser(shared={"user": {"userid?int(0,9)": "UserID"}}) f = sp.parse(["@user"]) assert f(value) == expect
def test_list_refer(value, expect): sp = SchemaParser(shared={"numbers": ["(1,3)&unique", "int(0,9)"]}) f = sp.parse("@numbers") assert f(value) == expect
def test_multi_self_described_error(schema): sp = SchemaParser(shared={"user": {"userid?int": "desc"}}) with pytest.raises(SchemaError): sp.parse(schema)
def test_refer_scalar(value, expect): sp = SchemaParser(shared={"userid": "int(0,9)"}) f = sp.parse({"userid@userid": "UserID"}) assert f(value) == expect
def test_mixins(schema, value, expect): sp = SchemaParser(shared={"user1": {"userid?int": "userid"}, "user2": {"name?str": "name", "age?int": "age"}}) assert sp.parse(schema)(value) == expect
def test_shared_not_found(): sp = SchemaParser() with pytest.raises(SchemaError): sp.parse({'user@user': '******'})
def test_optional_refer(schema, value, expect): sp = SchemaParser(shared={"user": {"userid?int": "userid"}}) assert sp.parse(schema)(value) == expect
def test_refer_scalar(schema, value): sp = SchemaParser(shared={'userid': 'int'}) f = sp.parse(schema) assert f(value) == value
def test_multi_refer_error(schema): sp = SchemaParser(shared={"number": "int", "text": "str"}) with pytest.raises(SchemaError): sp.parse(schema)
def test_refer_list(schema, value): sp = SchemaParser(shared={'tags': ['int']}) f = sp.parse(schema) assert f(value) == value
def test_list_refer_fail(value): sp = SchemaParser(shared={"numbers": ["(1,3)&unique", "int(0,9)"]}) f = sp.parse("@numbers") with pytest.raises(Invalid): f(value)
def test_multi_refer_error(schema): sp = SchemaParser(shared={'number': 'int', 'text': 'str'}) with pytest.raises(SchemaError): sp.parse(schema)
def test_error_position(): shared = {'name': [{'key?unknown': 'value'}]} with pytest.raises(SchemaError) as exinfo: SchemaParser(shared=shared) assert exinfo.value.position == 'name[].key'
def test_refer_dict(value, expect): sp = SchemaParser(shared={"user": {"userid?int(0,9)": "UserID"}}) f = sp.parse({"group@user": "******"}) value = {"group": value} expect = {"group": expect} assert f(value) == expect
def test_mixin_shared_not_found(): sp = SchemaParser(shared={"user1": {"userid?int": "userid"}}) with pytest.raises(SchemaError): sp.parse({"$self@user2@user1": "desc"})