vtjson


Namevtjson JSON
Version 1.4.2 PyPI version JSON
download
home_page
SummaryA lightweight package for validating JSON like Python objects
upload_time2024-01-28 17:49:31
maintainer
docs_urlNone
author
requires_python>=3.7
license
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # vtjson
A lightweight package for validating JSON like Python objects.

## Schemas

Validation of JSON like Python objects is done according to a `schema` which is somewhat inspired by a typescript type. The format of a schema is more or less self explanatory as the following example shows.

### Example

Below is the schema of the run object in the mongodb database underlying the Fishtest web application https://tests.stockfishchess.org/tests

```python
import math
from datetime import datetime
from bson.objectid import ObjectId
from vtjson import ip_address, number, regex, url

net_name = regex("nn-[a-z0-9]{12}.nnue", name="net_name")
tc = regex(r"([1-9]\d*/)?\d+(\.\d+)?(\+\d+(\.\d+)?)?", name="tc")
str_int = regex(r"[1-9]\d*", name="str_int")
sha = regex(r"[a-f0-9]{40}", name="sha")
country_code = regex(r"[A-Z][A-Z]", name="country_code")
run_id = regex(r"[a-f0-9]{24}", name="run_id")

worker_info_schema = {
    "uname": str,
    "architecture": [str, str],
    "concurrency": int,
    "max_memory": int,
    "min_threads": int,
    "username": str,
    "version": int,
    "python_version": [int, int, int],
    "gcc_version": [int, int, int],
    "compiler": {"clang++", "g++"},
    "unique_key": str,
    "modified": bool,
    "ARCH": str,
    "nps": number,
    "near_github_api_limit": bool,
    "remote_addr": ip_address,
    "country_code": {country_code, "?"},
}

results_schema = {
    "wins": int,
    "losses": int,
    "draws": int,
    "crashes": int,
    "time_losses": int,
    "pentanomial": [int, int, int, int, int],
}

schema = {
    "_id?": ObjectId,
    "start_time": datetime,
    "last_updated": datetime,
    "tc_base": number,
    "base_same_as_master": bool,
    "results_stale?": bool,
    "rescheduled_from?": run_id,
    "approved": bool,
    "approver": str,
    "finished": bool,
    "deleted": bool,
    "failed": bool,
    "is_green": bool,
    "is_yellow": bool,
    "workers?": int,
    "cores?": int,
    "results": results_schema,
    "results_info?": {
        "style": str,
        "info": [str, ...],
    },
    "args": {
        "base_tag": str,
        "new_tag": str,
        "base_net": net_name,
        "new_net": net_name,
        "num_games": int,
        "tc": tc,
        "new_tc": tc,
        "book": str,
        "book_depth": str_int,
        "threads": int,
        "resolved_base": sha,
        "resolved_new": sha,
        "msg_base": str,
        "msg_new": str,
        "base_options": str,
        "new_options": str,
        "info": str,
        "base_signature": str_int,
        "new_signature": str_int,
        "username": str,
        "tests_repo": url,
        "auto_purge": bool,
        "throughput": number,
        "itp": number,
        "priority": number,
        "adjudication": bool,
        "sprt?": {
            "alpha": 0.05,
            "beta": 0.05,
            "elo0": number,
            "elo1": number,
            "elo_model": "normalized",
            "state": {"", "accepted", "rejected"},
            "llr": number,
            "batch_size": int,
            "lower_bound": -math.log(19),
            "upper_bound": math.log(19),
            "lost_samples?": int,
            "illegal_update?": int,
            "overshoot?": {
                "last_update": int,
                "skipped_updates": int,
                "ref0": number,
                "m0": number,
                "sq0": number,
                "ref1": number,
                "m1": number,
                "sq1": number,
            },
        },
        "spsa?": {
            "A": number,
            "alpha": number,
            "gamma": number,
            "raw_params": str,
            "iter": int,
            "num_iter": int,
            "params": [
                {
                    "name": str,
                    "start": number,
                    "min": number,
                    "max": number,
                    "c_end": number,
                    "r_end": number,
                    "c": number,
                    "a_end": number,
                    "a": number,
                    "theta": number,
                },
                ...,
            ],
            "param_history?": [
                [{"theta": number, "R": number, "c": number}, ...],
                ...,
            ],
        },
    },
    "tasks": [
        {
            "num_games": int,
            "active": bool,
            "last_updated": datetime,
            "start": int,
            "residual?": number,
            "residual_color?": str,
            "bad?": True,
            "stats": results_schema,
            "worker_info": worker_info_schema,
        },
        ...,
    ],
    "bad_tasks?": [
        {
            "num_games": int,
            "active": False,
            "last_updated": datetime,
            "start": int,
            "residual": number,
            "residual_color": str,
            "bad": True,
            "task_id": int,
            "stats": results_schema,
            "worker_info": worker_info_schema,
        },
        ...,
    ],
}
```
## Conventions
- As in typescript, a (string) key ending in `?` represents an optional key. The corresponding schema (the item the key points to) will only be used for validation when the key is present in the object that should be validated. A key can also be made optional by wrapping it as `optional_key(key)`.
- If in a list/tuple the last entry is `...` (ellipsis) it means that the next to last entry will be repeated zero or more times. In this way generic types can be created. For example the schema `[str, ...]` represents a list of strings.
## Usage
To validate an object against a schema one can simply do
```python
validate(schema, object)
```  
If the validation fails this will throw a `ValidationError` and the exception contains an explanation about what went wrong. The full signature of `validate` is
```python
validate(schema, object, name="object", strict=True)
```
- The optional `name` argument is used to refer to the object being validated in the returned message.
- The optional argument `strict` indicates whether or not the object being validated is allowed to have keys/entries which are not in the schema.
## Wrappers
A wrapper takes one or more schemas as arguments and produces a new schema. **Warning:** wrappers should be considered as being immutable. In other words: if the underlying schemas change after a wrapper has been created then the effect of validating against the wrapper is undefined. If you _really_ need this then you should recreate the wrapper.
- An object matches the schema `union(schema1, ..., schemaN)` if it matches one of the schemas `schema1, ..., schemaN`. This is almost the same as `{schema1, ..., schemaN}`, or equivalently `set((schema1, ..., schemaN))` if `schema1, ..., schemaN` are hashable.
- An object matches the schema `intersect(schema1, ..., schemaN)` if it matches all the schemas `schema1, ..., schemaN`.
- An object matches the schema `complement(schema)` if it does not match `schema`.
- An object matches the schema `lax(schema)` if it matches `schema` when validated with `strict=False`.
- An object matches the schema `strict(schema)` if it matches `schema` when validated with `strict=True`.
- An object matches the schema `set_name(schema, name)` if it matches `schema`. But the `name` argument will be used in non-validation messages.
- An object matches the schema `compile(schema)` if it matches `schema`. `vtjson` compiles the schema before performing a validation so pre-compiling is not necessary but, in some cases, it may gain a bit of performance. 
- An object matches the schema `quote(schema)` if it is equal to `schema`. For example the schema `{"cats", "dogs"}` matches the strings `"cats"` and `"dogs"` but the schema `quote({"cats", "dogs"})` matches the set `{"cats", "dogs"}`. 
## Built-ins
Some built-ins take arguments. If no arguments are given then the parentheses can be omitted. So `email` is equivalent to `email()`.
- `regex(pattern, name=None, fullmatch=True, flags=0)`. This matches the strings which match the given pattern. The optional `name` argument may be used to give the regular expression a descriptive name. By default the entire string is matched, but this can be overruled via the `fullmatch` argument. The `flags` argument has the usual meaning.
- `interval(lowerbound, upperbound)`. This checks if `lowerbound <= object <= upperbound`, provided the comparisons make sense. An upper/lowerbound `...` (ellipsis) means that the
corresponding inequality is not checked.
- `number`. Matches `int` and `float`.
- `email`. Checks if the object is a valid email address. This uses the package `email_validator`. The `email` schema accepts the same options as `validate_email` in loc. cit.
- `ip_address` and `url`. These are similar to `email`.
- `domain_name(ascii_only=True, resolve=False)`. Checks if the object is a valid domain name. If `ascii_only=False` then allow IDNA domain names. If `resolve=True` check if the domain name resolves.
- `date_time(format=None)`. Without argument this represents an ISO 8601 date-time. The `format` argument represents a format string for `strftime`.
- `date` and `time`. These represent an ISO 8601 date and an ISO 8601 time.
## Format
A schema can be, in order of precedence:
- A class with the following properties:
  - it has a no-argument constructor;
  - the instances have a `__validate__` method with signature
  ```python
  __validate__(self, object, name, strict)
  ```
  - The parameters of `__validate__()` have the same semantics as those of `validate()`. The return value of `__validate__()` should be the empty string if validation succeeds, and otherwise it should be an explanation about what went wrong.
