Description
-----------
Python schema library agnostic from languages.
.. image:: https://img.shields.io/pypi/l/b3j0f.schema.svg
:target: https://pypi.python.org/pypi/b3j0f.schema/
:alt: License
.. image:: https://img.shields.io/pypi/status/b3j0f.schema.svg
:target: https://pypi.python.org/pypi/b3j0f.schema/
:alt: Development Status
.. image:: https://img.shields.io/pypi/v/b3j0f.schema.svg
:target: https://pypi.python.org/pypi/b3j0f.schema/
:alt: Latest release
.. image:: https://img.shields.io/pypi/pyversions/b3j0f.schema.svg
:target: https://pypi.python.org/pypi/b3j0f.schema/
:alt: Supported Python versions
.. image:: https://img.shields.io/pypi/implementation/b3j0f.schema.svg
:target: https://pypi.python.org/pypi/b3j0f.schema/
:alt: Supported Python implementations
.. image:: https://img.shields.io/pypi/wheel/b3j0f.schema.svg
:target: https://travis-ci.org/b3j0f/schema
:alt: Download format
.. image:: https://travis-ci.org/b3j0f/schema.svg?branch=master
:target: https://travis-ci.org/b3j0f/schema
:alt: Build status
.. image:: https://coveralls.io/repos/b3j0f/schema/badge.png
:target: https://coveralls.io/r/b3j0f/schema
:alt: Code test coverage
.. image:: https://img.shields.io/pypi/dm/b3j0f.schema.svg
:target: https://pypi.python.org/pypi/b3j0f.schema/
:alt: Downloads
.. image:: https://readthedocs.org/projects/b3j0fschema/badge/?version=master
:target: https://readthedocs.org/projects/b3j0fschema/?badge=master
:alt: Documentation Status
.. image:: https://landscape.io/github/b3j0f/schema/master/landscape.svg?style=flat
:target: https://landscape.io/github/b3j0f/schema/master
:alt: Code Health
Links
-----
- `Homepage`_
- `PyPI`_
- `Documentation`_
Installation
------------
pip install b3j0f.schema
Features
--------
This library provides an abstraction layer for manipulating schema from several languages.
The abstraction layer is a python object which can validate data (properties to validate are object attributes or dictionary items) and be dumped into a dictionary or specific language format.
Supported languages are:
- python
- json
- xsd
It is also possible to generate a schema from a dictionary. And validation rules are fully and easily customisable thanks to using a schema such as a property.
Example
-------
Data Validation
~~~~~~~~~~~~~~~
.. code-block:: python
from b3j0f.schema import build, validate
# json format with required subinteger property
resource = '{"title": "test", "properties": {"subname": {"type": "string", "default": "test"}}, {"subinteger": {"type": "integer"}}, "required": ["subinteger"]}'
Test = build(resource)
test = Test(subname='example')
assert test.subinteger == 0 # instanciation value
assert Test.subinteger.default == 0 # default value
assert test.subname == 'example' # instanciation value
assert Test.subname.default == 'test' # instanciation value
error = None
try:
test.subname = 2 # wrong setting because subname is not a string
except TypeError as error:
pass
assert error is not None
assert 'subname' in Test.getschemas()
validate(Test.subinteger, 1) # validate property
validate(test, {'subinteger': 1}) # validate dictionary
class Sub(object): # object to validate with required subinteger
subinteger = 1
validate(test, Sub) # validate an object with required subinteger
validate(test, Sub())
wrongvalues = [
'', # object without subinteger
{'subinteger': ''}, # wrong data type for subinteger
{} # dictionary without the required property subinteger
]
for wrongvalue in wrongvalues:
error = None
try:
validate(test, wrongvalues)
except TypeError as error:
pass
assert error is not None
Schema retrieving
~~~~~~~~~~~~~~~~~
.. code-block:: python
from b3j0f.schema import register, getbyname, getbyuuid, data2schema
assert getbyuuid(test.uuid) is None
assert test not in getbyname(test.name)
register(test)
assert test is getbyuuid(test.uuid)
assert test in getbyname(test.name)
schema = data2schema(2, name='vint') # get an integer schema with 2 such as a default value and name vint
assert schema.default == 2
assert schema.name == 'vint'
error = None
try:
schema.default = ''
except TypeError as error:
pass
assert error is not None
Schema definition
~~~~~~~~~~~~~~~~~
.. code-block:: python
from b3j0f.schema import Schema, updatecontent
@updatecontent # change public attributes/functionss to schemas
class Test(Schema):
subname = 'test' # specify inner schema such as a string schema with default value 'test'
subinteger = 1 # speciy inner schema sub as an integer with default value 1
test = Test()
test = Test(subname='example')
assert test.subname == 'example' # instanciation value
assert Test.subname.default == 'test' # instanciation value
assert test.subinteger == 1 # instanciation value
assert Test.subinteger.default == 1 # default value
error = None
try:
test.subname = 2 # wrong setting because subname is not a string
except TypeError as error:
pass
assert error is not None
assert 'subname' in Test.getschemas()
Complex Schema definition
~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
from b3j0f.schema import Schema, ThisSchema, RefSchema, build
from random import random
@build(foo=2) # transform a python class to a schema class with the additional property foo
class Test(object):
key = DynamicValue(lambda: random()) # generate a new key at each instanciation
subtest = ThisSchema(key=3.) # use this schema such as inner schema
ref = RefSchema() # ref is validated by this schema
assert issubclass(Test, Schema)
test1, test2 = Test(), Test()
# check foo
assert test1.foo == test2.foo == 2
# check key and subtest properties
assert test1.key != test2.key
assert test1.subtest.key == test2.subtest.key == 3.
# check ref
assert test1.ref is None
test1.ref = Test()
error = None
try:
test.ref = 2
except TypeError as error:
pass
assert error is not None
Function schema definition
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
from b3j0f.schema import FunctionSchema, ParamSchema, FloatSchema, BooleanSchema, StringSchema, ArraySchema, OneOfSchema
@data2schema
def test(a, b, c=2, d=None, e=None, f=None): # definition of a shema function. Parameter values and (function) types are defined in the signature and the docstring.
"""
:param float a: default 0.
:type b: bool
:type d: ints # list of int
:type e: list of str #: list of str
:type f: int,float #: one of (int, float)
:rtype: str
"""
return a, b, c
assert isinstance(test, FunctionSchema)
assert isinstance(test.params, ArraySchema)
assert isinstance(test.params[0], ParamSchema)
assert len(test.params) == 6
assert test.params[0].name == 'a'
assert test.params[0].mandatory == True
assert isinstance(test.params[0].ref, FloatSchema)
assert test.params[0].default is 0.
assert test.params[1].name == 'b'
assert isinstance(test.params[1].ref, BooleanSchema)
assert test.params[1].mandatory is True
assert test.params[1].default is False
assert test.params[2].name == 'c'
assert isinstance(test.params[2].ref, IntegerSchema)
assert test.params[2].mandatory is False
assert test.params[2].default is 2
assert test.params[3].name == 'd'
assert isinstance(test.params[3].ref, ArraySchema)
assert isinstance(test.params[3].ref.itemtype, IntegerSchema)
assert test.params[3].mandatory is False
assert test.params[3].default is None
assert test.params[4].name == 'e'
assert isinstance(test.params[4].ref, ArraySchema)
assert isinstance(test.params[4].ref.itemtype, StringSchema)
assert test.params[4].mandatory is False
assert test.params[4].default is None
assert test.params[5].name == 'f'
assert isinstance(test.params[5].ref, OneOfSchema)
assert isinstance(test.params[5].ref.schemas[0], IntegerSchema)
assert isinstance(test.params[5].ref.schemas[1], FloatSchema)
assert test.params[5].mandatory is False
assert test.params[5].default is None
assert test.rtype is StringSchema
assert test(1, 2) == 'test'
Generate a schema from a data
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
from b3j0f.schema import data2schema
data = { # data is a dict
'a': 1
}
schemacls = dict2schemacls(data, name='test')
assert isinstance(schemacls.a, IntegerSchema)
assert schemacls.a.default is 1
assert isinstance(schemacls.name, StringSchema)
assert schemacls.name.default == 'test'
validate(schemacls(), data)
class Test(object): # data is an object
a = 1
schemacls = dict2schemacls(data, name='test')
assert isinstance(schemacls.a, IntegerSchema)
assert schemacls.a.default is 1
assert isinstance(schemacls.name, StringSchema)
assert schemacls.name.default == 'test'
validate(schemacls(), Test)
validate(schemacls(), Test())
Schema property getting/setting/deleting customisation such as a property
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
class Test(Schema):
@Schema
def test(self):
self.op = 'get'
return getattr(self, '_test', 1)
@test.setter
def test(self, value):
self.op = 'set'
self._test = value
@test.deleter
def test(self):
self.op = 'del'
del self._test
test = Test()
# check getter
assert test.test == 1
assert test.op == 'get'
# check setter
test.test = 2
assert test.op == 'set'
assert test.test == 2
# check deleter
del test.test
assert test.op == 'del'
assert test.test == 1
Perspectives
------------
- wait feedbacks during 6 months before passing it to a stable version.
- Cython implementation.
Donation
--------
.. image:: https://liberapay.com/assets/widgets/donate.svg
:target: https://liberapay.com/b3j0f/donate
:alt: I'm grateful for gifts, but don't have a specific funding goal.
.. _Homepage: https://github.com/b3j0f/schema
.. _Documentation: http://b3j0fschema.readthedocs.org/en/master/
.. _PyPI: https://pypi.python.org/pypi/b3j0f.schema/