Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for allOf, anyOf, const, any #3

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@
venv
*.pyc
build
dist
dist
.pytest_cache
.benchmarks
.hypothesis
__pycache__
10 changes: 10 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"python.testing.pytestArgs": [
"-vs",
"--color=yes",
"tests"
],
"python.testing.unittestEnabled": false,
"python.testing.nosetestsEnabled": false,
"python.testing.pytestEnabled": true
}
104 changes: 104 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@

## Hyposchema

```python

from jsonschema import validate
import json
from hypothesis import given, settings
from hyposchema.hypo_schema import generate_from_schema


EXAMPLE_JSON_SCHEMA= {
"title": "Example Schema",
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"age": {
"description": "Age in years",
"type": "integer",
"minimum": 0
},
"listOfElements": {
"type": "array",
"items": {
"type": "number"
}
},
"listOfRandom": {
"min": 4,
"max": 10,
"type": "array"
},
"type": {
"type": "string",
"enum": ["string", "int", "bool"]
},
"nestedMap": {
"type": "object",
"properties": {
"firstProp": {
"type": "string"
}
},
"required": ["firstProp"]
}
},
"required": ["firstName", "lastName", "nestedMap", "listOfElements"]
}




@given(generate_from_schema(EXAMPLE_JSON_SCHEMA))
@settings(max_examples=10)
def test_basic_map(example_data):
print(json.dumps(example_data, indent=4))
validate(example_data, EXAMPLE_JSON_SCHEMA)

test_basic_map()

```

Better using a simpler schema than json-schema, like `skema`:

```python

import skema
from jsonschema import validate
import json
from hypothesis import given, settings
from hyposchema.hypo_schema import generate_from_schema

EXAMPLE_JSON_SCHEMA=skema.to_jsonschema("""
Root: EventA & EventB
EventA:
type: Str
fields:
args: [
name: Str
type: Str | Any
]
...

EventB:
timestamp: Any
sentBy: Str
madeBy: "me" | "you"
...

""", resolve=True)

@given(generate_from_schema(EXAMPLE_JSON_SCHEMA))
@settings(max_examples=10)
def test_basic_map( example_data):
print(json.dumps(example_data, indent=4))
validate(example_data, EXAMPLE_JSON_SCHEMA)

test_basic_map()
```
42 changes: 42 additions & 0 deletions hyposchema/hypo_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ def gen_enum(prop):
enum = prop["enum"]
return hs.sampled_from(enum)

def gen_const(prop):
const = prop["const"]
return hs.sampled_from([const])

def gen_bool(prop):
return hs.booleans()

Expand All @@ -93,6 +97,30 @@ def gen_one_of(prop):

return hs.one_of(possible_values)

def gen_any_of(prop):
possible_values = []
for value in prop["anyOf"]:
possible_values.append(get_generator(value))

return hs.one_of(possible_values)

def gen_all_of(prop):
dicts = prop["allOf"]
if not all([d['type'] == 'object' for d in dicts]):
raise Exception('allOf is supported as array of object types')
result = {
'additionalProperties': True,
'required': [],
'properties': {},
}
for obj in dicts:
result['additionalProperties'] = result['additionalProperties'] and bool(obj.get('additionalProperties', True))
result['properties'] = {**result['properties'], **(obj.get('properties', {}) or {})}
result['required'] = result['required'] + (obj.get('required', []) or [])
result['required'] = list(set(result['required']))
return gen_object(result)




def get_generator(prop):
Expand All @@ -103,15 +131,29 @@ def get_generator(prop):
"object": gen_object,
"array": gen_array,
}
if not prop:
return gen_anything()

enum = prop.get("enum", None)
if enum is not None:
return gen_enum(prop)

const = prop.get("const", None)
if const is not None:
return gen_const(prop)

one_of = prop.get("oneOf", None)
if one_of is not None:
return gen_one_of(prop)

any_of = prop.get("anyOf", None)
if any_of is not None:
return gen_any_of(prop)

all_of = prop.get("allOf", None)
if all_of is not None:
return gen_all_of(prop)

json_type = prop.get("type", None)
if json_type is None:
raise JsonTypeError("Couldnt find type in prop {0}".format(prop))
Expand Down
2 changes: 1 addition & 1 deletion hyposchema/regex.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from itertools import chain

import hypothesis.strategies as strats
from hypothesis.strategies import defines_strategy
from hypothesis._strategies import defines_strategy
from hypothesis.searchstrategy import SearchStrategy
from hypothesis.internal.compat import hunichr, hrange

Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
twine
wheel
skema
File renamed without changes.
36 changes: 36 additions & 0 deletions tests/skema/test_complex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

import skema
from jsonschema import validate
import json
from hypothesis import given, settings
from hyposchema.hypo_schema import generate_from_schema


EXAMPLE_JSON_SCHEMA=skema.to_jsonschema("""
Root: EventA & EventB
EventA:
type: Str
fields:
args: [
name: Str
type: Str | Any
]
...

EventB:
timestamp: Any
sentBy: Str
madeBy: "me" | "you"
...

""", resolve=True)




@given(generate_from_schema(EXAMPLE_JSON_SCHEMA))
@settings(max_examples=10)
def test_basic_map( example_data):
print(json.dumps(example_data, indent=4))
validate(example_data, EXAMPLE_JSON_SCHEMA)

26 changes: 26 additions & 0 deletions tests/skema/test_simple.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

import skema
from jsonschema import validate
import json
from hypothesis import given, settings
from hyposchema.hypo_schema import generate_from_schema


EXAMPLE_JSON_SCHEMA=skema.to_jsonschema("""
Root:
a: Int
b: Str
c:
x: Any
y: Str | Int
""", resolve=True)




@given(generate_from_schema(EXAMPLE_JSON_SCHEMA))
@settings(max_examples=10)
def test_basic_map( example_data):
print(json.dumps(example_data, indent=4))
validate(example_data, EXAMPLE_JSON_SCHEMA)

63 changes: 63 additions & 0 deletions tests/test_allOf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import unittest

from jsonschema import validate
import json
from hypothesis import given, settings
from hyposchema.hypo_schema import generate_from_schema


EXAMPLE_JSON_SCHEMA= {
"title": "Example Schema",
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"all": {
'allOf': [
{"type": "object", 'properties': {'a': {}, 'b': {}}, 'required': ['a', 'b'] },
{"type": "object", 'properties': {'c': {}, 'd': {}}, 'required': ['c',] },
]
},
"age": {
"description": "Age in years",
"type": "integer",
"minimum": 0
},
"listOfElements": {
"type": "array",
"items": {
"type": "number"
}
},
"listOfRandom": {
"min": 4,
"max": 10,
"type": "array"
},
"type": {
"type": "string",
"enum": ["string", "int", "bool"]
},
"nestedMap": {
"type": "object",
"properties": {
"firstProp": {
"type": "string"
}
},
"required": ["firstProp"]
}
},
"required": ["all", "nestedMap", "listOfElements"]
}




@given(generate_from_schema(EXAMPLE_JSON_SCHEMA))
@settings(max_examples=10)
def test_basic_map( example_data):
print(json.dumps(example_data, indent=4))
validate(example_data, EXAMPLE_JSON_SCHEMA)

Loading