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

add support for draft-6 examples #43

Closed
wants to merge 1 commit into from
Closed
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: 4 additions & 2 deletions genson/schema/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,15 @@ def add_schema(self, schema):
del schema['$schema']
self._root_node.add_schema(schema)

def add_object(self, obj):
def add_object(self, obj, examples=False):
"""
Modify the schema to accommodate an object.

:param obj: any object or scalar that can be serialized in JSON
:param examples: whether or not to include examples values from the
given object.
"""
self._root_node.add_object(obj)
self._root_node.add_object(obj, examples)

def to_schema(self):
"""
Expand Down
7 changes: 5 additions & 2 deletions genson/schema/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,21 @@ def add_schema(self, schema):
# return self for easy method chaining
return self

def add_object(self, obj):
def add_object(self, obj, examples=False):
"""
Modify the schema to accommodate an object.

arguments:
* `obj` (required - `dict`):
a JSON object to use in generating the schema.
* `examples` (optional - `bbool`):
whether or not to include examples values from the
given object.
"""

# delegate to SchemaType object
active_strategy = self._get_strategy_for_object(obj)
active_strategy.add_object(obj)
active_strategy.add_object(obj, examples)

# return self for easy method chaining
return self
Expand Down
12 changes: 6 additions & 6 deletions genson/schema/strategies/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ def add_schema(self, schema):
if 'items' in schema:
self._items.add_schema(schema['items'])

def add_object(self, obj):
def add_object(self, obj, examples=False):
for item in obj:
self._items.add_object(item)
self._items.add_object(item, examples)

def items_to_schema(self):
return self._items.to_schema()
Expand All @@ -63,17 +63,17 @@ def __init__(self, node_class):
def add_schema(self, schema):
self.add_extra_keywords(schema)
if 'items' in schema:
self._add(schema['items'], 'add_schema')
self._add(schema['items'], lambda s, i: s.add_schema(i))

def add_object(self, obj):
self._add(obj, 'add_object')
def add_object(self, obj, examples=False):
self._add(obj, lambda s, i: s.add_object(i, examples))

def _add(self, items, func):
while len(self._items) < len(items):
self._items.append(self.node_class())

for subschema, item in zip(self._items, items):
getattr(subschema, func)(item)
func(subschema, item)

def items_to_schema(self):
return [item.to_schema() for item in self._items]
12 changes: 10 additions & 2 deletions genson/schema/strategies/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def match_object(cls, obj):
def __init__(self, node_class):
self.node_class = node_class
self._extra_keywords = {}
self._examples = []

def add_schema(self, schema):
self.add_extra_keywords(schema)
Expand All @@ -43,11 +44,18 @@ def add_extra_keywords(self, schema):
'values ({1!r} vs. {2!r}). Using {1!r}').format(
keyword, self._extra_keywords[keyword], value))

def add_object(self, obj):
def add_example(self, example):
if example not in self._examples:
self._examples.append(example)

def add_object(self, obj, examples=False):
pass

def to_schema(self):
return copy(self._extra_keywords)
schema = copy(self._extra_keywords)
if len(self._examples) > 0:
schema["examples"] = copy(self._examples)
return schema

def __eq__(self, other):
""" Required for SchemaBuilder.__eq__ to work properly """
Expand Down
6 changes: 3 additions & 3 deletions genson/schema/strategies/object.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def add_schema(self, schema):
else:
self._required &= set(schema['required'])

def add_object(self, obj):
def add_object(self, obj, examples=False):
properties = set()
for prop, subobj in obj.items():
pattern = None
Expand All @@ -53,10 +53,10 @@ def add_object(self, obj):
pattern = self._matching_pattern(prop)

if pattern is not None:
self._pattern_properties[pattern].add_object(subobj)
self._pattern_properties[pattern].add_object(subobj, examples)
else:
properties.add(prop)
self._properties[prop].add_object(subobj)
self._properties[prop].add_object(subobj, examples)

if self._required is None:
self._required = properties
Expand Down
8 changes: 7 additions & 1 deletion genson/schema/strategies/scalar.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ class String(TypedSchemaStrategy):
JS_TYPE = 'string'
PYTHON_TYPE = (str, type(u''))

def add_object(self, obj, examples=False):
if examples:
self.add_example(obj)


class Number(SchemaStrategy):
"""
Expand Down Expand Up @@ -73,9 +77,11 @@ def add_schema(self, schema):
if schema.get('type') == 'number':
self._type = 'number'

def add_object(self, obj):
def add_object(self, obj, examples=False):
if isinstance(obj, float):
self._type = 'number'
if examples:
self.add_example(obj)

def to_schema(self):
schema = super(Number, self).to_schema()
Expand Down
4 changes: 2 additions & 2 deletions test/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ def setUp(self):
def set_schema_options(self, **options):
self.builder = SchemaNode(**options)

def add_object(self, obj):
self.builder.add_object(obj)
def add_object(self, obj, examples=False):
self.builder.add_object(obj, examples)
self._objects.append(obj)

def add_schema(self, schema):
Expand Down
66 changes: 66 additions & 0 deletions test/test_gen_single.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class TestArrayList(base.SchemaNodeTestCase):

def setUp(self):
base.SchemaNodeTestCase.setUp(self)
self.maxDiff = None

def test_empty(self):
self.add_object([])
Expand Down Expand Up @@ -209,3 +210,68 @@ def test_three_deep(self):
}
}
})


class TestExamples(base.SchemaNodeTestCase):

def setUp(self):
base.SchemaNodeTestCase.setUp(self)

def test_empty_examples(self):
self.add_object({}, examples=True)
self.assertResult({"type": "object"})

def test_no_examples(self):
self.add_object(1, examples=False)
self.assertResult({"type": "integer"})

def test_integer(self):
self.add_object(1, examples=True)
self.add_object(2, examples=True)
self.assertResult({"type": "integer", "examples": [1, 2]})

def test_string(self):
self.add_object("foo", examples=True)
self.add_object("bar", examples=True)
self.assertResult({"type": "string", "examples": ["foo", "bar"]})

def test_bool(self):
self.add_object(True, examples=True)
self.assertResult({"type": "boolean"})

def test_array(self):
self.add_object(["spam", "spam", "eggs", "spam"], examples=True)
self.assertResult({"type": "array", "items": {
"type": "string", "examples": ["spam", "eggs"]}
})

def test_tuple(self):
self.add_schema({'type': 'array', 'items': [
{"type": "string"},
{"type": "integer"},
{"type": "boolean"}
]})
self.add_object(["one", 2, False], examples=True)
self.assertResult({"type": "array", "items": [
{"type": "string", "examples": ["one"]},
{"type": "integer", "examples": [2]},
{"type": "boolean"},
]})

def test_object(self):
self.add_object({
"foo": "bar",
"hop": 1
}, examples=True)
self.add_object({
"foo": "nop",
"hop": 1
}, examples=True)
self.assertResult({
"required": ["foo", "hop"],
"type": "object",
"properties": {
"foo": {"type": "string", "examples": ["bar", "nop"]},
"hop": {"type": "integer", "examples": [1]}
}
})