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
maxItemskeyword and where themaxLength, or not to forget to setadditionalPropertiesto 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.DictFieldexceptproperties(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
Truefor a role, it will be passed to nested documents. - inheritance_mode (str) –
An inheritance mode: one of
INLINE(default),ALL_OF,ANY_OF, orONE_OFNew 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
Optionsattribute (seeOptionsfor 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
Trueif 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
fieldusingrole.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(), butresolvablesare resolved usingrole.
-
classmethod
iter_fields()[source]¶ Iterates over the fields of the document, resolving its
resolvablesto all possible values.
-
classmethod
walk(through_document_fields=False, visited_documents=frozenset([]))[source]¶ Iterates recursively over the fields of the document, resolving occurring
resolvablesto their all possible values.Visits fields in a DFS order.
Parameters: - through_document_fields (bool) – If
True, walks through nestedDocumentFieldfields. - visited_documents (set) – Keeps track of visited
documentsto avoid infinite recursion whenthrough_document_fieldisTrue.
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
Documentis in this set, allDocumentFields 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 fieldss 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
optionsinto 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.NumberFieldAn 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.StringFieldAn email field.
-
class
jsl.fields.IPv4Field(pattern=None, format=None, min_length=None, max_length=None, **kwargs)[source]¶ Bases:
jsl.fields.primitive.StringFieldAn 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_CONSTANTor aDocumentsubclass. - 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
Resolvableresolving 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
BaseFieldorResolvable) – If the value ofitemsis a list or a tuple, and the array length is larger than the number of fields initems, then the additional items are described by theBaseFieldpassed 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 ->
BaseFieldorResolvable]) – A dictionary containing fields. - pattern_properties (dict[str ->
BaseFieldorResolvable]) – 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
BaseFieldorResolvable) – Describes properties that are not described by thepropertiesorpattern_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 ( BaseFieldorResolvable) – A field to negate.-
field= None¶
-
-
class
jsl.fields.OneOfField(fields, **kwargs)[source]¶ Parameters: fields (list[ BaseFieldorResolvable]) – A list of fields, exactly one of which describes the data.-
fields= None¶
-
-
class
jsl.fields.AnyOfField(fields, **kwargs)[source]¶ Parameters: fields (list[ BaseFieldorResolvable]) – A list of fields, at least one of which describes the data.-
fields= None¶
-
-
class
jsl.fields.AllOfField(fields, **kwargs)[source]¶ Parameters: fields (list[ BaseFieldorResolvable]) – 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
Resolvableinterface.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
documentschema properties.New in version 0.1.3.
-
name= None¶ Name
-
required= None¶ Whether the field is required.
-
resolve(role)[source]¶ Implements the
Resolvableinterface.Always returns a
Resolution(self, role).Return type: Resolution
-
iter_possible_values()[source]¶ Implements the
Resolvableinterface.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
Documentis in this set, allDocumentFields 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 nestedDocumentFieldfields. - visited_documents (set) – Keeps track of visited
documentsto avoid infinite recursion whenthrough_document_fieldisTrue.
Returns: iterable of
BaseField- through_document_fields (bool) – If
-
resolve_and_iter_fields(role='default')[source]¶ The same as
iter_fields(), butresolvablesare resolved usingrole.
-
resolve_and_walk(role='default', through_document_fields=False, visited_documents=frozenset([]))[source]¶ The same as
walk(), butresolvablesare 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
fieldusingrole.If the value of
attrisresolvable, it resolves it using a givenroleand returns the result. Otherwise it returns the raw value androleunchanged.Raises: AttributeErrorReturn 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
Resolvableimplementation.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
swill be replaced with a lambdalambda r: r == s; - An iterable
iwill 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
Resolvableinterface.Yields non-
Nonevalues fromvalues.
-
resolve(role)[source]¶ Implements the
Resolvableinterface.Parameters: role (str) – A role. Returns: A resolution,which value is the first value which matcher returns
Trueand 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 asvariableswhich 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
Varwill be the matchers of the scopes in order they were added to the class.Scopecan 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
variableresolves to an integer but aBaseFieldexpected; - All choices of
OneOfFieldare 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.StepA 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.StepA 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.StepA step of processing an attribute of a field.
entityis 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.StepA step of processing an item of an attribute.
entityis either a key or an index (e.g., it can be"created_at"if the current attribute ispropertiesof aDictFieldor0if the current attribute isitemsof 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
idattributes offieldsinto 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
currentandoutputscopes 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 newResolutionScopeto 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,
oneOfandanyOf, 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
DocumentFieldwithas_ref=Trueproduced 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 documentOptionsaspattern_propertiesoradditional_propertieswon’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
Nullvalue) 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
exclusiveMinimumby Keith T. Star.
0.0.9: 2015-04-10¶
- Introduce the
orderedargument forget_schema()that adds the ability to create more readable JSON schemas with ordered parameters.
0.0.7: 2015-03-11¶
- More subclassing-friendly
DocumentMetawhich 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