Welcome to JSL’s documentation!¶
JSL is a DSL for describing JSON schemas.
Its code is open source and available at GitHub.
Tutorial¶
For an overview of JSL’s basic functionality, please see the Overview and Tutorial.
Overview and Tutorial¶
Welcome to JSL!
This document is a brief tour of JSL’s features and a quick guide to its use. Additional documentation can be found in the API documentation.
Introduction¶
JSON Schema is a JSON-based format to define the structure of JSON data for validation and documentation.
JSL is a Python library that provides a DSL for describing JSON schemas.
Why invent a DSL?
- A JSON schema in terms of the Python language is a dictionary. A JSON schema of a more or less complex data structure is a dictionary which most likely contains a lot of nested dictionaries of dictionaries of dictionaries. Writing and maintaining the readability of such a dictionary are not very rewarding tasks. They require typing a lot of quotes, braces, colons and commas and carefully indenting everything.
- The JSON schema standard is not always intuitive. It takes a little bit of practice
to remember where to use the
maxItems
keyword and where themaxLength
, or not to forget to setadditionalProperties
to false, and so on. - The syntax is not very concise. The signal-to-noise ratio increases rapidly with the complexity of the schema, which makes large schemas difficult to read.
JSL is created to address these issues. It allows you to define JSON schemas as if they were ORM models – using classes and fields and relying on the deep metaclass magic under the hood.
Such an approach makes writing and reading schemas easier. It encourages the decomposition of large schemas into smaller readable pieces and makes schemas extendable using class inheritance. It enables the autocomplete feature or IDEs and makes any mistype in a JSON schema keyword cause a RuntimeError.
Since every JSON schema object is itself valid JSON, the json
module in
the Python standard library can be used for printing and serialization of
a generated schema.
Quick Example¶
import jsl
class Entry(jsl.Document):
name = jsl.StringField(required=True)
class File(Entry):
content = jsl.StringField(required=True)
class Directory(Entry):
content = jsl.ArrayField(jsl.OneOfField([
jsl.DocumentField(File, as_ref=True),
jsl.DocumentField(jsl.RECURSIVE_REFERENCE_CONSTANT)
]), required=True)
Directory.get_schema(ordered=True)
returns the following schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"directory": {
"type": "object",
"properties": {
"name": {"type": "string"},
"content": {
"type": "array",
"items": {
"oneOf": [
{"$ref": "#/definitions/file"},
{"$ref": "#/definitions/directory"}
]
}
}
},
"required": ["name", "content"],
"additionalProperties": false
},
"file": {
"type": "object",
"properties": {
"name": {"type": "string"},
"content": {"type": "string"}
},
"required": ["name", "content"],
"additionalProperties": false
}
},
"$ref": "#/definitions/directory"
}
Main Features¶
JSL introduces the notion of a document and provides a set of fields.
The schema of a document is always {"type": "object"}
, whose properties
contain the
schemas of the fields of the document. A document may be thought of as a DictField
with some special abilities. A document is a class, thus it has a name, by which it can be
referenced from another document and either inlined or included using the
{"$ref": "..."}
syntax (see DocumentField
and its as_ref
parameter).
Also documents can be recursive.
The most useful method of Document
and the fields is Document.get_schema()
.
Fields and their parameters are named correspondingly to the keywords described in the JSON Schema standard. So getting started with JSL will be easy for those familiar with the standard.
Variables and Scopes¶
Suppose there is an application that provides a JSON RESTful API backed by MongoDB.
Let’s describe a User
data model:
class User(jsl.Document):
id = jsl.StringField(required=True)
login = jsl.StringField(required=True, min_length=3, max_length=20)
User.get_schema(ordered=True)
produces the following schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"additionalProperties": false,
"properties": {
"id": {"type": "string"},
"login": {
"type": "string",
"minLength": 3,
"maxLength": 20
}
},
"required": ["id", "login"]
}
It describes a response of the imaginary /users/<login>/
endpoint and
perhaps a database document structure (if the application stores users “as is”).
Let’s now describe a structure of the data required to create a new user
(i.e., a JSON-payload of POST
-requests to the imaginary /users/
endpoint).
The data may and may not contain id
; if id
is not present, it will
be generated by the application:
class UserCreationRequest(jsl.Document):
id = jsl.StringField()
login = jsl.StringField(required=True, min_length=3, max_length=20)
The only difference between User
and UserCreationRequest
is whether
the "id"
field is required or not.
JSL provides means not to repeat ourselves.
Using Variables¶
Variables
. are objects which value depends on a given role.
Which value must be used for which role is determined by a list of rules.
A rule is a pair of a matcher and a value. A matcher is a callable that returns
True
or False
(or a string or an iterable that will be converted to a lambda).
Here’s what it may look like:
>>> var = jsl.Var([
... # the same as (lambda r: r == 'role_1', 'A')
... ('role_1', 'A'),
... # the same as (lambda r: r in ('role_2', 'role_3'), 'A')
... (('role_2', 'role_3'), 'B'),
... (lambda r: r.startswith('bad_role_'), 'C'),
... ], default='D')
>>> var.resolve('role_1')
Resolution(value='A', role='role_1')
>>> var.resolve('role_2')
Resolution(value='B', role='role_2')
>>> var.resolve('bad_role_1')
Resolution(value='C', role='bad_role_1')
>>> var.resolve('qwerty')
Resolution(value='D', role='qwerty')
Variables can be used instead of regular values almost everywhere in JSL –
e.g., they can be added to documents, passed as arguments to fields
or even used as properties of a DictField
.
Let’s introduce a couple of roles for our User
document:
# to describe structures of POST requests
REQUEST_ROLE = 'request'
# to describe structures of responses
RESPONSE_ROLE = 'response'
# to describe structures of database documents
DB_ROLE = 'db'
Create a variable true_if_not_requests
which is only True
when the role is
REQUEST_ROLE
:
true_if_not_request = jsl.Var({
jsl.not_(REQUEST_ROLE): True
})
And describe User
and UserCreationRequest
in a single document
using true_if_not_requests
for the required
argument of the id
field:
class User(jsl.Document):
id = jsl.StringField(required=true_if_not_request)
login = jsl.StringField(required=True, min_length=3, max_length=20)
The role
argument can be specified for the Document.get_schema()
method:
User.get_schema(ordered=True, role=REQUEST_ROLE)
That call will return the following schema. Note that "id"
is not listed as required:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"additionalProperties": false,
"properties": {
"id": {"type": "string"},
"login": {
"type": "string",
"minLength": 3,
"maxLength": 20
}
},
"required": ["login"]
}
Using Scopes¶
Let’s add a version
field to the User
document with the following
requirements in mind: it is stored in the database, but must not appear
neither in the request nor the response (a reason for this can be that HTTP
headers such as ETag
and If-Match
are used for concurrency control).
One way is to turn the version
field into a variable that only resolves
to the field when the current role is DB_ROLE
and resolves to
None
otherwise:
class User(jsl.Document):
id = jsl.StringField(required=true_if_not_request)
login = jsl.StringField(required=True, min_length=3, max_length=20)
version = jsl.Var({
DB_ROLE: jsl.StringField(required=True)
})
Another (and more preferable) way is to use scopes
:
class User(jsl.Document):
id = jsl.StringField(required=true_if_not_request)
login = jsl.StringField(required=True, min_length=3, max_length=20)
with jsl.Scope(DB_ROLE) as db_scope:
db_scope.version = jsl.StringField(required=True)
A scope is a set of fields
and a matcher.
A scope can be added to a document, and if the matcher of a scope returns True
,
its fields will be present in the resulting schema.
A document may contain arbitrary number of scopes:
class Message(jsl.Document):
created_at = jsl.IntField(required=True)
content = jsl.StringField(required=True)
class User(jsl.Document):
id = jsl.StringField(required=true_if_not_request)
login = jsl.StringField(required=True, min_length=3, max_length=20)
with jsl.Scope(jsl.not_(REQUEST_ROLE)) as full_scope:
# a new user can not have messages
full_scope.messages = jsl.ArrayField(
jsl.DocumentField(Message), required=True)
with jsl.Scope(DB_ROLE) as db_scope:
db_scope.version = jsl.StringField(required=True)
Now User.get_schema(ordered=True, role=DB_ROLE)
returns the following schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"additionalProperties": false,
"properties": {
"id": {"type": "string"},
"login": {
"type": "string",
"minLength": 3,
"maxLength": 20
},
"messages": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"created_at": {
"type": "integer"
},
"content": {
"type": "string"
}
},
"required": ["created_at", "content"]
}
},
"version": {"type": "string"}
},
"required": ["id", "login", "messages", "version"]
}
Document Inheritance¶
There are four inheritance modes available in JSL: inline, all-of, any-of, and one-of.
In the inline mode (used by default), a schema of the child document contains a copy of its parent’s fields.
In the the other three modes a schema of the child document is a validator of the type allOf, anyOf, or oneOf that contains references to all parent schemas along with the schema that defines the child’s fields.
The inheritance mode can be set using the inheritance_mode
document option
.
Example¶
Suppose we have a Shape document:
class Shape(Base):
class Options(object):
definition_id = 'shape'
color = StringField()
The table below shows the difference between inline and all-of modes:
Inline | All-of |
---|---|
class Circle(Shape):
class Options(object):
definition_id = 'circle'
# inheritance_mode = INLINE
radius = NumberField()
|
class Circle(Shape):
class Options(object):
definition_id = 'circle'
inheritance_mode = ALL_OF
radius = NumberField()
|
Resulting schema: {
"type": "object",
"properties": {
"color": {
"type": "string"
},
"radius": {
"type": "number"
}
}
}
|
Resulting schema: {
"definitions": {
"shape": {
"type": "object",
"properties": {
"color": {
"type": "string"
}
}
}
},
"allOf": [
{
"$ref": "#/definitions/shape"
},
{
"type": "object",
"properties": {
"radius": {
"type": "number"
}
}
}
]
}
|
More Examples¶
A JSON schema from the official documentation defined using JSL:
class DiskDevice(jsl.Document):
type = jsl.StringField(enum=['disk'], required=True)
device = jsl.StringField(pattern='^/dev/[^/]+(/[^/]+)*$', required=True)
class DiskUUID(jsl.Document):
type = jsl.StringField(enum=['disk'], required=True)
label = jsl.StringField(pattern='^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-'
'[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$',
required=True)
class NFS(jsl.Document):
type = jsl.StringField(enum=['nfs'], required=True)
remotePath = jsl.StringField(pattern='^(/[^/]+)+$', required=True)
server = jsl.OneOfField([
jsl.StringField(format='ipv4'),
jsl.StringField(format='ipv6'),
jsl.StringField(format='host-name'),
], required=True)
class TmpFS(jsl.Document):
type = jsl.StringField(enum=['tmpfs'], required=True)
sizeInMb = jsl.IntField(minimum=16, maximum=512, required=True)
class FSTabEntry(jsl.Document):
class Options(object):
description = 'schema for an fstab entry'
storage = jsl.OneOfField([
jsl.DocumentField(DiskDevice, as_ref=True),
jsl.DocumentField(DiskUUID, as_ref=True),
jsl.DocumentField(NFS, as_ref=True),
jsl.DocumentField(TmpFS, as_ref=True),
], required=True)
fstype = jsl.StringField(enum=['ext3', 'ext4', 'btrfs'])
options = jsl.ArrayField(jsl.StringField(), min_items=1, unique_items=True)
readonly = jsl.BooleanField()
API Documentation¶
Document¶
-
jsl.document.
ALL_OF
= 'all_of'¶ str(object=’‘) -> string
Return a nice string representation of the object. If the argument is a string, the return value is the same object.
-
jsl.document.
ANY_OF
= 'any_of'¶ str(object=’‘) -> string
Return a nice string representation of the object. If the argument is a string, the return value is the same object.
-
jsl.document.
ONE_OF
= 'one_of'¶ str(object=’‘) -> string
Return a nice string representation of the object. If the argument is a string, the return value is the same object.
-
jsl.document.
INLINE
= 'inline'¶ str(object=’‘) -> string
Return a nice string representation of the object. If the argument is a string, the return value is the same object.
-
class
jsl.document.
Options
(additional_properties=False, pattern_properties=None, min_properties=None, max_properties=None, title=None, description=None, default=None, enum=None, id='', schema_uri='http://json-schema.org/draft-04/schema#', definition_id=None, roles_to_propagate=None, inheritance_mode='inline')[source]¶ A container for options.
All the arguments are the same and work exactly as for
fields.DictField
exceptproperties
(since it is automatically populated with the document fields) and these:Parameters: - definition_id (str or
Resolvable
) – A unique string to be used as a key for this document in the “definitions” schema section. If not specified, will be generated from module and class names. - schema_uri (str) – An URI of the JSON Schema meta-schema.
- roles_to_propagate (callable, string or iterable) – A matcher. If it returns
True
for a role, it will be passed to nested documents. - inheritance_mode (str) –
An inheritance mode: one of
INLINE
(default),ALL_OF
,ANY_OF
, orONE_OF
New in version 0.1.4.
- definition_id (str or
-
class
jsl.document.
Document
[source]¶ A document. Can be thought as a kind of
fields.DictField
, which properties are defined by the fields and scopes added to the document class.It can be tuned using special
Options
attribute (seeOptions
for available settings):class User(Document): class Options(object): title = 'User' description = 'A person who uses a computer or network service.' login = StringField(required=True)
Note
A subclass inherits options of its parent documents.
-
classmethod
is_recursive
(role='default')[source]¶ Returns
True
if there is aDocumentField
-references cycle that containscls
.Parameters: role (str) – A current role.
-
classmethod
get_definition_id
(role='default')[source]¶ Returns a unique string to be used as a key for this document in the
"definitions"
schema section.
-
classmethod
resolve_field
(field, role='default')[source]¶ Resolves a field with the name
field
usingrole
.Raises: AttributeError
-
classmethod
resolve_and_iter_fields
(role='default')[source]¶ Resolves each resolvable attribute of a document using the specified role and yields a tuple of (attribute name, field) in case the result is a JSL field.
Changed in version 0.2: The method has been changed to iterate only over fields that attached as attributes, and yield tuples instead of plain
BaseField
.Return type: iterable of (str, BaseField
)
-
classmethod
resolve_and_walk
(role='default', through_document_fields=False, visited_documents=frozenset([]))[source]¶ The same as
walk()
, butresolvables
are resolved usingrole
.
-
classmethod
iter_fields
()[source]¶ Iterates over the fields of the document, resolving its
resolvables
to all possible values.
-
classmethod
walk
(through_document_fields=False, visited_documents=frozenset([]))[source]¶ Iterates recursively over the fields of the document, resolving occurring
resolvables
to their all possible values.Visits fields in a DFS order.
Parameters: - through_document_fields (bool) – If
True
, walks through nestedDocumentField
fields. - visited_documents (set) – Keeps track of visited
documents
to avoid infinite recursion whenthrough_document_field
isTrue
.
Returns: iterable of
BaseField
- through_document_fields (bool) – If
-
classmethod
get_schema
(role='default', ordered=False)[source]¶ Returns a JSON schema (draft v4) of the document.
Parameters: - role (str) – A role.
- ordered (bool) – If
True
, the resulting schema dictionary is ordered. Fields are listed in the order they are added to the class. Schema properties are also ordered in a sensible and consistent way, making the schema more human-readable.
Raises: Return type: dict or OrderedDict
-
classmethod
get_definitions_and_schema
(role='default', res_scope=ResolutionScope( base=, current=, output= ), ordered=False, ref_documents=None)[source]¶ Returns a tuple of two elements.
The second element is a JSON schema of the document, and the first is a dictionary that contains definitions that are referenced from the schema.
Parameters: - role (str) – A role.
- ordered (bool) – If
True
, the resulting schema dictionary is ordered. Fields are listed in the order they are added to the class. Schema properties are also ordered in a sensible and consistent way, making the schema more human-readable. - res_scope (
ResolutionScope
) – The current resolution scope. - ref_documents (set) – If subclass of
Document
is in this set, allDocumentField
s pointing to it will be resolved as a reference:{"$ref": "#/definitions/..."}
. Note: resulting definitions will not contain schema for this document.
Raises: Return type: (dict or OrderedDict)
-
classmethod
-
class
jsl.document.
DocumentMeta
[source]¶ A metaclass for
Document
. It’s responsible for collecting options, fields and scopes registering the document in the registry, making it the owner of nesteddocument fields
s and so on.-
options_container
¶ A class to be used by
create_options()
. Must be a subclass ofOptions
.alias of
Options
-
classmethod
collect_fields
(mcs, bases, attrs)[source]¶ Collects fields from the current class and its parent classes.
Return type: a dictionary mapping field names to fields
-
classmethod
collect_options
(mcs, bases, attrs)[source]¶ Collects options from the current class and its parent classes.
Returns: a dictionary of options
-
classmethod
create_options
(options)[source]¶ Wraps
options
into a container class (seeoptions_container
).Parameters: options – a dictionary of options Returns: an instance of options_container
-
Fields¶
Primitive Fields¶
-
class
jsl.fields.
NullField
(id='', default=None, enum=None, title=None, description=None, **kwargs)[source]¶ A null field.
-
class
jsl.fields.
BooleanField
(id='', default=None, enum=None, title=None, description=None, **kwargs)[source]¶ A boolean field.
-
class
jsl.fields.
NumberField
(multiple_of=None, minimum=None, maximum=None, exclusive_minimum=None, exclusive_maximum=None, **kwargs)[source]¶ A number field.
Parameters: - multiple_of (number or
Resolvable
) – A value must be a multiple of this factor. - minimum (number or
Resolvable
) – A minimum allowed value. - exclusive_minimum (bool or
Resolvable
) – Whether a value is allowed to exactly equal the minimum. - maximum (number or
Resolvable
) – A maximum allowed value. - exclusive_maximum (bool or
Resolvable
) – Whether a value is allowed to exactly equal the maximum.
-
multiple_of
= None¶
-
minimum
= None¶
-
exclusive_minimum
= None¶
-
maximum
= None¶
-
exclusive_maximum
= None¶
- multiple_of (number or
-
class
jsl.fields.
IntField
(multiple_of=None, minimum=None, maximum=None, exclusive_minimum=None, exclusive_maximum=None, **kwargs)[source]¶ Bases:
jsl.fields.primitive.NumberField
An integer field.
-
class
jsl.fields.
StringField
(pattern=None, format=None, min_length=None, max_length=None, **kwargs)[source]¶ A string field.
Parameters: - pattern (string or
Resolvable
) – A regular expression (ECMA 262) that a string value must match. - format (string or
Resolvable
) – A semantic format of the string (for example,"date-time"
,"email"
, or"uri"
). - min_length (int or
Resolvable
) – A minimum length. - max_length (int or
Resolvable
) – A maximum length.
-
pattern
= None¶
-
format
= None¶
-
min_length
= None¶
-
max_length
= None¶
- pattern (string or
-
class
jsl.fields.
EmailField
(pattern=None, format=None, min_length=None, max_length=None, **kwargs)[source]¶ Bases:
jsl.fields.primitive.StringField
An email field.
-
class
jsl.fields.
IPv4Field
(pattern=None, format=None, min_length=None, max_length=None, **kwargs)[source]¶ Bases:
jsl.fields.primitive.StringField
An IPv4 field.
Compound Fields¶
-
jsl.fields.
RECURSIVE_REFERENCE_CONSTANT
¶ A special value to be used as an argument to create a recursive
DocumentField
.
-
class
jsl.fields.
DocumentField
(document_cls, as_ref=False, **kwargs)[source]¶ A reference to a nested document.
Parameters: - document_cls – A string (dot-separated path to document class, i.e.
"app.resources.User"
),RECURSIVE_REFERENCE_CONSTANT
or aDocument
subclass. - as_ref (bool) – If
True
, the schema ofdocument_cls`
is placed into the definitions dictionary, and the field schema just references to it:{"$ref": "#/definitions/..."}
. It may make a resulting schema more readable.
-
as_ref
= None¶
- document_cls – A string (dot-separated path to document class, i.e.
-
class
jsl.fields.
RefField
(pointer, **kwargs)[source]¶ A reference.
Parameters: pointer (str) – A JSON pointer.
-
pointer
= None¶
-
-
class
jsl.fields.
ArrayField
(items=None, additional_items=None, min_items=None, max_items=None, unique_items=None, **kwargs)[source]¶ An array field.
Parameters: - items –
Either of the following:
BaseField
– all items of the array must match the field schema;- a list or a tuple of
fields
– all items of the array must be valid according to the field schema at the corresponding index (tuple typing); - a
Resolvable
resolving to either of the first two options.
- min_items (int or
Resolvable
) – A minimum length of an array. - max_items (int or
Resolvable
) – A maximum length of an array. - unique_items (bool or
Resolvable
) – Whether all the values in the array must be distinct. - additional_items (bool or
BaseField
orResolvable
) – If the value ofitems
is a list or a tuple, and the array length is larger than the number of fields initems
, then the additional items are described by theBaseField
passed using this argument.
-
items
= None¶
-
min_items
= None¶
-
max_items
= None¶
-
unique_items
= None¶
-
additional_items
= None¶
- items –
-
class
jsl.fields.
DictField
(properties=None, pattern_properties=None, additional_properties=None, min_properties=None, max_properties=None, **kwargs)[source]¶ A dictionary field.
Parameters: - properties (dict[str ->
BaseField
orResolvable
]) – A dictionary containing fields. - pattern_properties (dict[str ->
BaseField
orResolvable
]) – A dictionary whose keys are regular expressions (ECMA 262). Properties match against these regular expressions, and for any that match, the property is described by the corresponding field schema. - additional_properties (bool or
BaseField
orResolvable
) – Describes properties that are not described by theproperties
orpattern_properties
. - min_properties (int or
Resolvable
) – A minimum number of properties. - max_properties (int or
Resolvable
) – A maximum number of properties
-
properties
= None¶
-
pattern_properties
= None¶
-
additional_properties
= None¶
-
min_properties
= None¶
-
max_properties
= None¶
- properties (dict[str ->
-
class
jsl.fields.
NotField
(field, **kwargs)[source]¶ Parameters: field ( BaseField
orResolvable
) – A field to negate.-
field
= None¶
-
-
class
jsl.fields.
OneOfField
(fields, **kwargs)[source]¶ Parameters: fields (list[ BaseField
orResolvable
]) – A list of fields, exactly one of which describes the data.-
fields
= None¶
-
-
class
jsl.fields.
AnyOfField
(fields, **kwargs)[source]¶ Parameters: fields (list[ BaseField
orResolvable
]) – A list of fields, at least one of which describes the data.-
fields
= None¶
-
-
class
jsl.fields.
AllOfField
(fields, **kwargs)[source]¶ Parameters: fields (list[ BaseField
orResolvable
]) – A list of fields, all of which describe the data.-
fields
= None¶
-
Base Classes¶
-
jsl.fields.
Null
= <jsl.fields.base.NullSentinel object>¶ A special value that can be used to set the default value of a field to null.
-
class
jsl.fields.
BaseField
(name=None, required=False, **kwargs)[source]¶ A base class for fields of
documents
. Instances of this class may be added to a document to define its properties.Implements the
Resolvable
interface.Parameters: - required (bool or
Resolvable
) – Whether the field is required. Defaults toFalse
. - name (str) –
If specified, used as a key under which the field schema appears in
document
schema properties.New in version 0.1.3.
-
name
= None¶ Name
-
required
= None¶ Whether the field is required.
-
resolve
(role)[source]¶ Implements the
Resolvable
interface.Always returns a
Resolution(self, role)
.Return type: Resolution
-
iter_possible_values
()[source]¶ Implements the
Resolvable
interface.Yields a single value –
self
.
-
get_definitions_and_schema
(role='default', res_scope=ResolutionScope( base=, current=, output= ), ordered=False, ref_documents=None)[source]¶ Returns a tuple of two elements.
The second element is a JSON schema of the data described by this field, and the first is a dictionary that contains definitions that are referenced from the schema.
Parameters: - role (str) – A role.
- ordered (bool) – If
True
, the resulting schema dictionary is ordered. Fields are listed in the order they are added to the class. Schema properties are also ordered in a sensible and consistent way, making the schema more human-readable. - res_scope (
ResolutionScope
) – The current resolution scope. - ref_documents (set) – If subclass of
Document
is in this set, allDocumentField
s pointing to it will be resolved to a reference:{"$ref": "#/definitions/..."}
. Note: resulting definitions will not contain schema for this document.
Raises: Return type: (dict, dict or OrderedDict)
-
iter_fields
()[source]¶ Iterates over the nested fields of the document examining all possible values of the occuring
resolvables
.
-
walk
(through_document_fields=False, visited_documents=frozenset([]))[source]¶ Iterates recursively over the nested fields, examining all possible values of the occuring
resolvables
.Visits fields in a DFS order.
Parameters: - through_document_fields (bool) – If
True
, walks through nestedDocumentField
fields. - visited_documents (set) – Keeps track of visited
documents
to avoid infinite recursion whenthrough_document_field
isTrue
.
Returns: iterable of
BaseField
- through_document_fields (bool) – If
-
resolve_and_iter_fields
(role='default')[source]¶ The same as
iter_fields()
, butresolvables
are resolved usingrole
.
-
resolve_and_walk
(role='default', through_document_fields=False, visited_documents=frozenset([]))[source]¶ The same as
walk()
, butresolvables
are resolved usingrole
.
-
get_schema
(ordered=False, role='default')[source]¶ Returns a JSON schema (draft v4) of the field.
Parameters: - role (str) – A role.
- ordered (bool) – If
True
, the resulting schema dictionary is ordered. Fields are listed in the order they are added to the class. Schema properties are also ordered in a sensible and consistent way, making the schema more human-readable.
Raises: Return type: dict or OrderedDict
-
resolve_attr
(attr, role='default')[source]¶ Resolves an attribure with the name
field
usingrole
.If the value of
attr
isresolvable
, it resolves it using a givenrole
and returns the result. Otherwise it returns the raw value androle
unchanged.Raises: AttributeError
Return type: Resolution
- required (bool or
-
class
jsl.fields.
BaseSchemaField
(id='', default=None, enum=None, title=None, description=None, **kwargs)[source]¶ A base class for fields that directly map to JSON Schema validator.
Parameters: - required (bool or
Resolvable
) – If the field is required. Defaults toFalse
. - id (str) – A string to be used as a value of the “id” keyword of the resulting schema.
- default (any JSON-representable object, a callable or a
Resolvable
) – The default value for this field. May beNull
(a special value to set the default value to null) or a callable. - enum (list, tuple, set, callable or
Resolvable
) – A list of valid choices. May be a callable. - title (str or
Resolvable
) – A short explanation about the purpose of the data described by this field. - description (str or
Resolvable
) – A detailed explanation about the purpose of the data described by this field.
-
id
= None¶ A string to be used as a value of the “id” keyword of the resulting schema.
-
title
= None¶ A short explanation about the purpose of the data.
-
description
= None¶ A detailed explanation about the purpose of the data.
- required (bool or
Roles¶
-
jsl.roles.
DEFAULT_ROLE
¶ A default role.
-
class
jsl.roles.
Resolution
(value, role)¶ A resolution result, a
namedtuple
.-
value
¶ A resolved value (the first element).
-
role
¶ A role to be used for visiting nested objects (the second element).
-
-
class
jsl.roles.
Resolvable
[source]¶ An interface that represents an object which value varies depending on a role.
-
resolve
(role)[source]¶ Returns a value for a given
role
.Parameters: role (str) – A role. Returns: A resolution
.
-
-
class
jsl.roles.
Var
(values=None, default=None, propagate=<function all_>)[source]¶ A
Resolvable
implementation.Parameters: - values (dict or list of pairs) –
A dictionary or a list of key-value pairs, where keys are matchers and values are corresponding values.
Matchers are callables returning boolean values. Strings and iterables are also accepted and processed as follows:
- A string
s
will be replaced with a lambdalambda r: r == s
; - An iterable
i
will be replaced with a lambdalambda r: r in i
.
- A string
- default – A value to return if all matchers returned
False
. - propagate (callable, string or iterable) – A matcher that determines which roles are to be propagated down
to the nested objects. Default is
all_
that matches all roles.
-
values
¶ A list of pairs (matcher, value).
-
propagate
¶ A matcher that determines which roles are to be propagated down to the nested objects.
-
iter_possible_values
()[source]¶ Implements the
Resolvable
interface.Yields non-
None
values fromvalues
.
-
resolve
(role)[source]¶ Implements the
Resolvable
interface.Parameters: role (str) – A role. Returns: A resolution
,which value is the first value which matcher returns
True
and the role is either a givenrole
(ifpropagate`
matcher returnsTrue
) orDEFAULT_ROLE
(otherwise).
- values (dict or list of pairs) –
-
class
jsl.roles.
Scope
(matcher)[source]¶ A scope consists of a set of fields and a matcher. Fields can be added to a scope as attributes:
scope = Scope('response') scope.name = StringField() scope.age = IntField()
A scope can then be added to a
Document
. During a document class construction process, fields of each of its scopes are added to the resulting class asvariables
which only resolve to fields when the matcher of the scope returnsTrue
.If two fields with the same name are assigned to different document scopes, the matchers of the corresponding
Var
will be the matchers of the scopes in order they were added to the class.Scope
can also be used as a context manager. At the moment it does not do anything and only useful as a syntactic sugar – to introduce an extra indentation level for the fields defined within the same scope.For example:
class User(Document): with Scope('db_role') as db: db._id = StringField(required=True) db.version = StringField(required=True) with Scope('response_role') as db: db.version = IntField(required=True)
Is an equivalent of:
class User(Document): db._id = Var([ ('db_role', StringField(required=True)) ]) db.version = Var([ ('db_role', StringField(required=True)) ('response_role', IntField(required=True)) ])
Parameters: matcher (callable, string or iterable) – A matcher. -
__matcher__
¶ A matcher.
-
Exceptions¶
-
class
jsl.exceptions.
SchemaGenerationException
(message)[source]¶ Raised when a valid JSON schema can not be generated from a JSL object.
Examples of such situation are the following:
- A
variable
resolves to an integer but aBaseField
expected; - All choices of
OneOfField
are variables and all resolve toNone
.
Note: this error can only happen if variables are used in a document or field description.
Parameters: message (str) – A message. -
message
= None¶ A message.
- A
Steps¶
Steps attached to a SchemaGenerationException
serve as a traceback
and help a user to debug the error in the document or field description.
-
class
jsl.exceptions.
Step
(entity, role='default')[source]¶ A step of the schema generation process that caused the error.
Parameters: - entity – An entity being processed.
- role (str) – A current role.
-
entity
= None¶ An entity being processed.
-
role
= None¶ A current role.
-
class
jsl.exceptions.
DocumentStep
(entity, role='default')[source]¶ Bases:
jsl.exceptions.Step
A step of processing a
document
.Parameters: - entity (subclass of
Document
) – An entity being processed. - role (str) – A current role.
- entity (subclass of
-
class
jsl.exceptions.
FieldStep
(entity, role='default')[source]¶ Bases:
jsl.exceptions.Step
A step of processing a
field
.Parameters: - entity (instance of
BaseField
) – An entity being processed. - role (str) – A current role.
- entity (instance of
-
class
jsl.exceptions.
AttributeStep
(entity, role='default')[source]¶ Bases:
jsl.exceptions.Step
A step of processing an attribute of a field.
entity
is the name of an attribute (e.g.,"properties"
,"additional_properties"
, etc.)Parameters: - entity (str) – An entity being processed.
- role (str) – A current role.
-
class
jsl.exceptions.
ItemStep
(entity, role='default')[source]¶ Bases:
jsl.exceptions.Step
A step of processing an item of an attribute.
entity
is either a key or an index (e.g., it can be"created_at"
if the current attribute isproperties
of aDictField
or0
if the current attribute isitems
of aArrayField
).Parameters: - entity (str or int) – An entity being processed.
- role (str) – A current role.
Resolution Scope¶
-
class
jsl.resolutionscope.
ResolutionScope
(base='', current='', output='')[source]¶ An utility class to help with translating
id
attributes offields
into JSON schema"id"
properties.Parameters: - base (str) – A URI, a resolution scope of the outermost schema.
- current (str) – A URI, a resolution scope of the current schema.
- output (str) – A URI, an output part (expressed by parent schema id properties) scope of the current schema.
-
base
¶ A resolution scope of the outermost schema.
-
current
¶ A resolution scope of the current schema.
-
output
¶ An output part (expressed by parent schema id properties) scope of the current schema.
-
replace
(current=None, output=None)[source]¶ Returns a copy of the scope with the
current
andoutput
scopes replaced.
-
alter
(field_id)[source]¶ Returns a pair, where the first element is the identifier to be used as a value for the
"id"
JSON schema field and the second is a newResolutionScope
to be used when visiting the nested fields of the field with idfield_id
.Return type: (str, ResolutionScope
)
-
jsl.resolutionscope.
EMPTY_SCOPE
¶ An empty
ResolutionScope
.
Changelog¶
0.2.3 2016-04-24¶
- Introduction of two new inheritance modes,
oneOf
andanyOf
, by Steven Seguin (issue #22).
0.2.2 2016-02-06¶
- Documentation fixes by mulhern <amulhern@redhat.com> (issue #17).
0.2.1 2015-11-23¶
- Fix a bug when referencing a recursive document using
DocumentField
withas_ref=True
produced circular references (issue #16).
0.2.0 2015-11-08¶
- Minor breaking change for the issue #15:
Document.resolve_and_iter_fields()
now iterates only over fields that are attached as attributes (fields specified in documentOptions
aspattern_properties
oradditional_properties
won’t be processed), and yields tuples of (field name, field).
0.1.5: 2015-10-22¶
- Fix a bug when using RECURSIVE_REFERENCE_CONSTANT under a scope caused infinite recursion (issue #14).
0.1.4: 2015-10-11¶
- Introduce inheritance modes.
0.1.3: 2015-08-12¶
0.1.2: 2015-06-12¶
- Allow specifying a null default value for fields (see
Null
value) by Nathan Hoad.
0.1.1: 2015-05-29¶
- Fix
Document.resolve_field()
method; - Allow specifying a resolvable as a
definition_id
(seedocument options
).
0.1.0: 2015-05-13¶
0.0.10: 2015-04-28¶
- Fix spelling of
exclusiveMinimum
by Keith T. Star.
0.0.9: 2015-04-10¶
- Introduce the
ordered
argument forget_schema()
that adds the ability to create more readable JSON schemas with ordered parameters.
0.0.7: 2015-03-11¶
- More subclassing-friendly
DocumentMeta
which allows to override methods for collecting document fields and options and choose a container class for storing options; - Various minor bugfixes.
0.0.5: 2015-03-01¶
- Python 3 support by Igor Davydenko.
Installation¶
$ pip install jsl