- An object having a `__validate__` attribute with signature
  ```python
  __validate__(object, name, strict)
  ```
  as above. This is for example how the wrapper schemas are implemented internally.
- A Python type. In that case validation is done by checking membership.
- A callable. Validation is done by applying the callable to the object. If applying the callable throws an exception then the corresponding message will be part of the non-validation message.
- A `list` or a `tuple`. Validation is done by first checking membership of the corresponding types, and then performing validation for each of the entries of the object being validated against the corresponding entries of the schema.
- A dictionary. Validation is done by first checking membership of the `dict` type, and then performing validation for each of the items of the object being validated against the corresponding items of the schema.
- A `set`. A set validates an object, if one of its members does.
- An arbitrary Python object. Validation is done by checking equality of the schema and the object, except when the schema is of type `float`, in which case `math.isclose` is used.
## Creating types
A cool feature of `vtjson` is that one can transform a schema into a genuine Python type via
```python
t = make_type(schema)
```
so that validation can be done via
```python
isinstance(object, t)
```
The drawback, compared to using `validate` directly, is that there is no feedback when validation fails. You can get it back as a console debug message via the optional `debug` argument to `make_type`.
The full signature of `make_type` is
```python
make_type(schema, name=None, strict=True, debug=False)
```
The optional `name` argument is used to set the `__name__` attribute of the type. If it is not supplied then `vtjson` tries to make an educated guess.
## Examples
```python
>>> from vtjson import set_name, validate
>>> schema = {"fruit" : {"apple", "pear", "strawberry"}, "price" : float}
>>> object = {"fruit" : "dog", "price": 1.0 }
>>> validate(schema, object)
...
vtjson.ValidationError: object['fruit'] (value:dog) is not equal to 'pear' and object['fruit'] (value:dog) is not equal to 'strawberry' and object['fruit'] (value:dog) is not equal to 'apple'
>>> fruit = set_name({"apple", "pear", "strawberry"}, "fruit")
>>> schema = {"fruit" : fruit, "price" : float}
>>> validate(schema, object)
...
vtjson.ValidationError: object['fruit'] (value:dog) is not of type 'fruit'
>>> object = {"fruit" : "apple"}
>>> validate(schema, object)
...
vtjson.ValidationError: object['price'] is missing
```
For many more examples see the file `test_validate.py` in the source distribution.
## FAQ
Q: Why not just use the Python implementation of `JSON schema` (see https://pypi.org/project/jsonschema/)?

A: Various reasons.
- A `vtjson` schema is much more concise than a `JSON` schema!
- `vtjson` can validate objects which are more general than strictly `JSON`. See the introductory example above. 
- More fundamentally, the design philosophy of `vtsjon` is different. A `JSON` schema  is language independent and fully declarative. These are very nice properties but, this being said, declarative languages have a tendency to suffer from feature creep as they try to deal with more and more exotic use cases (e.g. `css`).  A `vtjson` schema on the other hand leverages the versatility of the Python language. It is generally declarative, with a limited, but easily extendable set of primitives. But if more functionality is needed then it can be extended by using appropriate bits of Python code (as the `ordered_pair` example below illustrates). In practice this is what you will need in any case since a purely declarative language will never be able to deal with every possible validation scenario. 

Q: How is this different from https://pypi.org/project/json-checker/ \?

A: Good question! I discovered `json-checker` after I had written `vtjson`. Although the details are different `json-checker` and `vtjson` share many of the same principles.

Q: How to combine validations?

A: Use `intersect`. For example the following schema validates positive integers but reject positive floats.
```python
schema = intersect(int, interval(0, ...))
```
More generally one may use the pattern `intersect(schema, more_validations)` where the first argument makes sure that the object to be validated has the required layout to be an acceptable input for the later arguments. For example an ordered pair of integers can be validated using the schema
```python
def ordered_pair(o):
    return o[0] <= o[1]
schema = intersect((int, int), ordered_pair)
```
Or in a one liner
```python
schema = intersect((int, int), set_name(lambda o: o[0] <= o[1], "ordered_pair"))
```
The following also works if you are content with less nice output on validation failure (try it)
```
schema = intersect((int, int), lambda o: o[0] <= o[1])
```

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "vtjson",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "",
    "keywords": "",
    "author": "",
    "author_email": "Michel Van den Bergh <michel.vandenbergh@uhasselt.be>",
    "download_url": "https://files.pythonhosted.org/packages/b0/4b/147c913525a3bc3dc38005e6fdb4c04a7f03319c9242ca9cac5ebcc5840a/vtjson-1.4.2.tar.gz",
    "platform": null,
    "description": "# vtjson\nA lightweight package for validating JSON like Python objects.\n\n## Schemas\n\nValidation of JSON like Python objects is done according to a `schema` which is somewhat inspired by a typescript type. The format of a schema is more or less self explanatory as the following example shows.\n\n### Example\n\nBelow is the schema of the run object in the mongodb database underlying the Fishtest web application https://tests.stockfishchess.org/tests\n\n```python\nimport math\nfrom datetime import datetime\nfrom bson.objectid import ObjectId\nfrom vtjson import ip_address, number, regex, url\n\nnet_name = regex(\"nn-[a-z0-9]{12}.nnue\", name=\"net_name\")\ntc = regex(r\"([1-9]\\d*/)?\\d+(\\.\\d+)?(\\+\\d+(\\.\\d+)?)?\", name=\"tc\")\nstr_int = regex(r\"[1-9]\\d*\", name=\"str_int\")\nsha = regex(r\"[a-f0-9]{40}\", name=\"sha\")\ncountry_code = regex(r\"[A-Z][A-Z]\", name=\"country_code\")\nrun_id = regex(r\"[a-f0-9]{24}\", name=\"run_id\")\n\nworker_info_schema = {\n    \"uname\": str,\n    \"architecture\": [str, str],\n    \"concurrency\": int,\n    \"max_memory\": int,\n    \"min_threads\": int,\n    \"username\": str,\n    \"version\": int,\n    \"python_version\": [int, int, int],\n    \"gcc_version\": [int, int, int],\n    \"compiler\": {\"clang++\", \"g++\"},\n    \"unique_key\": str,\n    \"modified\": bool,\n    \"ARCH\": str,\n    \"nps\": number,\n    \"near_github_api_limit\": bool,\n    \"remote_addr\": ip_address,\n    \"country_code\": {country_code, \"?\"},\n}\n\nresults_schema = {\n    \"wins\": int,\n    \"losses\": int,\n    \"draws\": int,\n    \"crashes\": int,\n    \"time_losses\": int,\n    \"pentanomial\": [int, int, int, int, int],\n}\n\nschema = {\n    \"_id?\": ObjectId,\n    \"start_time\": datetime,\n    \"last_updated\": datetime,\n    \"tc_base\": number,\n    \"base_same_as_master\": bool,\n    \"results_stale?\": bool,\n    \"rescheduled_from?\": run_id,\n    \"approved\": bool,\n    \"approver\": str,\n    \"finished\": bool,\n    \"deleted\": bool,\n    \"failed\": bool,\n    \"is_green\": bool,\n    \"is_yellow\": bool,\n    \"workers?\": int,\n    \"cores?\": int,\n    \"results\": results_schema,\n    \"results_info?\": {\n        \"style\": str,\n        \"info\": [str, ...],\n    },\n    \"args\": {\n        \"base_tag\": str,\n        \"new_tag\": str,\n        \"base_net\": net_name,\n        \"new_net\": net_name,\n        \"num_games\": int,\n        \"tc\": tc,\n        \"new_tc\": tc,\n        \"book\": str,\n        \"book_depth\": str_int,\n        \"threads\": int,\n        \"resolved_base\": sha,\n        \"resolved_new\": sha,\n        \"msg_base\": str,\n        \"msg_new\": str,\n        \"base_options\": str,\n        \"new_options\": str,\n        \"info\": str,\n        \"base_signature\": str_int,\n        \"new_signature\": str_int,\n        \"username\": str,\n        \"tests_repo\": url,\n        \"auto_purge\": bool,\n        \"throughput\": number,\n        \"itp\": number,\n        \"priority\": number,\n        \"adjudication\": bool,\n        \"sprt?\": {\n            \"alpha\": 0.05,\n            \"beta\": 0.05,\n            \"elo0\": number,\n            \"elo1\": number,\n            \"elo_model\": \"normalized\",\n            \"state\": {\"\", \"accepted\", \"rejected\"},\n            \"llr\": number,\n            \"batch_size\": int,\n            \"lower_bound\": -math.log(19),\n            \"upper_bound\": math.log(19),\n            \"lost_samples?\": int,\n            \"illegal_update?\": int,\n            \"overshoot?\": {\n                \"last_update\": int,\n                \"skipped_updates\": int,\n                \"ref0\": number,\n                \"m0\": number,\n                \"sq0\": number,\n                \"ref1\": number,\n                \"m1\": number,\n                \"sq1\": number,\n            },\n        },\n        \"spsa?\": {\n            \"A\": number,\n            \"alpha\": number,\n            \"gamma\": number,\n            \"raw_params\": str,\n            \"iter\": int,\n            \"num_iter\": int,\n            \"params\": [\n                {\n                    \"name\": str,\n                    \"start\": number,\n                    \"min\": number,\n                    \"max\": number,\n                    \"c_end\": number,\n                    \"r_end\": number,\n                    \"c\": number,\n                    \"a_end\": number,\n                    \"a\": number,\n                    \"theta\": number,\n                },\n                ...,\n            ],\n            \"param_history?\": [\n                [{\"theta\": number, \"R\": number, \"c\": number}, ...],\n                ...,\n            ],\n        },\n    },\n    \"tasks\": [\n        {\n            \"num_games\": int,\n            \"active\": bool,\n            \"last_updated\": datetime,\n            \"start\": int,\n            \"residual?\": number,\n            \"residual_color?\": str,\n            \"bad?\": True,\n            \"stats\": results_schema,\n            \"worker_info\": worker_info_schema,\n        },\n        ...,\n    ],\n    \"bad_tasks?\": [\n        {\n            \"num_games\": int,\n            \"active\": False,\n            \"last_updated\": datetime,\n            \"start\": int,\n            \"residual\": number,\n            \"residual_color\": str,\n            \"bad\": True,\n            \"task_id\": int,\n            \"stats\": results_schema,\n            \"worker_info\": worker_info_schema,\n        },\n        ...,\n    ],\n}\n```\n## Conventions\n- As in typescript, a (string) key ending in `?` represents an optional key. The corresponding schema (the item the key points to) will only be used for validation when the key is present in the object that should be validated. A key can also be made optional by wrapping it as `optional_key(key)`.\n- If in a list/tuple the last entry is `...` (ellipsis) it means that the next to last entry will be repeated zero or more times. In this way generic types can be created. For example the schema `[str, ...]` represents a list of strings.\n## Usage\nTo validate an object against a schema one can simply do\n```python\nvalidate(schema, object)\n```  \nIf the validation fails this will throw a `ValidationError` and the exception contains an explanation about what went wrong. The full signature of `validate` is\n```python\nvalidate(schema, object, name=\"object\", strict=True)\n```\n- The optional `name` argument is used to refer to the object being validated in the returned message.\n- The optional argument `strict` indicates whether or not the object being validated is allowed to have keys/entries which are not in the schema.\n## Wrappers\nA wrapper takes one or more schemas as arguments and produces a new schema. **Warning:** wrappers should be considered as being immutable. In other words: if the underlying schemas change after a wrapper has been created then the effect of validating against the wrapper is undefined. If you _really_ need this then you should recreate the wrapper.\n- An object matches the schema `union(schema1, ..., schemaN)` if it matches one of the schemas `schema1, ..., schemaN`. This is almost the same as `{schema1, ..., schemaN}`, or equivalently `set((schema1, ..., schemaN))` if `schema1, ..., schemaN` are hashable.\n- An object matches the schema `intersect(schema1, ..., schemaN)` if it matches all the schemas `schema1, ..., schemaN`.\n- An object matches the schema `complement(schema)` if it does not match `schema`.\n- An object matches the schema `lax(schema)` if it matches `schema` when validated with `strict=False`.\n- An object matches the schema `strict(schema)` if it matches `schema` when validated with `strict=True`.\n- An object matches the schema `set_name(schema, name)` if it matches `schema`. But the `name` argument will be used in non-validation messages.\n- An object matches the schema `compile(schema)` if it matches `schema`. `vtjson` compiles the schema before performing a validation so pre-compiling is not necessary but, in some cases, it may gain a bit of performance. \n- An object matches the schema `quote(schema)` if it is equal to `schema`. For example the schema `{\"cats\", \"dogs\"}` matches the strings `\"cats\"` and `\"dogs\"` but the schema `quote({\"cats\", \"dogs\"})` matches the set `{\"cats\", \"dogs\"}`. \n## Built-ins\nSome built-ins take arguments. If no arguments are given then the parentheses can be omitted. So `email` is equivalent to `email()`.\n- `regex(pattern, name=None, fullmatch=True, flags=0)`. This matches the strings which match the given pattern. The optional `name` argument may be used to give the regular expression a descriptive name. By default the entire string is matched, but this can be overruled via the `fullmatch` argument. The `flags` argument has the usual meaning.\n- `interval(lowerbound, upperbound)`. This checks if `lowerbound <= object <= upperbound`, provided the comparisons make sense. An upper/lowerbound `...` (ellipsis) means that the\ncorresponding inequality is not checked.\n- `number`. Matches `int` and `float`.\n- `email`. Checks if the object is a valid email address. This uses the package `email_validator`. The `email` schema accepts the same options as `validate_email` in loc. cit.\n- `ip_address` and `url`. These are similar to `email`.\n- `domain_name(ascii_only=True, resolve=False)`. Checks if the object is a valid domain name. If `ascii_only=False` then allow IDNA domain names. If `resolve=True` check if the domain name resolves.\n- `date_time(format=None)`. Without argument this represents an ISO 8601 date-time. The `format` argument represents a format string for `strftime`.\n- `date` and `time`. These represent an ISO 8601 date and an ISO 8601 time.\n## Format\nA schema can be, in order of precedence:\n- A class with the following properties:\n  - it has a no-argument constructor;\n  - the instances have a `__validate__` method with signature\n  ```python\n  __validate__(self, object, name, strict)\n  ```\n  - The parameters of `__validate__()` have the same semantics as those of `validate()`. The return value of `__validate__()` should be the empty string if validation succeeds, and otherwise it should be an explanation about what went wrong.\n- An object having a `__validate__` attribute with signature\n  ```python\n  __validate__(object, name, strict)\n  ```\n  as above. This is for example how the wrapper schemas are implemented internally.\n- A Python type. In that case validation is done by checking membership.\n- A callable. Validation is done by applying the callable to the object. If applying the callable throws an exception then the corresponding message will be part of the non-validation message.\n- A `list` or a `tuple`. Validation is done by first checking membership of the corresponding types, and then performing validation for each of the entries of the object being validated against the corresponding entries of the schema.\n- A dictionary. Validation is done by first checking membership of the `dict` type, and then performing validation for each of the items of the object being validated against the corresponding items of the schema.\n- A `set`. A set validates an object, if one of its members does.\n- An arbitrary Python object. Validation is done by checking equality of the schema and the object, except when the schema is of type `float`, in which case `math.isclose` is used.\n## Creating types\nA cool feature of `vtjson` is that one can transform a schema into a genuine Python type via\n```python\nt = make_type(schema)\n```\nso that validation can be done via\n```python\nisinstance(object, t)\n```\nThe drawback, compared to using `validate` directly, is that there is no feedback when validation fails. You can get it back as a console debug message via the optional `debug` argument to `make_type`.\nThe full signature of `make_type` is\n```python\nmake_type(schema, name=None, strict=True, debug=False)\n```\nThe optional `name` argument is used to set the `__name__` attribute of the type. If it is not supplied then `vtjson` tries to make an educated guess.\n## Examples\n```python\n>>> from vtjson import set_name, validate\n>>> schema = {\"fruit\" : {\"apple\", \"pear\", \"strawberry\"}, \"price\" : float}\n>>> object = {\"fruit\" : \"dog\", \"price\": 1.0 }\n>>> validate(schema, object)\n...\nvtjson.ValidationError: object['fruit'] (value:dog) is not equal to 'pear' and object['fruit'] (value:dog) is not equal to 'strawberry' and object['fruit'] (value:dog) is not equal to 'apple'\n>>> fruit = set_name({\"apple\", \"pear\", \"strawberry\"}, \"fruit\")\n>>> schema = {\"fruit\" : fruit, \"price\" : float}\n>>> validate(schema, object)\n...\nvtjson.ValidationError: object['fruit'] (value:dog) is not of type 'fruit'\n>>> object = {\"fruit\" : \"apple\"}\n>>> validate(schema, object)\n...\nvtjson.ValidationError: object['price'] is missing\n```\nFor many more examples see the file `test_validate.py` in the source distribution.\n## FAQ\nQ: Why not just use the Python implementation of `JSON schema` (see https://pypi.org/project/jsonschema/)?\n\nA: Various reasons.\n- A `vtjson` schema is much more concise than a `JSON` schema!\n- `vtjson` can validate objects which are more general than strictly `JSON`. See the introductory example above. \n- More fundamentally, the design philosophy of `vtsjon` is different. A `JSON` schema  is language independent and fully declarative. These are very nice properties but, this being said, declarative languages have a tendency to suffer from feature creep as they try to deal with more and more exotic use cases (e.g. `css`).  A `vtjson` schema on the other hand leverages the versatility of the Python language. It is generally declarative, with a limited, but easily extendable set of primitives. But if more functionality is needed then it can be extended by using appropriate bits of Python code (as the `ordered_pair` example below illustrates). In practice this is what you will need in any case since a purely declarative language will never be able to deal with every possible validation scenario. \n\nQ: How is this different from https://pypi.org/project/json-checker/ \\?\n\nA: Good question! I discovered `json-checker` after I had written `vtjson`. Although the details are different `json-checker` and `vtjson` share many of the same principles.\n\nQ: How to combine validations?\n\nA: Use `intersect`. For example the following schema validates positive integers but reject positive floats.\n```python\nschema = intersect(int, interval(0, ...))\n```\nMore generally one may use the pattern `intersect(schema, more_validations)` where the first argument makes sure that the object to be validated has the required layout to be an acceptable input for the later arguments. For example an ordered pair of integers can be validated using the schema\n```python\ndef ordered_pair(o):\n    return o[0] <= o[1]\nschema = intersect((int, int), ordered_pair)\n```\nOr in a one liner\n```python\nschema = intersect((int, int), set_name(lambda o: o[0] <= o[1], \"ordered_pair\"))\n```\nThe following also works if you are content with less nice output on validation failure (try it)\n```\nschema = intersect((int, int), lambda o: o[0] <= o[1])\n```\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "A lightweight package for validating JSON like Python objects",
    "version": "1.4.2",
    "project_urls": {
        "Bug Tracker": "https://github.com/vdbergh/vtjson/issues",
        "Homepage": "https://github.com/vdbergh/vtjson"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "c61736e21768ff4d728b8f248e4ea5388c0dccbb8fbf05d118fbda32bcf2cf26",
                "md5": "303e2dcedd0441cfb8ee0991bd0b6fa1",
                "sha256": "7326037d79d7389263b4133e0987b8985bd3fb709e246ef5efe38df60b28022a"
            },
            "downloads": -1,
            "filename": "vtjson-1.4.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "303e2dcedd0441cfb8ee0991bd0b6fa1",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 10414,
            "upload_time": "2024-01-28T17:49:29",
            "upload_time_iso_8601": "2024-01-28T17:49:29.802479Z",
            "url": "https://files.pythonhosted.org/packages/c6/17/36e21768ff4d728b8f248e4ea5388c0dccbb8fbf05d118fbda32bcf2cf26/vtjson-1.4.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "b04b147c913525a3bc3dc38005e6fdb4c04a7f03319c9242ca9cac5ebcc5840a",
                "md5": "c2e61069c4044075e9938bbcf6ad026d",
                "sha256": "ed5825eb36cc9a86c2debe60796724414d48bbfeec442c5f50e8ffc92dc9c485"
            },
            "downloads": -1,
            "filename": "vtjson-1.4.2.tar.gz",
            "has_sig": false,
            "md5_digest": "c2e61069c4044075e9938bbcf6ad026d",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 10658,
            "upload_time": "2024-01-28T17:49:31",
            "upload_time_iso_8601": "2024-01-28T17:49:31.260655Z",
            "url": "https://files.pythonhosted.org/packages/b0/4b/147c913525a3bc3dc38005e6fdb4c04a7f03319c9242ca9cac5ebcc5840a/vtjson-1.4.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-01-28 17:49:31",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "vdbergh",
    "github_project": "vtjson",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "vtjson"
}
        
Elapsed time: 0.20814s