pax_global_header 0000666 0000000 0000000 00000000064 13210323354 0014506 g ustar 00root root 0000000 0000000 52 comment=6571d051014a3fc302ecd8d67191cd07a6973fdd
validictory-1.1.2/ 0000775 0000000 0000000 00000000000 13210323354 0014040 5 ustar 00root root 0000000 0000000 validictory-1.1.2/.coveragerc 0000664 0000000 0000000 00000000131 13210323354 0016154 0 ustar 00root root 0000000 0000000 [run]
omit = validictory/tests/*
[report]
exclude_lines =
if __name__ == .__main__.:
validictory-1.1.2/.gitignore 0000664 0000000 0000000 00000000100 13210323354 0016017 0 ustar 00root root 0000000 0000000 *.egg-info
*.pyc
dist
build
.tox
.coverage
.ropeproject
htmlcov
validictory-1.1.2/.travis.yml 0000664 0000000 0000000 00000000412 13210323354 0016146 0 ustar 00root root 0000000 0000000 language: python
python:
- "2.7"
- "3.4"
- "3.5"
- "3.6"
install: pip install pytest pytest-cov coveralls
script: py.test --cov validictory --cov-config=.coveragerc
after_success:
- coveralls
notifications:
email:
- dev@jamesturk.net
validictory-1.1.2/AUTHORS.txt 0000664 0000000 0000000 00000002276 13210323354 0015735 0 ustar 00root root 0000000 0000000 Initially derived from jsonschema, by Ian Lewis and Yusuke Muraoka.
* James Turk - port from jsonschema & additional features & maintenance
* Silas Sewell - Cosmetic fixes
* Filip Noetzel - lots of work towards being compatible with the draft-03 spec
* Kai Lautaportti - blank_by_default
* Filod Lin - fix for longs, validate as ints
* John Krauss - ignore additionalProperties with non-dicts
* Alexandre Conrad - IP address validator
* Michael Stephens - support for collections.Mapping
* Simon Weber - doc improvements
* Ahmed El-Hassany - fix for patternProperties on non-required fields
* Lowe Thiderman - improved error message for additionalProperties
* Alfredo Deza - improved SchemaError messages
* Arthur S. - fix to support Decimal in addition to float
* Jesse Printz - doc fix
* Nicola Iarocci - disallow_unknown_properties option
* Rhett Garber - addition of FieldValidationError
* Marc Abramowitz - fix for error message when data doesn't match one of several types, fix to allow dict-like types to validate as dicts
* Alon Horev - fix for disallow_unknown_properties bug
* Daniel Rech - support for default param
* Juan Menéndez & James Clemence - fix for patternProperties w/ additionalProperties
validictory-1.1.2/LICENSE.txt 0000664 0000000 0000000 00000002153 13210323354 0015664 0 ustar 00root root 0000000 0000000 Copyright (c) 2013 James Turk
Contains code from jsonschema 0.2, copyright 2008 Ian Lewis, Yusuke Muraoka
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
validictory-1.1.2/MANIFEST.in 0000664 0000000 0000000 00000000073 13210323354 0015576 0 ustar 00root root 0000000 0000000 include *.rst
include LICENSE.txt
include CHANGELOG.txt
validictory-1.1.2/README.rst 0000664 0000000 0000000 00000004541 13210323354 0015533 0 ustar 00root root 0000000 0000000 ===========
validictory
===========
:warning: **:warning: As of 2018 this library is deprecated, please consider using jsonschema (https://pypi.python.org/pypi/jsonschema) instead.**
.. image:: https://travis-ci.org/jamesturk/validictory.svg?branch=master
:target: https://travis-ci.org/jamesturk/validictory
.. image:: https://coveralls.io/repos/jamesturk/validictory/badge.png?branch=master
:target: https://coveralls.io/r/jamesturk/validictory
.. image:: https://img.shields.io/pypi/v/validictory.svg
:target: https://pypi.python.org/pypi/validictory
.. image:: https://readthedocs.org/projects/validictory/badge/?version=latest
:target: https://readthedocs.org/projects/validictory/?badge=latest
:alt: Documentation Status
A general purpose Python data validator.
Works with Python 2.7 and Python 3.3+
Schema format based on JSON Schema Proposal (http://json-schema.org)
Contains code derived from jsonschema, by Ian Lewis and Yusuke Muraoka.
Usage
=====
JSON documents and schema must first be loaded into a Python dictionary type
before it can be validated.
Parsing a simple JSON document::
>>> import validictory
>>>
>>> validictory.validate("something", {"type":"string"})
Parsing a more complex JSON document::
>>> import json
>>> import validictory
>>>
>>> data = json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]')
>>> schema = {
... "type":"array",
... "items":[
... {"type":"string"},
... {"type":"object",
... "properties":{
... "bar":{
... "items":[
... {"type":"string"},
... {"type":"any"},
... {"type":"number"},
... {"type":"integer"}
... ]
... }
... }
... }
... ]
... }
>>> validictory.validate(data,schema)
Catch ValueErrors to handle validation issues::
>>> import validictory
>>>
>>> try:
... validictory.validate("something", {"type":"string","minLength":15})
... except ValueError, error:
... print(error)
...
Length of value 'something' for field '_data' must be greater than or equal to 15
You can read more in the official documentation at `Read the Docs `_.
validictory-1.1.2/docs/ 0000775 0000000 0000000 00000000000 13210323354 0014770 5 ustar 00root root 0000000 0000000 validictory-1.1.2/docs/Makefile 0000664 0000000 0000000 00000011002 13210323354 0016422 0 ustar 00root root 0000000 0000000 # Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
help:
@echo "Please use \`make ' where is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/validictory.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/validictory.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/validictory"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/validictory"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
make -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
validictory-1.1.2/docs/changelog.rst 0000664 0000000 0000000 00000010767 13210323354 0017464 0 ustar 00root root 0000000 0000000 validictory changelog
=====================
1.1.2
-----
**2017-12-01**
* final release w/ deprecation notice
* use full path in RequiredFieldValidationError, thanks Marco Mariani
1.1.1
-----
**2017-04-04**
* fix for mutable default arguments from Nick Stefan
1.1.0
-----
**2016-10-16**
* added support for minProperties/maxProperties, thanks to Heiko Finzel
* stop manipulating sys.path in tests, thanks to Hartmut Goebel
* fix for SchemaError being raised within list, thanks to Seb James
* fix remove_unknown_properties not playing nice w/ patternProperties
1.0.2
-----
**2016-06-21**
* bugfix to support microseconds in datetime validation, thanks to Christopher Graham
1.0.1
-----
**2015-09-24**
* bugfix for fail_fast w/ type lists
1.0.0
-----
**2015-01-16**
* stable release
1.0.0a2
-------
**2014-07-15**
* ensure path to field is used in error
1.0.0a1
-------
**2014-07-10**
* fix TypeError from format validators
* some documentation fixes
* enum options are callable (from James McKinney)
* switch to py.test
* internal changes to how _validate and _error work
* initial work on fail_fast=False
* initial work on descriptive field names
0.9.3
-----
**2013-11-25**
* fix bad 0.9.2 release that didn't have a fix for invalid code from a PR
0.9.2
-----
**2013-11-25**
* fix from Marc Abramowitz for validating dict-like things as dicts
* fix for patternProperties from Juan Menéndez & James Clemence
* include implementation of "default" property from Daniel Rech
* drop official support for Python 3.2
* remove a test that relied on dict ordering
* updated docs from Mark Grandi
* fix where format validators were cleared (also Mark Grandi)
0.9.1
-----
**2013-05-23**
* fix for error message when data doesn't match one of multiple subtypes
* fix for disallow_unknown_properties
0.9.0
-----
**2013-01-19**
* remove optional and requires, deprecated in 0.6
* improved additionalProperties error message
* improved schema error message
* add long to utc-millisec validation
* accept Decimal where float is accepted
* add FieldValidationError so that field names can be retrieved from error
* a few Python 3 fixes
0.8.3
-----
**2012-03-13**
* bugfix for Python 3: fix regression from 0.8.1 in use of long
0.8.2
-----
**2012-03-09**
* doc improvements
* PEP8 nearly everything
* bugfix for patternProperties
* ip-address should have been a format, not a type, breaks
any code written depending on it in 0.8.1
0.8.1
-----
**2012-03-04**
* add GeoJSON example to docs
* allow longs in int/number validation
* ignore additionalProperties for non-dicts
* ip-address type validator
0.8.0
-----
**2012-01-26**
* validate_enum accepts any container type
* add support for Python 3
* drop support for Python 2.5 and earlier
0.7.2
-----
**2011-09-27**
* add blank_by_default argument
* more descriptive error message for list items
0.7.1
-----
**2011-05-03**
* PEP8 changes to code base
* fix for combination of format & required=False
* use ABCs to determine types in Python >= 2.6
0.7.0
-----
**2011-03-15**
* fix dependencies not really supporting lists
* add what might be the draft03 behavior for schema dependencies
* add Sphinx documentation
0.6.1
-----
**2011-01-21**
* bugfix for uniqueItems
0.6.0
-----
**2011-01-20**
* more draft-03 stuff: patternProperties, additionalItems, exclusive{Minimum,Maximum}, divisibleBy
* custom format validators
* treat tuples as lists
* replace requires with dependencies (deprecating requires)
* replace optional with required (deprecating optional)
* addition of required_by_default parameter
0.5.0
-----
**2011-01-13**
* blank false by default
* draft-03 stuff: uniqueItems, date formats
0.4.1
-----
**2010-08-27**
* test custom types
* optional defaults to False correctly
* remove raise_errors
* add value check in additionalProperties
0.4.0
-----
**2010-08-02**
* renamed to validictory
* removal of maxDecimal
* ignore unknown attributes
* differentiate between a schema error and a validation error
* filter through _error
* combine Items/Length checks
* modular type checking
* major test refactor
0.3.0
-----
**2010-07-29**
* took over abandoned json_schema code
* removal of interactive mode
* PEP 8 cleanup of source
* list/dict checks more flexible
* remove identity/options/readonly junk
validictory-1.1.2/docs/conf.py 0000664 0000000 0000000 00000015551 13210323354 0016276 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
#
# validictory documentation build configuration file, created by
# sphinx-quickstart on Wed Mar 9 12:09:00 2011.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'validictory'
copyright = u'2014, James Turk'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '1.1'
# The full version, including alpha/beta/rc tags.
release = '1.1.2'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'validictorydoc'
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'validictory.tex', u'validictory Documentation',
u'James Turk', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'validictory', u'validictory Documentation',
[u'James Turk'], 1)
]
validictory-1.1.2/docs/index.rst 0000664 0000000 0000000 00000002104 13210323354 0016626 0 ustar 00root root 0000000 0000000 validictory |release|
=====================
**As of 2018 this library is deprecated, please consider using jsonschema instead.**
Overview
--------
validictory is a general purpose Python data validator that allows validation of arbitrary Python data structures.
Schema format is based on the `JSON Schema proposal `_, so combined with :mod:`json` the
library is also useful as a validator for JSON data.
Contains code derived from `jsonschema `_ by Ian Lewis and Ysuke Muraoka.
Obtaining validictory
---------------------
Source is available from `GitHub `_.
The latest release is always available on `PyPI `_ and can be installed via `pip `_.
Documentation lives at `ReadTheDocs `_.
Contents
--------
.. toctree::
:maxdepth: 2
usage
validictory
changelog
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
validictory-1.1.2/docs/usage.rst 0000664 0000000 0000000 00000054325 13210323354 0016637 0 ustar 00root root 0000000 0000000 Using validictory
=================
**As of 2018 this library is deprecated, please consider using jsonschema instead.**
Normal use of validictory is as simple as calling :func:`validictory.validate`,
the only thing to learn is how to craft a schema.
Sample Usage
-------------
JSON documents and schema must first be loaded into a Python dictionary type
before it can be validated.
Parsing a simple JSON document::
>>> import validictory
>>> validictory.validate("roast beef", {"type":"string"})
Parsing a more complex JSON document::
>>> import json
>>> import validictory
>>> data = json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]')
>>> schema = {
... "type":"array",
... "items":[
... {"type":"string"},
... {"type":"object",
... "properties":{
... "bar":{
... "items":[
... {"type":"string"},
... {"type":"any"},
... {"type":"number"},
... {"type":"integer"}
... ]
... }
... }
... }
... ]
... }
>>> validictory.validate(data,schema)
Catch ValueErrors to handle validation issues::
>>> import validictory
>>> try:
... validictory.validate("short", {"type":"string","minLength":15})
... except ValueError, error:
... print error
...
Length of value 'short' for field '_data' must be greater than or equal to 15
For more example usage of all schema options check out the tests within
``validictory/tests``.
Schema Options
--------------
``type``
Validate that an item in the data is of a particular type.
If a list of values is provided then any of the specified types
will be accepted.
Provided value can be any combination of the following:
* ``string`` - str and unicode objects
* ``integer`` - ints
* ``number`` - ints and floats
* ``boolean`` - bools
* ``object`` - dicts
* ``array`` - lists and tuples
* ``null`` - None
* ``any`` - any type is acceptable
``properties``
List of validators for properties of the object.
In essence each item in the provided dict for properties is a sub-schema
applied against the property (if present) with the same name in the data.
::
# each key in the 'properties' option matches a key in the object that you are validating,
# and the value to each key in the 'properties' option is the schema to validate
# the value of the key in the JSON you are verifying.
data = json.loads(''' {"obj1": {"obj2": 12}}''' )
schema =
{
"type": "object",
"properties": {
"obj1": {
"type": "object",
"properties": {
"obj2": {
"type": "integer"
}
}
}
}
}
validictory.validate(data, schema)
``patternProperties``
Define a set of patterns that validate against subschemas.
Similarly to how ``properties`` works, any properties in the data that have
a name matching a particular pattern must validate against the provided
sub-schema.
::
data = json.loads('''
{
"one": "hello",
"two": "helloTwo",
"thirtyThree": 12
}''')
schema = {
"type": "object",
"properties": {
"one": {
"type": "string"
},
"two": {
"type": "string"
}
},
# each subkey of the 'patternProperties' option is a
# regex, and the value is the schema to validate
# all values whose keys match said regex.
"patternProperties": {
"^.+Three$": {
"type": "number"
}
}
}
``additionalProperties``
Schema for all additional properties not included in properties.
Can be ``False`` to disallow any additional properties not in
``properties``, or can be a sub-schema that all properties
not included in ``properties`` must match.
::
data = json.loads('''
{
"one": [12, 13],
"two": "hello",
"three": null,
"four": null
}''')
schema = {
"type": "object",
"properties": {
"one": {
"type": "array"
},
"two": {
"type": "string"
}
},
# this will match any keys that were not listed in 'properties'
"additionalProperties": {
"type": "null"
}
}
validictory.validate(data, schema)
``items``
Provide a schema or list of schemas to match against a list.
If the provided value is a schema object then every item in the list
will be validated against the given schema.
If the provided value is a list of schemas then each item in the list
must match the schema in the same position of the list. (extra items
will be validated according to ``additionalItems``)
::
# given a schema object, every list will be validated against it.
data = json.loads(''' {"results": [1, 2, 3, 4, 5]}''')
schema = {
"properties": {
"results": {
"items": {
"type": "integer"
}
}
}
}
validictory.validate(data, schema)
# given a list, each item in the list is matched against the schema
# at the same index. (entry 0 in the json will be matched against entry 0
# in the schema, etc)
dataTwo = json.loads(''' {"results": [1, "a", false, null, 5.3]} ''')
schemaTwo = {
"properties": {
"results": {
"items": [
{"type": "integer"},
{"type": "string"},
{"type": "boolean"},
{"type": "null"},
{"type": "number"}
]
}
}
}
validictory.validate(dataTwo, schemaTwo)
``additionalItems``
Used in conjunction with ``items``. If False then no additional items
are allowed, if a schema is provided then all additional items must
match the provided schema.
::
data = json.loads(''' {"results": [1, "a", false, null, null, null]} ''')
schema = {
"properties": {
"results": {
"items": [
{"type": "integer"},
{"type": "string"},
{"type": "boolean"}
],
# when using 'items' and providing a list (so that values in the list get validated
# by the schema at the same index), any extra values get validated using additionalItems
"additionalItems": {
"type": "null"
}
}
}
}
validictory.validate(data, schema)
``required``
If True, the property must be present to validate.
The default value of this parameter is set on the call to
:func:`~validictory.validate`. By default it is ``True``.
::
data = json.loads(''' {"one": 1, "two": 2}''')
schema = {
"type": "object",
"properties": {
"one": {
"type": "number",
},
"two": {
"type": "number",
},
# even though "three" is missing, it will pass validation
# because required = False
"three": {
"type": "number",
"required": False
}
}
}
validictory.validate(data, schema)
.. note:: If you are following the JSON Schema spec, this diverges from the
official spec as of v3. If you want to validate against v3 more
correctly, be sure to set ``required_by_default`` to False.
``dependencies``
Can be a single string or list of strings representing properties
that must exist if the given property exists.
For example::
schema = {"prop01": {"required":False},
"prop02": {"required":False, "dependencies":"prop01"}}
# would validate
{"prop01": 7}
# would fail (missing prop01)
{"prop02": 7}
``minimum`` and ``maximum``
If the value is a number (int or float), these methods will validate
that the values are less than or greater than the given minimum/maximum.
Minimum and maximum values are inclusive by default.
::
data = json.loads(''' {"result": 10, "resultTwo": 12}''')
schema = {
"properties": {
"result": { # passes
"minimum": 9,
"maximum": 10
},
"resultTwo": { # fails
"minimum": 13
}
}
}
``exclusiveMinimum`` and ``exclusiveMaximum``
If these values are present and set to True, they will modify the
``minimum`` and ``maximum`` tests to be exclusive.
::
data = json.loads(''' {"result": 10, "resultTwo": 12, "resultThree": 15}''')
schema = {
"properties": {
"result": { # fails, has to > 10
"exclusiveMaximum": 10
},
"resultTwo": { # fails, has to be > 12
"exclusiveMinimum": 12
},
"resultThree": { # passes
"exclusiveMaximum": 20,
"exclusiveMinimum": 14
}
}
}
``minItems``, ``minLength``, ``maxItems``, and ``maxLength``
If the value is a list or str, these will test the length of the list
or string.
There is no difference in implementation between the items/length variants.
::
data = json.loads(''' { "one": "12345", "two": "2345", "three": [1, 2, 3, 4, 5]} ''')
schema = {
"properties": {
"one": { # passes
"minLength": 4,
"maxLength": 6
},
"two": { # fails
"minLength": 6
},
"three": { # passes
"maxItems": 5
}
}
}
``uniqueItems``
Indicate that all attributes in a list must be unique.
::
data = json.loads(''' {"one": [1, 2, 3, 4], "two": [1, 1, 2]} ''')
schema = {
"properties": {
"one": { # passes
"uniqueItems": True
},
"two": { # fails
"uniqueItems": True
}
}
}
``pattern``
If the value is a string, this provides a regular expression that
the string must match to be valid.
::
data = json.loads(''' {"twentyOne": "21", "thirtyThree": "33"} ''')
schema = {
"properties": {
"thirtyThree": {
"pattern": "^33$"
}
}
}
``blank``
If False, validate that string values are not blank (the empty string).
The default value of this parameter is set when initializing
`SchemaValidator`. By default it is ``False``.
::
data = json.loads(''' {"hello": "", "testing": ""}''')
schema = {
"properties": {
"hello": {
"blank": True # passes
},
"testing": {
"blank": False # fails
}
}
}
``enum``
Provides an array that the value must match if present.
::
data = json.loads(''' {"today": "monday", "tomorrow": "something"}''')
dayList = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]
schema = {
"properties": {
"today": {
"enum": dayList # passes
},
"tomorrow": {
"enum": dayList # does not pass, 'something' is not in the enum.
}
}
}
``format``
Validate that the value matches a predefined format.
By default several formats are recognized:
* ``date-time``: 'yyyy-mm-ddhh:mm:ssZ'
* ``date``: 'yyyy-mm-dd'
* ``time``: 'hh:mm::ss'
* ``utc-millisec``: number of seconds since UTC
* ``ip-address``: IPv4 address, in dotted-quad string format (for example, '123.45.67.89')
formats can be provided as a dictionary (of type {"formatString": format_func} ) to the ``format_validators`` argument of
``validictory.validate``.
Custom formatting functions have the function signature ``format_func(validator, fieldname, value, format_option):``.
* ``validator`` is a reference to the SchemaValidator (or custom validator class if you passed one in for the ``validator_cls`` argument in ``validictory.validate``).
* ``fieldname`` is the name of the field whose value you are validating in the JSON.
* ``value`` is the actual value that you are validating
* ``format_option`` is the name of the format string that was provided in the JSON, useful if you have one format function for multiple format strings.
Here is an example of writing a custom format function to validate `UUIDs `_:
::
import json
import validictory
import uuid
data = json.loads(''' { "uuidInt": 117574695023396164616661330147169357159,
"uuidHex": "fad9d8cc11d64578bff327df93276964"}''')
schema = {
"title": "My test schema",
"properties": {
"uuidHex": {
"format": "uuid_hex"
},
"uuidInt": {
"format": "uuid_int"
}
}
}
def validate_uuid(validator, fieldname, value, format_option):
print("*********************")
print("validator:",validator)
print("fieldname:", fieldname)
print("value", value)
print("format_option", format_option)
print("*********************")
if format_option == "uuid_hex":
try:
uuid.UUID(hex=value)
except Exception as e:
raise validictory.FieldValidationError("Could not parse UUID \
from hex string %(uuidstr)s, reason: %(reason)s"
% {"uuidstr": value, "reason": e}, fieldname, value)
elif format_option == "uuid_int":
try:
uuid.UUID(int=value)
except Exception as e:
raise validictory.FieldValidationError("Could not parse UUID \
from int string %(uuidstr)s, reason: %(reason)s"
% {"uuidstr": value, "reason": e}, fieldname, value)
else:
raise validictory.FieldValidationError("Invalid format option for \
'validate_uuid': %(format)s" % format_option,
fieldName, value)
try:
formatdict = {"uuid_hex": validate_uuid, "uuid_int": validate_uuid}
validictory.validate(data, schema, format_validators=formatdict)
print("Successfully validated %(data)s!" % {"data": data})
except Exception as e2:
print("couldn't validate =( reason: %(reason)s" % {"reason": e})
``divisibleBy``
Ensures that the data value can be divided (without remainder) by a
given divisor (**not 0**).
::
data = json.loads('''{"value": 12, "valueTwo": 13} ''')
schema = {
"properties": {
"value": {
"divisibleBy": 2 # passes
},
"valueTwo": {
"divisibleBy": 2 # fails
}
}
}
``title`` and ``description``
These do no validation, but if provided must be strings or a
``~validictory.SchemaError`` will be raised.
::
data = json.loads(''' {"hello": "testing"}''')
schema = {
"title": "My test schema",
"properties": {
"hello": {
"type": "string",
"description": Make sure the 'hello' key is a string"
}
}
}
Examples
--------------
Using a Schema
..............
The schema can be either a deserialized JSON document or a literal python object
::
data = json.loads(''' {"age": 23, "name": "Steven"} ''')
# json string
schemaOne = json.loads(''' {"type": "object", "properties":
{"age": {"type": "integer"}, "name": {"type": "string"}}} ''')
# python object literal
schemaTwo = {"type": "object", "properties":
{"age": {"type": "integer"}, "name": {"type": "string"}}}
validictory.validate(data, schemaOne)
validictory.validate(data, schemaTwo)
Validating Using Builtin Types
...............................
::
data = json.loads('''
{
"name": "bob",
"age": 23,
"siblings": null,
"registeredToVote": false,
"friends": ["Jane", "Michael"],
"heightInInches": 70.2
} ''')
schema =
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "integer"
},
"siblings": {
"type": "null"
},
"registeredToVote": {
"type": "boolean"
},
"friends": {
"type": "array"
}
}
}
validictory.validate(data, schema)
the 'number' type can be used when you don't care what type the number is, or 'integer' if you want a non
floating point number
::
dataTwo = json.loads('''{"valueOne": 12} ''')
schemaTwo = { "properties": { "valueOne": { "type": "integer"}} }
validictory.validate(dataTwo, schemaTwo)
the 'any' type can be used to validate any type.
::
dataThree = json.loads(''' {"valueOne": 12, "valueTwo": null, "valueThree": "hello" }''')
schemaThree = {
"properties": {
"valueOne": {"type": "any"},
"valueTwo": {"type": "any"},
"valueThree": {"type": "any"}
}
}
validictory.validate(dataThree, schemaThree)
You can list multiple types as well.
::
dataFour = json.loads(''' {"valueOne": 12, "valueTwo": null}''')
schemaFour = {
"properties": {
"valueOne": {
"type": ["string", "number"]
},
"valueTwo": {
"type": ["null", "string"]
}
}
}
validictory.validate(dataFour, schemaFour)
Validating Nested Containers
............................
::
data = json.loads('''
{
"results": {
"xAxis": [
[0, 1],
[1, 3],
[2, 5],
[3, 1]
],
"yAxis": [
[0, "sunday"],
[1, "monday"],
[2, "tuesday"],
[3, "wednesday"]
]
}
} ''')
schema = {
"type": "object",
"properties": {
"results": {
"type": "object",
"properties": {
"xAxis": {
"type": "array",
"items": {
"type": "array",
# use a list of schemas, so that the the schema at index 0
# matches the item in the list at index 0, etc.
"items": [{"type": "number"}, {"type": "number"}]
}
},
"yAxis": {
"type": "array",
"items": {
"type": "array",
"items": [{"type": "number"}, {"type": "string"}]
}
}
}
}
}
}
validictory.validate(data, schema)
Specifying Custom Types
.......................
If a list is specified for the 'types' option, then you can specify a schema or multiple schemas
that each element in the list will be tested against. This also allows you to split up your
schema definition for ease of reading, or to share schema definitions between other schemas.
::
schema = {
"type": "object",
"properties": {
"foo_or_bar_list": {
"type": "array",
"items": {
"type": [
{"type": "object",
# foo definition
},
{"type": "object",
# bar definition
},
]
}
}
}
}
A common example of this is the GeoJSON spec, which allows for a geometry
collection to have a list of geometries (Point, MultiPoint, LineString,
MultiLineString, Polygon, MultiPolygon).
Simplified GeoJSON example::
# to simplify things we make a few subschema dicts
position = {
"type": "array",
"minItems": 2,
"maxItems": 3
}
point = {
"type": "object",
"properties": {
"type": {
"pattern": "Point"
},
"coordinates": {
"type": position
}
}
}
multipoint = {
"type": "object",
"properties": {
"type": {
"pattern": "MultiPoint"
},
"coordinates": {
"type": "array",
"minItems": 2,
"items": position
}
}
}
# the main schema
simplified_geojson_geometry = {
"type": "object",
"properties": {
"type": {
"pattern": "GeometryCollection"
},
# this defines an array ('geometries') that is a list of objects
# which conform to one of the schemas in the type list
"geometries": {
"type": "array",
"items": {"type": [point, multipoint]}
}
}
}
(thanks to Jason Sanford for bringing this need to my attention, see `his
blog post on validating GeoJSON `_)
validictory-1.1.2/docs/validictory.rst 0000664 0000000 0000000 00000000530 13210323354 0020051 0 ustar 00root root 0000000 0000000 validictory module
==================
**As of 2018 this library is deprecated, please consider using jsonschema instead.**
.. module:: validictory
validate
--------
.. autofunction:: validate
SchemaValidator
---------------
.. autoclass:: SchemaValidator
Exceptions
----------
.. autoclass:: ValidationError
.. autoclass:: SchemaError
validictory-1.1.2/setup.cfg 0000664 0000000 0000000 00000000034 13210323354 0015656 0 ustar 00root root 0000000 0000000 [bdist_wheel]
universal = 1
validictory-1.1.2/setup.py 0000775 0000000 0000000 00000002171 13210323354 0015556 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
from setuptools import setup, find_packages
from validictory import __version__
DESCRIPTION = "general purpose python data validator"
LONG_DESCRIPTION = open('README.rst').read()
setup(name='validictory',
version=__version__,
description=DESCRIPTION,
long_description=LONG_DESCRIPTION,
author='James Turk',
author_email='james.p.turk@gmail.com',
url='http://github.com/jamesturk/validictory',
license="MIT License",
platforms=["any"],
packages=find_packages(),
test_suite="validictory.tests",
classifiers=["Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Natural Language :: English",
"Operating System :: OS Independent",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Topic :: Software Development :: Libraries :: Python Modules",
],
)
validictory-1.1.2/tox.ini 0000664 0000000 0000000 00000000304 13210323354 0015350 0 ustar 00root root 0000000 0000000 [tox]
envlist = py27,pypy,py34,py35,flake8
[testenv]
commands = py.test
deps =
setuptools
pytest
[testenv:flake8]
deps = flake8
commands = flake8 validictory
[flake8]
max-line-length=99
validictory-1.1.2/validictory/ 0000775 0000000 0000000 00000000000 13210323354 0016371 5 ustar 00root root 0000000 0000000 validictory-1.1.2/validictory/__init__.py 0000775 0000000 0000000 00000005400 13210323354 0020504 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
from validictory.validator import (SchemaValidator, FieldValidationError, MultipleValidationError,
ValidationError, SchemaError)
__all__ = ['validate', 'SchemaValidator', 'FieldValidationError', 'MultipleValidationError',
'ValidationError', 'SchemaError']
__version__ = '1.1.2'
def validate(data, schema, validator_cls=SchemaValidator,
format_validators=None, required_by_default=True,
blank_by_default=False, disallow_unknown_properties=False,
apply_default_to_data=False, fail_fast=True,
remove_unknown_properties=False):
'''
Validates a parsed json document against the provided schema. If an
error is found a :class:`ValidationError` is raised.
If there is an issue in the schema a :class:`SchemaError` will be raised.
:param data: python data to validate
:param schema: python dictionary representing the schema (see
`schema format`_)
:param validator_cls: optional validator class (default is
:class:`SchemaValidator`)
:param format_validators: optional dictionary of custom format validators
:param required_by_default: defaults to True, set to False to make
``required`` schema attribute False by default.
:param disallow_unknown_properties: defaults to False, set to True to
disallow properties not listed in the schema definition
:param apply_default_to_data: defaults to False, set to True to modify the
data in case the schema definition includes a "default" property
:param fail_fast: defaults to True, set to False if you prefer to get
all validation errors back instead of only the first one
:param remove_unknown_properties: defaults to False, set to True to
filter out properties not listed in the schema definition. Only applies
when disallow_unknown_properties is False.
'''
v = validator_cls(format_validators, required_by_default, blank_by_default,
disallow_unknown_properties, apply_default_to_data, fail_fast,
remove_unknown_properties)
return v.validate(data, schema)
if __name__ == '__main__':
import sys
import json
if len(sys.argv) == 2:
if sys.argv[1] == "--help":
raise SystemExit("%s SCHEMAFILE [INFILE]" % (sys.argv[0],))
schemafile = open(sys.argv[1], 'rb')
infile = sys.stdin
elif len(sys.argv) == 3:
schemafile = open(sys.argv[1], 'rb')
infile = open(sys.argv[2], 'rb')
else:
raise SystemExit("%s SCHEMAFILE [INFILE]" % (sys.argv[0],))
try:
obj = json.load(infile)
schema = json.load(schemafile)
validate(obj, schema)
except ValueError as e:
raise SystemExit(e)
validictory-1.1.2/validictory/tests/ 0000775 0000000 0000000 00000000000 13210323354 0017533 5 ustar 00root root 0000000 0000000 validictory-1.1.2/validictory/tests/__init__.py 0000664 0000000 0000000 00000000000 13210323354 0021632 0 ustar 00root root 0000000 0000000 validictory-1.1.2/validictory/tests/test_apply_default.py 0000664 0000000 0000000 00000006122 13210323354 0023776 0 ustar 00root root 0000000 0000000 from unittest import TestCase
import validictory
def validate_with_apply_default_to_data(data, schema):
return validictory.validate(
data,
schema,
required_by_default=True,
apply_default_to_data=True
)
class TestItemDefaults(TestCase):
""" recognize a "default" keyword in a schema as a fallback for
missing properties as described in
http://json-schema.org/latest/json-schema-validation.html#anchor101
"""
def test_property_default_is_applied(self):
schema = {
"type": "object",
"properties": {
"foo": {
"default": "bar"
},
"baz": {
"type": "integer"
}
}
}
data = {'baz': 2}
validate_with_apply_default_to_data(data, schema)
# Note: data was changed!
self.assertEqual(data, {"foo": "bar", "baz": 2})
def test_property_default_denied_if_wrong_type_for_default(self):
schema = {
"type": "object",
"properties": {
"foo": {
"type": "integer",
"default": "bar"
}
}
}
data = {}
# from specification:
# "There are no restrictions placed on the value of this keyword."
# "It is RECOMMENDED that a default value be
# valid against the associated schema."
self.assertRaises(
validictory.SchemaError,
validate_with_apply_default_to_data,
data,
schema
)
# the original data is unchanged
self.assertEqual(data, {})
def test_property_default_with_wrong_default_raises_error_if_unused(self):
schema = {
"type": "object",
"properties": {
"foo": {
"type": "integer",
"default": "bar"
}
}
}
data = {'foo': 1}
# The SchemaError is still raised because the schema is still wrong
# even if the property is contained in the data
self.assertRaises(
validictory.SchemaError,
validate_with_apply_default_to_data,
data,
schema
)
# the original data is unchanged
self.assertEqual(data, {'foo': 1})
def test_property_default_has_different_memory_id(self):
schema = {
"type": "object",
"properties": {
"foo": {
"default": []
}
}
}
data = {}
validictory.validate(
data,
schema,
required_by_default=True,
apply_default_to_data=True
)
# correctly apply default
self.assertEqual({"foo":[]}, data)
# dont re-use the actual array from the schema
applied_default_id = id(data["foo"])
original_schema_id = id(schema["properties"]["foo"]["default"])
self.assertNotEqual(applied_default_id, original_schema_id)
validictory-1.1.2/validictory/tests/test_disallow_unknown_properties.py 0000664 0000000 0000000 00000007226 13210323354 0027024 0 ustar 00root root 0000000 0000000 from unittest import TestCase
import validictory
class TestDisallowUnknownProperties(TestCase):
def setUp(self):
self.data_simple = {"name": "john doe", "age": 42}
self.schema_simple = {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
}
self.data_complex = {
"inv_number": "123",
"rows": [
{
"sku": "ab-456",
"desc": "a description",
"price": 100.45
},
{
"sku": "xy-123",
"desc": "another description",
"price": 999.00
}
],
"data": {
"name": "john doe",
"age": 42
}
}
self.schema_complex = {
"type": "object",
"properties": {
"inv_number": {"type": "string"},
"rows": {
"type": "array",
"items": {
"type": "object",
"properties": {
"sku": {"type": "string"},
"desc": {"type": "string"},
"price": {"type": "number"}
}
},
},
"data": {
"type": (
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"hair": {
"type": "string"
}
}
},
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "number"
}
}
}
)
}
}
}
def test_disallow_unknown_properties_pass(self):
try:
validictory.validate(self.data_simple, self.schema_simple,
disallow_unknown_properties=True)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_disallow_unknown_properties_fail(self):
self.data_simple["sex"] = "male"
self.assertRaises(validictory.SchemaError, validictory.validate,
self.data_simple, self.schema_simple,
disallow_unknown_properties=True)
def test_disallow_unknown_properties_complex_pass(self):
try:
validictory.validate(self.data_complex, self.schema_complex,
disallow_unknown_properties=True)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_disallow_unknown_properties_complex_fail(self):
newrow = {"sku": "789", "desc": "catch me if you can", "price": 1,
"rice": 666}
self.data_complex["rows"].append(newrow)
self.assertRaises(validictory.SchemaError, validictory.validate,
self.data_complex, self.schema_complex,
disallow_unknown_properties=True)
validictory-1.1.2/validictory/tests/test_fail_fast.py 0000664 0000000 0000000 00000010174 13210323354 0023077 0 ustar 00root root 0000000 0000000 from unittest import TestCase
import validictory
class TestFailFast(TestCase):
def test_multi_error(self):
schema = {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
}
data = {"name": 2, "age": "fourty-two"}
# ensure it raises an error
self.assertRaises(validictory.ValidationError, validictory.validate,
data, schema, fail_fast=True)
# ensure it raises a MultiError
self.assertRaises(validictory.MultipleValidationError, validictory.validate,
data, schema, fail_fast=False)
# ensure that the MultiError has 2 errors
try:
validictory.validate(data, schema, fail_fast=False)
except validictory.MultipleValidationError as mve:
assert len(mve.errors) == 2
def test_multi_error_in_list(self):
schema = {
"type": "object",
"properties": {
"words": {"type": "array", "items": {"type": "string"}},
},
}
data = {"words": ["word", 32, 2.1, True]}
# ensure it raises an error
self.assertRaises(validictory.ValidationError, validictory.validate,
data, schema, fail_fast=True)
# ensure it raises a MultiError
self.assertRaises(validictory.MultipleValidationError, validictory.validate,
data, schema, fail_fast=False)
# ensure that the MultiError has 3 errors since 3 of the items were bad
try:
validictory.validate(data, schema, fail_fast=False)
except validictory.MultipleValidationError as mve:
assert len(mve.errors) == 3
def test_multi_error_with_format(self):
schema = {
"type": "object",
"properties": {
"date": {"type": "string", "format": "date"},
"time": {"type": "string", "format": "time"}
},
}
data = {"date": "2011-02-99", "time": "30:00:00"}
# ensure it raises an error
self.assertRaises(validictory.ValidationError, validictory.validate,
data, schema, fail_fast=True)
# ensure it raises a MultiError
self.assertRaises(validictory.MultipleValidationError, validictory.validate,
data, schema, fail_fast=False)
# ensure that the MultiError has 2 errors
try:
validictory.validate(data, schema, fail_fast=False)
except validictory.MultipleValidationError as mve:
assert len(mve.errors) == 2
def test_no_error_with_type_list(self):
schema = {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"sibling": {"type": ["string", "null"]}
},
}
data = {"name": "john doe", "age": 42, "sibling": None}
# this should not raise an error
validictory.validate(data, schema, fail_fast=True)
# and neither should this...fixed by dc78c
validictory.validate(data, schema, fail_fast=False)
def test_multi_error_with_type_list(self):
schema = {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"sibling": {"type": ["string", "null"]}
},
}
data = {"name": 2, "age": "fourty-two", "sibling": 0}
# ensure it raises an error
self.assertRaises(validictory.ValidationError, validictory.validate,
data, schema, fail_fast=True)
# ensure it raises a MultiError
self.assertRaises(validictory.MultipleValidationError, validictory.validate,
data, schema, fail_fast=False)
# ensure that the MultiError has 3 errors
try:
validictory.validate(data, schema, fail_fast=False)
except validictory.MultipleValidationError as mve:
assert len(mve.errors) == 3
validictory-1.1.2/validictory/tests/test_items.py 0000664 0000000 0000000 00000010664 13210323354 0022274 0 ustar 00root root 0000000 0000000 from unittest import TestCase
import pytest
import validictory
class TestItems(TestCase):
schema1 = {
"type": "array",
"items": {"type": "string"}
}
schema2 = {
"type": "array",
"items": [{"type": "integer"}, {"type": "string"}, {"type": "boolean"}]
}
schema3 = {
"type": "array",
"items": ({"type": "integer"}, {"type": "string"}, {"type": "boolean"})
}
def test_items_single_pass(self):
data = ["string", "another string", "mystring"]
data2 = ["JSON Schema is cool", "yet another string"]
try:
validictory.validate(data, self.schema1)
validictory.validate(data2, self.schema1)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_items_single_fail(self):
data = ["string", "another string", 1]
self.assertRaises(ValueError, validictory.validate, data, self.schema1)
def test_items_multiple_pass(self):
data = [1, "More strings?", True]
data2 = [12482, "Yes, more strings", False]
try:
validictory.validate(data, self.schema2)
validictory.validate(data2, self.schema2)
validictory.validate(tuple(data), self.schema3)
validictory.validate(tuple(data2), self.schema3)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_items_multiple_fail(self):
data = [1294, "Ok. I give up"]
data2 = [1294, "Ok. I give up", "Not a boolean"]
self.assertRaises(ValueError, validictory.validate, data, self.schema2)
self.assertRaises(ValueError, validictory.validate, data2,
self.schema2)
def test_items_descriptive_fail(self):
data = [1294]
try:
validictory.validate(data, self.schema1)
except ValueError as e:
# warning should mention list position
assert '[0]' in str(e)
class TestAdditionalItems(TestCase):
schema1 = {
"type": "array",
"items": [{"type": "integer"}, {"type": "string"},
{"type": "boolean"}],
"additionalItems": False
}
schema2 = {
"type": "array",
"items": [{"type": "integer"}, {"type": "string"},
{"type": "boolean"}],
"additionalItems": True
}
schema3 = {
"type": "array",
"items": [{"type": "integer"}, {"type": "string"},
{"type": "boolean"}],
"additionalItems": {"type": "number"}
}
schema4 = {
"type": "array",
"items": [{"type": "integer"}, {"type": "string"},
{"type": "boolean"}],
"additionalItems": {"type": ["number", "boolean"]}
}
def test_additionalItems_false_no_additional_items_pass(self):
data = [12482, "Yes, more strings", False]
try:
validictory.validate(data, self.schema1)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_additionalItems_false_additional_items_fail(self):
data = [12482, "Yes, more strings", False, "I don't belong here"]
pytest.raises(ValueError, validictory.validate, data, self.schema1)
def test_additionalItems_pass(self):
data = [12482, "Yes, more strings", False, ["I'm"],
{"also": "allowed!"}]
try:
validictory.validate(data, self.schema2)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_additionalItems_schema_pass(self):
data = [12482, "Yes, more strings", False, 13.37, 47.11]
try:
validictory.validate(data, self.schema3)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_additionalItems_schema_fail(self):
data = [12482, "Yes, more strings", False, 13.37, "I'm not allowed"]
self.assertRaises(ValueError, validictory.validate, data, self.schema3)
def test_additionalItems_multischema_pass(self):
data = [12482, "Yes, more strings", False, 13.37, 47.11, True, False]
try:
validictory.validate(data, self.schema4)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_additionalItems_multischema_fail(self):
data = [12482, "Yes, more strings", False, 13.37, True, "I'm not allowed"]
pytest.raises(ValueError, validictory.validate, data, self.schema4)
validictory-1.1.2/validictory/tests/test_other.py 0000664 0000000 0000000 00000006357 13210323354 0022300 0 ustar 00root root 0000000 0000000 from unittest import TestCase
import validictory
class TestSchemaErrors(TestCase):
def setUp(self):
self.valid_desc = {"description": "My Description for My Schema"}
self.invalid_desc = {"description": 1233}
self.valid_title = {"title": "My Title for My Schema"}
self.invalid_title = {"title": 1233}
# doesn't matter what this is
self.data = "whatever"
def test_description_pass(self):
try:
validictory.validate(self.data, self.valid_desc)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_description_fail(self):
self.assertRaises(ValueError, validictory.validate, self.data,
self.invalid_desc)
def test_title_pass(self):
try:
validictory.validate(self.data, self.valid_title)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_title_fail(self):
self.assertRaises(ValueError, validictory.validate, self.data,
self.invalid_title)
def test_invalid_type(self):
expected = "Type for field 'bar' must be 'dict', got: 'str'"
data = {'bar': False}
schema = {"type": "object", "required": True,
"properties": {"bar": "foo"}}
try:
validictory.validate(data, schema)
result = None
except Exception as e:
result = e.__str__()
self.assertEqual(expected, result)
class TestFieldValidationErrors(TestCase):
def setUp(self):
self.schema = {"type": "object", "required": True,
"properties": {"bar": {"type": "integer"}}}
self.data = {"bar": "faz"}
def test(self):
try:
validictory.validate(self.data, self.schema)
except validictory.FieldValidationError as e:
self.assertEqual(e.fieldname, "bar")
self.assertEqual(e.value, "faz")
else:
self.fail("No Exception")
def test_deep_required_error():
schema = {'type': 'object', 'properties': {'foo': {'type': 'object',
'properties': {'bar': {'type': 'integer', 'required': 'true'}}}}}
data = {'foo': {'baz': 3}}
try:
validictory.validate(data, schema)
except validictory.validator.RequiredFieldValidationError as e:
estr = str(e)
assert 'foo' in estr
assert 'bar' in estr
def test_deep_error():
schema = {'type': 'object', 'properties': {'foo': {'type': 'object', 'properties':
{'bar': {'type': 'array', 'items':
{'type': 'object', 'properties':
{'baz':
{'type': 'string',
'enum': ['a', 'b']}}}}}}}}
data = {'foo': {'bar': [{'baz': 'a'}, {'baz': 'x'}]}}
try:
validictory.validate(data, schema)
except validictory.FieldValidationError as e:
estr = str(e)
assert 'baz' in estr
assert 'foo' in estr
assert 'bar' in estr
assert '1' in estr
validictory-1.1.2/validictory/tests/test_properties.py 0000664 0000000 0000000 00000025424 13210323354 0023347 0 ustar 00root root 0000000 0000000 from unittest import TestCase
import validictory
class TestProperties(TestCase):
props = {
"prop01": {"type": "string"},
"prop02": {"type": "number", "required": False},
"prop03": {"type": "integer"},
"prop04": {"type": "boolean"},
"prop05": {
"type": "object",
"required": False,
"properties": {
"subprop01": {"type": "string"},
"subprop02": {"type": "string", "required": True}
}
}
}
schema = {"type": "object", "properties": props}
def test_properties1(self):
data = {
"prop01": "test",
"prop02": 1.20,
"prop03": 1,
"prop04": True,
"prop05": {
"subprop01": "test",
"subprop02": "test2",
}
}
try:
validictory.validate(data, self.schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_properties2(self):
data = {
"prop01": "test",
"prop02": 1.20,
"prop03": 1,
"prop04": True
}
try:
validictory.validate(data, self.schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_properties3(self):
data = {
"prop02": 1.60,
"prop05": {
"subprop01": "test"
}
}
self.assertRaises(ValueError, validictory.validate, data, self.schema)
class TestPatternProperties(TestCase):
schema = {'patternProperties': {'[abc]': {'type': 'boolean'}}}
def test_patternproperties_pass(self):
data = {'a': True}
try:
validictory.validate(data, self.schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_patternproperties_nonmatch(self):
data = {'a': True, 'd': 'foo'}
try:
validictory.validate(data, self.schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_patternproperties_nested(self):
schema = {'patternProperties': {'[abc]': {
'patternProperties': {'[abc]': {'type': 'boolean'}}
}}}
data = {'a': {'b': False}}
try:
validictory.validate(data, schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_patternproperties_fail_multiple(self):
data = {'a': True, 'b': False, 'c': 'foo'}
self.assertRaises(ValueError, validictory.validate, data, self.schema)
def test_patternproperties_fail(self):
data = {'a': 12}
self.assertRaises(ValueError, validictory.validate, data, self.schema)
def test_patternproperties_missing(self):
schema = {'properties': {'patprops': {
'required': False, 'type': 'object',
'patternProperties': {'[abc]': {'required': True,
'type': 'array'}}
}}}
data = {'id': 1}
try:
validictory.validate(data, schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
class TestMinMaxProperties(TestCase):
def test_min_properties_pass(self):
schema = {
'minProperties': 1,
}
data = {
'a': 1
}
try:
validictory.validate(data, schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_max_properties_pass(self):
schema = {
'maxProperties': 2,
}
data = {
'a': 1,
'b': 2,
}
try:
validictory.validate(data, schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_min_properties_fail(self):
schema = {
'minProperties': 2,
}
data = {
'a': 1
}
self.assertRaises(ValueError, validictory.validate, data, schema)
def test_max_properties_fail(self):
schema = {
'maxProperties': 1,
}
data = {
'a': 1,
'b': 2,
}
self.assertRaises(ValueError, validictory.validate, data, schema)
def test_min_properties_pass_nondict(self):
schema = {
'minProperties': 2,
}
data = 123
try:
validictory.validate(data, schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_max_properties_pass_nondict(self):
schema = {
'maxProperties': 1,
}
data = 123
try:
validictory.validate(data, schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
class TestAdditionalProperties(TestCase):
def test_no_properties(self):
schema = {"additionalProperties": {"type": "integer"}}
for x in [1, 89, 48, 32, 49, 42]:
try:
data = {"prop": x}
validictory.validate(data, schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
# failures
for x in [1.2, "bad", {"test": "blah"}, [32, 49], None, True]:
self.assertRaises(ValueError, validictory.validate, {"prop": x},
schema)
def test_with_properties(self):
schema = {
"properties": {
"prop1": {"type": "integer"},
"prop2": {"type": "string"}
},
"additionalProperties": {"type": ["string", "number"]}
}
for x in [1, "test", 48, "ok", 4.9, 42]:
try:
data = {
"prop1": 123,
"prop2": "this is prop2",
"prop3": x
}
validictory.validate(data, schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
# failures
for x in [{"test": "blah"}, [32, 49], None, True]:
data = {
"prop1": 123,
"prop2": "this is prop2",
"prop3": x
}
self.assertRaises(ValueError, validictory.validate, data, schema)
def test_with_pattern_properties(self):
schema = {
"patternProperties": {
"[a-c]": {
"type": "string"}
}, "additionalProperties": False,
}
data = {"a": "test"}
try:
validictory.validate(data, schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
# failures
data = {"d": "test"}
self.assertRaises(ValueError, validictory.validate, data, schema)
data = {"a": "test", "d": "test"}
self.assertRaises(ValueError, validictory.validate, data, schema)
def test_true(self):
schema = {"additionalProperties": True}
for x in [1.2, 1, {"test": "blah"}, [32, 49], None, True, "blah"]:
try:
validictory.validate({"prop": x}, schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_false(self):
schema = {"additionalProperties": False}
for x in ["bad", {"test": "blah"}, [32.42, 494242], None, True, 1.34]:
self.assertRaises(ValueError, validictory.validate, {"prop": x},
schema)
def test_false_with_type_string(self):
schema = {
"type": ["object", "string"],
"properties": {
"key": {"type": "string"}
},
"additionalProperties": False
}
for data in ["foobar", {'key': 'value'}]:
try:
validictory.validate(data, schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
# failures
for data in [['foo', 'bar'], None, True, {'roses': 'red'}]:
self.assertRaises(ValueError, validictory.validate, data, schema)
class TestDependencies(TestCase):
props = {
"prop01": {"type": "string", "required": False},
"prop02": {"type": "number", "required": False,
"dependencies": "prop01"}
}
schema = {"type": "object", "properties": props}
props_array = {
"prop01": {"type": "string", "required": False},
"prop02": {"type": "string", "required": False},
"prop03": {"type": "number", "required": False,
"dependencies": ["prop01", "prop02"]}
}
schema_array = {"type": "object", "properties": props_array}
def test_dependencies_pass(self):
data1 = {}
data2 = {"prop01": "test"}
data3 = {"prop01": "test", "prop02": 2}
data4 = {"prop01": "a", "prop02": "b", "prop03": 7}
try:
validictory.validate(data1, self.schema)
validictory.validate(data2, self.schema)
validictory.validate(data3, self.schema)
validictory.validate(data4, self.schema_array)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_dependencies_fail(self):
data1 = {"prop02": 2}
data2 = {"prop01": "x", "prop03": 7}
self.assertRaises(ValueError, validictory.validate, data1, self.schema)
self.assertRaises(ValueError, validictory.validate, data2,
self.schema_array)
class TestRequired(TestCase):
props = {
"prop_def": {"type": "string"},
"prop_opt": {"type": "number", "required": False},
"prop_req": {"type": "boolean", "required": True}
}
schema = {"type": "object", "properties": props}
def_and_req = {"prop_def": "test", "prop_req": False}
req_only = {"prop_req": True}
opt_only = {"prop_opt": 7}
def test_required_pass(self):
try:
# should pass if def and req are there
validictory.validate(self.def_and_req, self.schema)
# should pass if default is missing but req_by_default=False
validictory.validate(self.req_only, self.schema,
required_by_default=False)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_required_fail(self):
# missing required should always fail
self.assertRaises(ValueError, validictory.validate, self.opt_only,
self.schema)
self.assertRaises(ValueError, validictory.validate, self.opt_only,
self.schema, required_by_default=False)
# missing the default, fail if required_by_default=True
self.assertRaises(ValueError, validictory.validate, self.req_only,
self.schema)
validictory-1.1.2/validictory/tests/test_remove_unknown_properties.py 0000664 0000000 0000000 00000006060 13210323354 0026476 0 ustar 00root root 0000000 0000000 from unittest import TestCase
import validictory
from copy import deepcopy
class TestRemoveUnknownProperties(TestCase):
def setUp(self):
self.data_simple = {"name": "john doe", "age": 42}
self.schema_simple = {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
}
self.data_complex = {
"inv_number": "123",
"rows": [
{
"sku": "ab-456",
"desc": "a description",
"price": 100.45
},
{
"sku": "xy-123",
"desc": "another description",
"price": 999.00
}
]
}
self.schema_complex = {
"type": "object",
"properties": {
"inv_number": {"type": "string"},
"rows": {
"type": "array",
"items": {
"type": "object",
"properties": {
"sku": {"type": "string"},
"desc": {"type": "string"},
"price": {"type": "number"}
}
},
}
}
}
def test_remove_unknown_properties_pass(self):
extra_data = deepcopy(self.data_simple)
extra_data["sex"] = "male"
validictory.validate(extra_data, self.schema_simple,
remove_unknown_properties=True)
self.assertEqual(extra_data, self.data_simple)
def test_remove_unknown_properties_patternproperties(self):
schema = {
"type": "object",
"patternProperties": {
"[abc]": {"type": "boolean"},
},
"properties": {
"d": {"type": "boolean"},
}
}
orig_data = {'a': True, 'b': False, 'd': True}
data = deepcopy(orig_data)
validictory.validate(data, schema, remove_unknown_properties=True)
self.assertEqual(data, orig_data)
def test_remove_unknown_properties_complex_pass(self):
try:
validictory.validate(self.data_complex, self.schema_complex,
remove_unknown_properties=True)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_remove_unknown_properties_complex_fail(self):
extra_data = deepcopy(self.data_complex)
newrow_invalid = {"sku": "789", "desc": "catch me if you can", "price": 1,
"rice": 666}
newrow_valid = {"sku": "789", "desc": "catch me if you can", "price": 1}
extra_data["rows"].append(newrow_invalid)
validictory.validate(extra_data, self.schema_complex,
remove_unknown_properties=True)
self.data_complex["rows"].append(newrow_valid)
self.assertEqual(extra_data, self.data_complex)
validictory-1.1.2/validictory/tests/test_schema_schema.py 0000664 0000000 0000000 00000007307 13210323354 0023733 0 ustar 00root root 0000000 0000000 from unittest import TestCase
import validictory
schema = {
"$schema": "http://json-schema.org/draft-03/schema#",
"id": "http://json-schema.org/draft-03/schema#",
"type": "object",
"properties": {
"type": {
"type": ["string", "array"],
"items": {
"type": ["string", {"$ref": "#"}]
},
"uniqueItems": True,
"default": "any"
},
"properties": {
"type": "object",
"additionalProperties": {"$ref": "#"},
"default": {}
},
"patternProperties": {
"type": "object",
"additionalProperties": {"$ref": "#"},
"default": {}
},
"additionalProperties": {
"type": [{"$ref": "#"}, "boolean"],
"default": {}
},
"items": {
"type": [{"$ref": "#"}, "array"],
"items": {"$ref": "#"},
"default": {}
},
"additionalItems": {
"type": [{"$ref": "#"}, "boolean"],
"default": {}
},
"required": {
"type": "boolean",
"default": False
},
"dependencies": {
"type": "object",
"additionalProperties": {
"type": ["string", "array", {"$ref": "#"}],
"items": {
"type": "string"
}
},
"default": {}
},
"minimum": {
"type": "number"
},
"maximum": {
"type": "number"
},
"exclusiveMinimum": {
"type": "boolean",
"default": False
},
"exclusiveMaximum": {
"type": "boolean",
"default": False
},
"minItems": {
"type": "integer",
"minimum": 0,
"default": 0
},
"maxItems": {
"type": "integer",
"minimum": 0
},
"uniqueItems": {
"type": "boolean",
"default": False
},
"pattern": {
"type": "string",
"format": "regex"
},
"minLength": {
"type": "integer",
"minimum": 0,
"default": 0
},
"maxLength": {
"type": "integer"
},
"enum": {
"type": "array",
"minItems": 1,
"uniqueItems": True
},
"default": {
"type": "any"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"format": {
"type": "string"
},
"divisibleBy": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": True,
"default": 1
},
"disallow": {
"type": ["string", "array"],
"items": {
"type": ["string", {"$ref": "#"}]
},
"uniqueItems": True
},
"extends": {
"type": [{"$ref": "#"}, "array"],
"items": {"$ref": "#"},
"default": {}
},
"id": {
"type": "string",
"format": "uri"
},
"$ref": {
"type": "string",
"format": "uri"
},
"$schema": {
"type": "string",
"format": "uri"
}
},
"dependencies": {
"exclusiveMinimum": "minimum",
"exclusiveMaximum": "maximum"
},
"default": {}
}
class TestSchemaSchema(TestCase):
def test_schema(self):
validictory.validate(schema, schema, required_by_default=False)
validictory-1.1.2/validictory/tests/test_type.py 0000664 0000000 0000000 00000012417 13210323354 0022132 0 ustar 00root root 0000000 0000000 from unittest import TestCase
from decimal import Decimal
import datetime
import sys
import validictory
if sys.version_info[0] == 3:
unicode_str = '\u2603'
else:
unicode_str = unicode('snowman')
class TestType(TestCase):
def test_schema(self):
schema = {
"type": [
{"type": "array", "minItems": 10},
{"type": "string", "pattern": "^0+$"}
]
}
data1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
data2 = "0"
data3 = 1203
for x in [data1, data2]:
try:
validictory.validate(x, schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
self.assertRaises(ValueError, validictory.validate, data3, schema)
def _test_type(self, typename, valids, invalids):
for x in valids:
try:
validictory.validate(x, {"type": typename})
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
for x in invalids:
self.assertRaises(ValueError, validictory.validate, x,
{"type": typename})
def test_integer(self):
valid_ints = [1, -89, 420000]
invalid_ints = [1.2, "bad", {"test": "blah"}, [32, 49], None, True]
self._test_type('integer', valid_ints, invalid_ints)
def test_string(self):
valids = ["abc", unicode_str]
invalids = [1.2, 1, {"test": "blah"}, [32, 49], None, True]
self._test_type('string', valids, invalids)
def test_number(self):
valids = [1.2, -89.42, 48, -32, Decimal('25.25')]
invalids = ["bad", {"test": "blah"}, [32.42, 494242], None, True]
self._test_type('number', valids, invalids)
def test_boolean(self):
valids = [True, False]
invalids = [1.2, "False", {"test": "blah"}, [32, 49], None, 1, 0]
self._test_type('boolean', valids, invalids)
def test_UserDict_is_object(self):
# A UserDict (and similar classes) are not dicts, but they're dict-like
# and should be treated as objects
try:
# Python 2
from UserDict import UserDict
except ImportError:
# Python 3
from collections import UserDict
valids = [UserDict({"a": "b"})]
invalids = []
self._test_type('object', valids, invalids)
def test_object(self):
valids = [{"blah": "test"}, {"this": {"blah": "test"}}, {1: 2, 10: 20}]
invalids = [1.2, "bad", 123, [32, 49], None, True]
self._test_type('object', valids, invalids)
def test_array(self):
valids = [[1, 89], [48, {"test": "blah"}, "49", 42], (47, 11)]
invalids = [1.2, "bad", {"test": "blah"}, 1234, None, True]
self._test_type('array', valids, invalids)
def test_null(self):
valids = [None]
invalids = [1.2, "bad", {"test": "blah"}, [32, 49], 1284, True]
self._test_type('null', valids, invalids)
def test_any(self):
valids = [1.2, "bad", {"test": "blah"}, [32, 49], None, 1284, True]
self._test_type('any', valids, [])
def test_default(self):
# test default value (same as any really)
valids = [1.2, "bad", {"test": "blah"}, [32, 49], None, 1284, True]
for x in valids:
try:
validictory.validate(x, {})
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_multi(self):
types = ["null", "integer", "string"]
valids = [None, 42, "string"]
invalids = [1.2, {"test": "blah"}, [32, 49], True]
self._test_type(types, valids, invalids)
self._test_type(tuple(types), valids, invalids)
class TestDisallow(TestType):
def _test_type(self, typename, valids, invalids):
for x in invalids:
try:
validictory.validate(x, {"disallow": typename})
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
for x in valids:
self.assertRaises(ValueError, validictory.validate, x,
{"disallow": typename})
class DateValidator(validictory.validator.SchemaValidator):
def validate_type_date(self, value):
return isinstance(value, datetime.date)
def validate_type_datetime(self, value):
return isinstance(value, datetime.datetime)
class TestCustomType(TestCase):
def test_date(self):
self._test_type('date', [datetime.date.today()],
[2010, '2010'])
def test_datetime(self):
self._test_type('datetime', [datetime.datetime.now()],
[2010, '2010', datetime.date.today()])
def test_either(self):
self._test_type(['datetime', 'date'],
[datetime.date.today(), datetime.datetime.now()],
[2010, '2010'])
def _test_type(self, typename, valids, invalids):
validator = DateValidator()
for x in valids:
try:
validator.validate(x, {"type": typename})
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
for x in invalids:
self.assertRaises(ValueError, validator.validate, x,
{"type": typename})
validictory-1.1.2/validictory/tests/test_values.py 0000664 0000000 0000000 00000044132 13210323354 0022447 0 ustar 00root root 0000000 0000000 """
Tests that test the value of individual items
"""
from unittest import TestCase
import re
import validictory
class TestEnum(TestCase):
schema = {"enum": ["test", True, 123, ["???"]]}
schema2 = {"enum": ("test", True, 123, ["???"])}
def test_enum_pass(self):
data = ["test", True, 123, ["???"]]
try:
for item in data:
validictory.validate(item, self.schema)
validictory.validate(item, self.schema2)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_enum_blank(self):
blank_schema = {"enum": ("test", True, 123, ["???"]), "blank": True}
data = ["test", True, 123, ["???"], ""]
try:
for item in data:
validictory.validate(item, blank_schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_enum_fail(self):
data = "unknown"
self.assertRaises(ValueError, validictory.validate, data, self.schema)
class TestPattern(TestCase):
# match simplified regular expression for an e-mail address
schema = {"pattern":
"^[A-Za-z0-9][A-Za-z0-9\.]*@([A-Za-z0-9]+\.)+[A-Za-z0-9]+$"}
def test_pattern_pass(self):
data = "my.email01@gmail.com"
try:
validictory.validate(data, self.schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_pattern_pass_nonstring(self):
data = 123
try:
validictory.validate(data, self.schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_pattern_fail(self):
data = "whatever"
self.assertRaises(ValueError, validictory.validate, data, self.schema)
def test_regex_compiled(self):
data = "my.email01@gmail.com"
re_schema = {'pattern': re.compile(
"^[A-Za-z0-9][A-Za-z0-9\.]*@([A-Za-z0-9]+\.)+[A-Za-z0-9]+$")}
try:
validictory.validate(data, re_schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
data = "whatever"
self.assertRaises(ValueError, validictory.validate, data, re_schema)
def validate_format_contains_spaces(validator, fieldname, value,
format_option):
if ' ' in value:
return
raise validictory.FieldValidationError(
"Value %(value)r of field '%(fieldname)s' does not contain any spaces,"
"but it should" % locals(), fieldname, value)
def validate_format_dict_not_empty(validator, fieldname, value,
format_option):
if len(value.keys()) > 0:
return
raise validictory.FieldValidationError(
"Value %(value)r of field '%(fieldname)s' should not be an empty"
"dict" % locals(), fieldname, value)
class TestFormat(TestCase):
schema_datetime = {"format": "date-time"}
schema_date = {"format": "date"}
schema_time = {"format": "time"}
schema_utcmillisec = {"format": "utc-millisec"}
schema_ip = {"format": "ip-address"}
schema_spaces = {"format": "spaces"}
schema_non_empty_dict = {"type": "object", "format": "non-empty-dict"}
def test_format_datetime_without_microseconds_pass(self):
data = "2011-01-13T10:56:53Z"
try:
validictory.validate(data, self.schema_datetime)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_format_datetime_with_microseconds_pass(self):
data = "2011-01-13T10:56:53.0438Z"
try:
validictory.validate(data, self.schema_datetime)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_format_date_pass(self):
data = "2011-01-13"
try:
validictory.validate(data, self.schema_date)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_format_time_pass(self):
data = "10:56:53"
try:
validictory.validate(data, self.schema_time)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_format_utcmillisec_pass(self):
try:
validictory.validate(1294915735, self.schema_utcmillisec)
validictory.validate(1294915735.0, self.schema_utcmillisec)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_format_datetime_nonexisting_day_fail(self):
data = "2013-13-13T00:00:00Z"
self.assertRaises(ValueError, validictory.validate, data,
self.schema_datetime)
def test_format_datetime_feb29_fail(self):
data = "2011-02-29T00:00:00Z"
self.assertRaises(ValueError, validictory.validate, data,
self.schema_datetime)
def test_format_datetime_notutc_fail(self):
data = "2011-01-13T10:56:53+01: 00"
self.assertRaises(ValueError, validictory.validate, data,
self.schema_datetime)
def test_format_datetime_fail(self):
data = "whatever"
self.assertRaises(ValueError, validictory.validate, data,
self.schema_datetime)
def test_format_datetime_bad_type(self):
data = 3
self.assertRaises(ValueError, validictory.validate, data,
self.schema_datetime)
def test_format_date_fail(self):
data = "whatever"
self.assertRaises(ValueError, validictory.validate, data,
self.schema_date)
def test_format_time_fail(self):
data = "whatever"
self.assertRaises(ValueError, validictory.validate, data,
self.schema_time)
def test_format_utcmillisec_fail(self):
data = "whatever"
self.assertRaises(ValueError, validictory.validate, data,
self.schema_utcmillisec)
def test_format_utcmillisec_negative_fail(self):
data = -1
self.assertRaises(ValueError, validictory.validate, data,
self.schema_utcmillisec)
def test_format_ip_pass(self):
valids = ["0.0.0.0", "255.255.255.255"]
for ip in valids:
try:
validictory.validate(ip, self.schema_ip)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_format_ip_fail(self):
invalids = [1.2, "bad", {"test": "blah"}, [32, 49], 1284, True,
"-0.-0.-0.-0", "-1.-1.-1.-1", "256.256.256.256"]
for ip in invalids:
self.assertRaises(ValueError, validictory.validate, ip,
self.schema_ip)
def test_format_required_false(self):
schema = {
'type': 'object',
'properties': {
'startdate': {'type': 'string', 'format': 'date-time',
'required': False}
}
}
try:
validictory.validate({}, schema, required_by_default=False)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_format_custom_unregistered_pass(self):
data = 'No-spaces-here'
try:
# no custom validator installed, so no error
validictory.validate(data, self.schema_spaces)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_format_custom_instantiated_pass(self):
data = 'Here are spaces'
validator = validictory.SchemaValidator(
{'spaces': validate_format_contains_spaces})
try:
# validator installed, but data validates
validator.validate(data, self.schema_spaces)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_format_custom_registered_pass(self):
data = 'Here are spaces'
validator = validictory.SchemaValidator()
validator.register_format_validator('spaces',
validate_format_contains_spaces)
try:
# validator registered, but data validates
validator.validate(data, self.schema_spaces)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_format_custom_registered_fail(self):
data = 'No-spaces-here'
validator = validictory.SchemaValidator(
{'spaces': validate_format_contains_spaces})
# validator registered, but data does not conform
self.assertRaises(ValueError, validator.validate, data,
self.schema_spaces)
def test_format_non_empty_fail(self):
data = {}
validator = validictory.SchemaValidator(
{'non-empty-dict': validate_format_dict_not_empty})
self.assertRaises(ValueError, validator.validate, data,
self.schema_non_empty_dict)
class TestUniqueItems(TestCase):
schema = {"uniqueItems": True}
schema_false = {"uniqueItems": False}
def test_uniqueitems_pass(self):
data = [1, 2, 3]
try:
validictory.validate(data, self.schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_uniqueitems_pass_string(self):
data = ['1', '2', '3']
try:
validictory.validate(data, self.schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_uniqueitems_pass_nested_array(self):
'''
uniqueItems only applies for the array it was specified on and not to
all datastructures nested within.
'''
data = [[1, [5, 5]], [2, [5, 5]]]
try:
validictory.validate(data, self.schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_uniqueitems_pass_not_an_array(self):
data = 13 # it's pretty unique
try:
validictory.validate(data, self.schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_uniqueitems_pass_different_types(self):
data = [1, "1"]
try:
validictory.validate(data, self.schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_uniqueitems_false_pass(self):
data = [1, 1, 1]
try:
validictory.validate(data, self.schema_false)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_uniqueitems_fail(self):
data = [1, 1, 1]
self.assertRaises(ValueError, validictory.validate, data, self.schema)
def test_uniqueitems_fail_nested_arrays(self):
data = [[1, 2, 3], [1, 2, 3]]
self.assertRaises(ValueError, validictory.validate, data, self.schema)
def test_uniqueitems_fail_nested_objects(self):
data = [{'one': 1, 'two': 2}, {'one': 1, 'two': 2}]
self.assertRaises(ValueError, validictory.validate, data, self.schema)
def test_uniqueitems_fail_null(self):
data = [None, None]
self.assertRaises(ValueError, validictory.validate, data, self.schema)
class TestMaximum(TestCase):
props = {
"prop01": {"type": "number", "maximum": 10},
"prop02": {"type": "integer", "maximum": 20}
}
props_exclusive = {
"prop": {"type": "integer", "maximum": 20, "exclusiveMaximum": True},
}
schema = {"type": "object", "properties": props}
schema_exclusive = {"type": "object", "properties": props_exclusive}
def test_maximum_pass(self):
# Test less than
data1 = {"prop01": 5, "prop02": 10}
# Test equal
data2 = {"prop01": 10, "prop02": 20}
try:
validictory.validate(data1, self.schema)
validictory.validate(data2, self.schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_maximum_exclusive_pass(self):
# Test less than
data = {"prop": 19}
try:
validictory.validate(data, self.schema_exclusive)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_maximum_fail(self):
# Test number
data1 = {"prop01": 11, "prop02": 19}
# Test integer
data2 = {"prop01": 9, "prop02": 21}
self.assertRaises(ValueError, validictory.validate, data1, self.schema)
self.assertRaises(ValueError, validictory.validate, data2, self.schema)
def test_maximum_exclusive_fail(self):
# Test equal
data = {"prop": 20}
self.assertRaises(ValueError, validictory.validate, data,
self.schema_exclusive)
class TestMinimum(TestCase):
props = {
"prop01": {"type": "number", "minimum": 10},
"prop02": {"type": "integer", "minimum": 20}
}
props_exclusive = {
"prop": {"type": "integer", "minimum": 20, "exclusiveMinimum": True},
}
schema = {"type": "object", "properties": props}
schema_exclusive = {"type": "object", "properties": props_exclusive}
def test_minimum_pass(self):
# Test greater than
data1 = {"prop01": 21, "prop02": 21}
# Test equal
data2 = {"prop01": 10, "prop02": 20}
try:
validictory.validate(data1, self.schema)
validictory.validate(data2, self.schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_minimum_exclusive_pass(self):
# Test greater than
data = {"prop": 21}
try:
validictory.validate(data, self.schema_exclusive)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_minimum_fail(self):
# Test number
data1 = {"prop01": 9, "prop02": 21}
# Test integer
data2 = {"prop01": 10, "prop02": 19}
self.assertRaises(ValueError, validictory.validate, data1, self.schema)
self.assertRaises(ValueError, validictory.validate, data2, self.schema)
def test_minimum_exclusive_fail(self):
# Test equal
data = {"prop": 20}
self.assertRaises(ValueError, validictory.validate, data,
self.schema_exclusive)
class TestMinLength(TestCase):
schema = {"minLength": 4}
def test_minLength_pass(self):
# str-equal, str-gt, list-equal, list-gt
data = ['test', 'string', [1, 2, 3, 4], [0, 0, 0, 0, 0]]
try:
for item in data:
validictory.validate(item, self.schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_minLength_pass_nonstring(self):
# test when data is not a string
data1 = 123
try:
validictory.validate(data1, self.schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_minLength_fail(self):
# test equal
data = ["car", [1, 2, 3]]
for item in data:
self.assertRaises(ValueError, validictory.validate, data,
self.schema)
class TestMaxLength(TestCase):
schema = {"maxLength": 4}
def test_maxLength_pass(self):
# str-equal, str-lt, list-equal, list-lt
data = ["test", "car", [1, 2, 3, 4], [0, 0, 0]]
try:
for item in data:
validictory.validate(item, self.schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_maxLength_pass_nonstring(self):
# test when data is not a string
data1 = 12345
try:
validictory.validate(data1, self.schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_maxLength_fail(self):
data = ["string", [1, 2, 3, 4, 5]]
for item in data:
self.assertRaises(ValueError, validictory.validate, item,
self.schema)
class TestBlank(TestCase):
def test_blank_default_false(self):
schema = {
"type": "object",
"properties": {
"key": {
"type": "string",
"required": True,
}
}
}
try:
validictory.validate({"key": "value"}, {}, blank_by_default=False)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
self.assertRaises(ValueError, validictory.validate, {"key": ""},
schema)
def test_blank_default_true(self):
schema = {
"type": "object",
"properties": {
"key": {
"type": "string",
"required": True,
}
}
}
try:
validictory.validate({"key": ""}, schema, blank_by_default=True)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_blank_false(self):
schema = {"blank": False}
try:
validictory.validate("test", schema, blank_by_default=True)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
self.assertRaises(ValueError, validictory.validate, "", schema)
def test_blank_true(self):
try:
validictory.validate("", {"blank": True}, blank_by_default=False)
validictory.validate("test", {"blank": True},
blank_by_default=False)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
class TestDivisibleBy(TestCase):
schema = {'type': 'number', 'divisibleBy': 12}
schema0 = {'type': 'number', 'divisibleBy': 0}
def test_divisibleBy_pass(self):
data = 60
try:
validictory.validate(data, self.schema)
except ValueError as e:
self.fail("Unexpected failure: %s" % e)
def test_divisibleBy_fail(self):
data = 13
self.assertRaises(ValueError, validictory.validate, data, self.schema)
def test_divisibleBy_ZeroDivisionError_fail(self):
data = 60
self.assertRaises(ValueError, validictory.validate, data, self.schema0)
validictory-1.1.2/validictory/validator.py 0000775 0000000 0000000 00000070627 13210323354 0020747 0 ustar 00root root 0000000 0000000 import re
import sys
import copy
import socket
from datetime import datetime
from decimal import Decimal
from collections import Mapping, Container
if sys.version_info[0] == 3:
_str_type = str
_int_types = (int,)
else:
_str_type = basestring
_int_types = (int, long)
class SchemaError(ValueError):
""" errors encountered in processing a schema (subclass of :class:`ValueError`) """
class ValidationError(ValueError):
""" validation errors encountered during validation (subclass of :class:`ValueError`) """
class FieldValidationError(ValidationError):
"""
Validation error that refers to a specific field and has `fieldname` and `value` attributes.
"""
def __init__(self, message, fieldname, value, path=''):
message = "Value {0!r} for field '{1}' {2}".format(value, path, message)
super(FieldValidationError, self).__init__(message)
self.fieldname = fieldname
self.value = value
self.path = path
class DependencyValidationError(ValidationError):
"""
Validation error that refers to a missing dependency
"""
def __init__(self, message):
super(DependencyValidationError, self).__init__(message)
class RequiredFieldValidationError(ValidationError):
"""
Validation error that refers to a missing field
"""
def __init__(self, message):
super(RequiredFieldValidationError, self).__init__(message)
class MultipleValidationError(ValidationError):
def __init__(self, errors):
msg = "{0} validation errors:\n{1}".format(len(errors), '\n'.join(str(e) for e in errors))
super(MultipleValidationError, self).__init__(msg)
self.errors = errors
def _generate_datetime_validator(format_option, dateformat_string):
def validate_format_datetime(validator, fieldname, value, format_option):
try:
# Additions to support date-time with microseconds without breaking
# existing date-time validation.
# Microseconds will appear specifically separated by a period, as
# some variable length decimal number
# such as '2015-11-18T19:57:05.061Z' instead of
# '2015-11-18T19:57:05Z' Better would be to use
# strict_rfc3339 vs datetime.strptime though the user needs the
# package installed for the import to succeed.
# import strict_rfc3339
# assert strict_rfc3339.validate_rfc3339(value)
if format_option == 'date-time' and '.' in value:
datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ')
else:
datetime.strptime(value, dateformat_string)
except:
msg = "is not in '{format_option}' format"
raise FieldValidationError(msg.format(format_option=format_option), fieldname, value)
return validate_format_datetime
validate_format_date_time = _generate_datetime_validator('date-time', '%Y-%m-%dT%H:%M:%SZ')
validate_format_date = _generate_datetime_validator('date', '%Y-%m-%d')
validate_format_time = _generate_datetime_validator('time', '%H:%M:%S')
def validate_format_utc_millisec(validator, fieldname, value, format_option):
if not isinstance(value, _int_types + (float, Decimal)) or value <= 0:
msg = "is not a positive number"
raise FieldValidationError(msg, fieldname, value)
def validate_format_ip_address(validator, fieldname, value, format_option):
try:
# Make sure we expect "X.X.X.X" as socket.inet_aton() converts "1" to "0.0.0.1"
socket.inet_aton(value)
ip = len(value.split('.')) == 4
except:
ip = False
if not ip:
msg = "is not a ip-address"
raise FieldValidationError(msg, fieldname, value)
DEFAULT_FORMAT_VALIDATORS = {
'date-time': validate_format_date_time,
'date': validate_format_date,
'time': validate_format_time,
'utc-millisec': validate_format_utc_millisec,
'ip-address': validate_format_ip_address,
}
class SchemaValidator(object):
'''
Validator largely based upon the JSON Schema proposal but useful for
validating arbitrary python data structures.
:param format_validators: optional dictionary of custom format validators
:param required_by_default: defaults to True, set to False to make
``required`` schema attribute False by default.
:param blank_by_default: defaults to False, set to True to make ``blank``
schema attribute True by default.
:param disallow_unknown_properties: defaults to False, set to True to
disallow properties not listed in the schema definition
:param apply_default_to_data: defaults to False, set to True to modify the
data in case the schema definition includes a "default" property
:param fail_fast: defaults to True, set to False if you prefer to get
all validation errors back instead of only the first one
:param remove_unknown_properties: defaults to False, set to True to
filter out properties not listed in the schema definition. Only applies
when disallow_unknown_properties is False.
'''
def __init__(self, format_validators=None, required_by_default=True,
blank_by_default=False, disallow_unknown_properties=False,
apply_default_to_data=False, fail_fast=True,
remove_unknown_properties=False):
self._format_validators = {}
self._errors = []
# add the default format validators
for key, value in DEFAULT_FORMAT_VALIDATORS.items():
self.register_format_validator(key, value)
# register any custom format validators if they were provided
if format_validators:
for key, value in format_validators.items():
self.register_format_validator(key, value)
self.required_by_default = required_by_default
self.blank_by_default = blank_by_default
self.disallow_unknown_properties = disallow_unknown_properties
self.apply_default_to_data = apply_default_to_data
self.fail_fast = fail_fast
# disallow_unknown_properties takes precedence over remove_unknown_properties
self.remove_unknown_properties = remove_unknown_properties
def register_format_validator(self, format_name, format_validator_fun):
self._format_validators[format_name] = format_validator_fun
def get_default(self, value):
if isinstance(value, dict) or isinstance(value, list):
return copy.deepcopy(value)
return value
def validate_type_string(self, val):
return isinstance(val, _str_type)
def validate_type_integer(self, val):
return type(val) in _int_types
def validate_type_number(self, val):
return type(val) in _int_types + (float, Decimal,)
def validate_type_boolean(self, val):
return type(val) == bool
def validate_type_object(self, val):
return isinstance(val, Mapping) or (hasattr(val, 'keys') and hasattr(val, 'items'))
def validate_type_array(self, val):
return isinstance(val, (list, tuple))
def validate_type_null(self, val):
return val is None
def validate_type_any(self, val):
return True
def _error(self, desc, value, fieldname, exctype=FieldValidationError, path='', **params):
params['value'] = value
params['fieldname'] = fieldname
message = desc.format(**params)
if exctype == FieldValidationError:
err = FieldValidationError(message, fieldname, value, path)
elif exctype == DependencyValidationError or exctype == RequiredFieldValidationError:
err = exctype(message)
err.fieldname = fieldname
err.path = path
if self.fail_fast:
raise err
else:
self._errors.append(err)
def _validate_unknown_properties(self, schema, data, fieldname, patternProperties):
"""Raise a SchemaError when unknown fields are found."""
schema_properties = set(schema)
data_properties = set(data)
delta = data_properties - schema_properties
if self.disallow_unknown_properties and delta:
unknowns = ', '.join(['"{0}"'.format(x) for x in delta])
raise SchemaError('Unknown properties for field "{fieldname}": {unknowns}'.format(
fieldname=fieldname, unknowns=unknowns))
elif self.remove_unknown_properties:
patterns = patternProperties.keys() if patternProperties else []
if patterns:
delta = [f for f in delta if not any(re.match(p, f) for p in patterns)]
for unknown_field in delta:
del data[unknown_field]
def validate_type(self, x, fieldname, schema, path, fieldtype=None):
''' Validates that the fieldtype specified is correct for the given data '''
# We need to know if the field exists or if it's just Null
fieldexists = True
try:
value = x[fieldname]
except KeyError:
fieldexists = False
value = None
if fieldtype and fieldexists:
if isinstance(fieldtype, (list, tuple)):
# Match if type matches any one of the types in the list
datavalid = False
errorlist = []
for eachtype in fieldtype:
try:
# if fail_fast is False, _error will not rais an exception.
# need to monitor the _errors list as well
errors = self._errors[:]
self.validate_type(x, fieldname, eachtype, path, eachtype)
if len(self._errors) > len(errors):
# an exception was raised.
# remove the error from self.errors and raise it here
raise self._errors.pop()
datavalid = True
break
except (SchemaError, ValidationError) as err:
errorlist.append(err)
if not datavalid:
self._error("doesn't match any of {numsubtypes} subtypes in {fieldtype}; "
"errorlist = {errorlist!r}",
value, fieldname, path=path, numsubtypes=len(fieldtype),
fieldtype=fieldtype, errorlist=errorlist)
elif isinstance(fieldtype, dict):
try:
self.__validate(fieldname, x, fieldtype, path)
except ValueError as e:
raise e
else:
try:
type_checker = getattr(self, 'validate_type_' + fieldtype)
except AttributeError:
raise SchemaError("Field type '{0}' is not supported.".format(fieldtype))
if not type_checker(value):
self._error("is not of type {fieldtype}", value, fieldname, path=path,
fieldtype=fieldtype)
def validate_properties(self, x, fieldname, schema, path, properties=None):
''' Validates properties of a JSON object by processing the object's schema recursively '''
value = x.get(fieldname)
if value is not None:
if isinstance(value, dict):
if isinstance(properties, dict):
if self.disallow_unknown_properties or self.remove_unknown_properties:
self._validate_unknown_properties(properties, value, fieldname,
schema.get('patternProperties'))
for property in properties:
self.__validate(property, value, properties.get(property),
path + '.' + property)
else:
raise SchemaError("Properties definition of field '{0}' is not an object"
.format(fieldname))
def validate_items(self, x, fieldname, schema, path, items=None):
'''
Validates that all items in the list for the given field match the given schema
'''
if x.get(fieldname) is not None:
value = x.get(fieldname)
if isinstance(value, (list, tuple)):
if isinstance(items, (list, tuple)):
if 'additionalItems' not in schema and len(items) != len(value):
self._error("is not of same length as schema list", value, fieldname,
path=path)
else:
for index, item in enumerate(items):
try:
self.__validate("_data", {"_data": value[index]}, item,
'{0}[{1}]'.format(path, index))
except FieldValidationError as e:
raise type(e)("Failed to validate field '%s' list schema: %s" %
(fieldname, e), fieldname, e.value)
elif isinstance(items, dict):
for index, item in enumerate(value):
if ((self.disallow_unknown_properties or
self.remove_unknown_properties) and 'properties' in items):
self._validate_unknown_properties(items['properties'],
item,
fieldname,
schema.get('patternProperties'))
self.__validate("[list item]", {"[list item]": item}, items,
'{0}[{1}]'.format(path, index))
else:
raise SchemaError("Properties definition of field '{0}' is "
"not a list or an object".format(fieldname))
def validate_required(self, x, fieldname, schema, path, required):
''' Validates that the given field is present if required is True '''
# Make sure the field is present
if fieldname not in x and required:
self._error("Required field '{fieldname}' is missing", None, path, path=path,
exctype=RequiredFieldValidationError)
def validate_blank(self, x, fieldname, schema, path, blank=False):
''' Validates that the given field is not blank if blank=False '''
value = x.get(fieldname)
if isinstance(value, _str_type) and not blank and not value:
self._error("cannot be blank'", value, fieldname, path=path)
def validate_patternProperties(self, x, fieldname, schema, path, patternproperties=None):
if patternproperties is None:
patternproperties = {}
value_obj = x.get(fieldname, {})
for pattern, schema in patternproperties.items():
for key, value in value_obj.items():
if re.match(pattern, key):
self.__validate("_data", {"_data": value}, schema, path)
def validate_additionalItems(self, x, fieldname, schema, path, additionalItems=False):
value = x.get(fieldname)
if not isinstance(value, (list, tuple)):
return
if isinstance(additionalItems, bool):
if additionalItems or 'items' not in schema:
return
elif len(value) != len(schema['items']):
self._error("is not of same length as schema list", value, fieldname, path=path)
remaining = value[len(schema['items']):]
if len(remaining) > 0:
self.__validate("_data", {"_data": remaining}, {"items": additionalItems}, path)
def validate_additionalProperties(self, x, fieldname, schema, path, additionalProperties=None):
'''
Validates additional properties of a JSON object that were not
specifically defined by the properties property
'''
# Shouldn't be validating additionalProperties on non-dicts
value = x.get(fieldname)
if not isinstance(value, dict):
return
# If additionalProperties is the boolean value True then we accept
# any additional properties.
if isinstance(additionalProperties, bool) and additionalProperties:
return
value = x.get(fieldname)
if isinstance(additionalProperties, (dict, bool)):
properties = schema.get("properties")
patterns = schema["patternProperties"].keys() if 'patternProperties' in schema else []
if properties is None:
properties = {}
if value is None:
value = {}
for eachProperty in value:
if (eachProperty not in properties and not
any(re.match(p, eachProperty) for p in patterns)):
# If additionalProperties is the boolean value False
# then we don't accept any additional properties.
if additionalProperties is False:
self._error("contains additional property '{prop}' not defined by "
"'properties' or 'patternProperties' and additionalProperties "
" is False", value, fieldname, prop=eachProperty, path=path)
self.__validate(eachProperty, value, additionalProperties, path)
else:
raise SchemaError("additionalProperties schema definition for "
"field '{0}' is not an object".format(fieldname))
def validate_dependencies(self, x, fieldname, schema, path, dependencies=None):
if x.get(fieldname) is not None:
# handle cases where dependencies is a string or list of strings
if isinstance(dependencies, _str_type):
dependencies = [dependencies]
if isinstance(dependencies, (list, tuple)):
for dependency in dependencies:
if dependency not in x:
self._error("Field '{dependency}' is required by field '{fieldname}'",
None, fieldname, dependency=dependency, path=path,
exctype=DependencyValidationError)
elif isinstance(dependencies, dict):
# NOTE: the version 3 spec is really unclear on what this means
# based on the meta-schema I'm assuming that it should check
# that if a key exists, the appropriate value exists
for k, v in dependencies.items():
if k in x and v not in x:
self._error("Field '{k}' is required by field '{v}'", None, fieldname,
k=k, v=v, exctype=DependencyValidationError, path=path)
else:
raise SchemaError("'dependencies' must be a string, list of strings, or dict")
def validate_minimum(self, x, fieldname, schema, path, minimum=None):
''' Validates that the field is longer than or equal to the minimum length if specified '''
exclusive = schema.get('exclusiveMinimum', False)
if x.get(fieldname) is not None:
value = x.get(fieldname)
if value is not None:
if (type(value) in (int, float) and
(not exclusive and value < minimum) or
(exclusive and value <= minimum)):
self._error("is less than minimum value: {minimum}", value, fieldname,
minimum=minimum, path=path)
def validate_maximum(self, x, fieldname, schema, path, maximum=None):
'''
Validates that the field is shorter than or equal to the maximum length if specified.
'''
exclusive = schema.get('exclusiveMaximum', False)
if x.get(fieldname) is not None:
value = x.get(fieldname)
if value is not None:
if (type(value) in (int, float) and
(not exclusive and value > maximum) or
(exclusive and value >= maximum)):
self._error("is greater than maximum value: {maximum}", value, fieldname,
maximum=maximum, path=path)
def validate_maxProperties(self, x, fieldname, schema, path, number=None):
'''
Validates that the number of properties of the given object is less than or equal
to the specified number
'''
value = x.get(fieldname)
if isinstance(value, dict) and len(value) > number:
self._error("must have number of properties less than or equal to {number}",
value, fieldname, number=number, path=path)
def validate_minProperties(self, x, fieldname, schema, path, number=None):
'''
Validates that the number of properties of the given object is greater than or equal
to the specified number
'''
value = x.get(fieldname)
if isinstance(value, dict) and len(value) < number:
self._error("must have number of properties greater than or equal to {number}",
value, fieldname, number=number, path=path)
def validate_maxLength(self, x, fieldname, schema, path, length=None):
'''
Validates that the value of the given field is shorter than or equal
to the specified length
'''
value = x.get(fieldname)
if isinstance(value, (_str_type, list, tuple)) and len(value) > length:
self._error("must have length less than or equal to {length}", value, fieldname,
length=length, path=path)
def validate_minLength(self, x, fieldname, schema, path, length=None):
'''
Validates that the value of the given field is longer than or equal to the specified length
'''
value = x.get(fieldname)
if isinstance(value, (_str_type, list, tuple)) and len(value) < length:
self._error("must have length greater than or equal to {length}", value, fieldname,
length=length, path=path)
validate_minItems = validate_minLength
validate_maxItems = validate_maxLength
def validate_format(self, x, fieldname, schema, path, format_option=None):
'''
Validates the format of primitive data types
'''
value = x.get(fieldname, None)
format_validator = self._format_validators.get(format_option, None)
if format_validator and value is not None:
try:
format_validator(self, fieldname, value, format_option)
except FieldValidationError as fve:
if self.fail_fast:
raise
else:
self._errors.append(fve)
# TODO: warn about unsupported format ?
def validate_pattern(self, x, fieldname, schema, path, pattern=None):
'''
Validates that the given field, if a string, matches the given regular expression.
'''
value = x.get(fieldname)
if (isinstance(value, _str_type) and
(isinstance(pattern, _str_type) and not re.match(pattern, value)
or not isinstance(pattern, _str_type) and not pattern.match(value))):
self._error("does not match regular expression '{pattern}'", value, fieldname,
pattern=pattern, path=path)
def validate_uniqueItems(self, x, fieldname, schema, path, uniqueItems=False):
'''
Validates that all items in an array instance MUST be unique
(contains no two identical values).
'''
# If additionalProperties is the boolean value True then we accept
# any additional properties.
if isinstance(uniqueItems, bool) and not uniqueItems:
return
values = x.get(fieldname)
if not isinstance(values, (list, tuple)):
return
hashables = set()
unhashables = []
for value in values:
if isinstance(value, (list, dict)):
container, add = unhashables, unhashables.append
else:
container, add = hashables, hashables.add
if value in container:
self._error("is not unique", value, fieldname, path=path)
else:
add(value)
def validate_enum(self, x, fieldname, schema, path, options=None):
'''
Validates that the value of the field is equal to one of the specified option values
'''
value = x.get(fieldname)
if value is not None:
if callable(options):
options = options(x)
if not isinstance(options, Container):
raise SchemaError("Enumeration {0!r} for field '{1}' must be a container".format(
options, fieldname))
if value not in options:
if not(value == '' and schema.get('blank', self.blank_by_default)):
self._error("is not in the enumeration: {options!r}", value, fieldname,
options=options, path=path)
def validate_title(self, x, fieldname, schema, path, title=None):
if not isinstance(title, (_str_type, type(None))):
raise SchemaError("The title for field '{0}' must be a string".format(fieldname))
def validate_description(self, x, fieldname, schema, path, description=None):
if not isinstance(description, (_str_type, type(None))):
raise SchemaError("The description for field '{0}' must be a string".format(fieldname))
def validate_divisibleBy(self, x, fieldname, schema, path, divisibleBy=None):
value = x.get(fieldname)
if not self.validate_type_number(value):
return
if divisibleBy == 0:
raise SchemaError("'{0!r}' <- divisibleBy can not be 0".format(schema))
if value % divisibleBy != 0:
self._error("is not divisible by '{divisibleBy}'.", x.get(fieldname), fieldname,
divisibleBy=divisibleBy, path=path)
def validate_disallow(self, x, fieldname, schema, path, disallow=None):
'''
Validates that the value of the given field does not match the disallowed type.
'''
try:
self.validate_type(x, fieldname, schema, path, disallow)
except ValidationError:
return
self._error("is disallowed for field '{fieldname}'", x.get(fieldname), fieldname,
disallow=disallow, path=path)
def validate(self, data, schema):
'''
Validates a piece of json data against the provided json-schema.
'''
self.__validate("data", {"data": data}, schema, '')
if self._errors:
raise MultipleValidationError(self._errors)
def __validate(self, fieldname, data, schema, path):
if schema is not None:
if not isinstance(schema, dict):
raise SchemaError("Type for field '%s' must be 'dict', got: '%s'" %
(fieldname, type(schema).__name__))
add_required_rule = self.required_by_default and 'required' not in schema
add_not_blank_rule = not self.blank_by_default and 'blank' not in schema
if add_required_rule or add_not_blank_rule:
newschema = copy.copy(schema)
if add_required_rule:
newschema['required'] = self.required_by_default
if add_not_blank_rule:
newschema['blank'] = self.blank_by_default
else:
newschema = schema
# add default values first before checking for required fields
if self.apply_default_to_data and 'default' in schema:
try:
self.validate_type(x={'_ds': self.get_default(schema['default'])}, fieldname='_ds',
schema=schema,
fieldtype=schema['type'] if 'type' in schema else None,
path=path)
except FieldValidationError as exc:
raise SchemaError(exc)
if fieldname not in data:
data[fieldname] = self.get_default(schema['default'])
# iterate over schema and call all validators
for schemaprop in newschema:
validatorname = "validate_" + schemaprop
validator = getattr(self, validatorname, None)
if validator:
validator(data, fieldname, schema, path, newschema.get(schemaprop))
return data