{ "info": { "author": "F-Secure Corporation", "author_email": "opensource@f-secure.com", "bugtrack_url": null, "classifiers": [ "Framework :: Pytest", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Testing" ], "description": ".. image:: https://img.shields.io/pypi/v/pytest-voluptuous.svg?style=flat\n :alt: PyPI Package latest release\n :target: https://pypi.python.org/pypi/pytest-voluptuous\n\n.. image:: https://img.shields.io/pypi/pyversions/pytest-voluptuous.svg?style=flat\n :alt: Supported versions\n :target: https://pypi.python.org/pypi/pytest-voluptuous\n\n.. image:: https://img.shields.io/pypi/implementation/pytest-voluptuous.svg?style=flat\n :alt: Supported implementations\n :target: https://pypi.python.org/pypi/pytest-voluptuous\n\n.. image:: https://img.shields.io/pypi/l/pytest-voluptuous.svg?style=flat\n :alt: License\n :target: https://pypi.python.org/pypi/pytest-voluptuous\n\n.. image:: https://travis-ci.org/F-Secure/pytest-voluptuous.svg?branch=master\n :target: https://travis-ci.org/f-secure/pytest-voluptuous\n :alt: Travis-CI\n\n.. image:: https://coveralls.io/repos/github/F-Secure/pytest-voluptuous/badge.svg?branch=master\n :target: https://coveralls.io/github/f-secure/pytest-voluptuous?branch=master\n :alt: Coveralls\n\n=================\npytest-voluptuous\n=================\n\nA `pytest `_ plugin for asserting data against\n`voluptuous `_ schema.\n\nCommon use case is to validate HTTP API responses (in your functional tests):\n\n.. code-block:: python\n\n import requests\n from pytest_voluptuous import S, Partial, Exact\n from voluptuous.validators import All, Length\n\n def test_pypi():\n resp = requests.get('https://pypi.python.org/pypi/pytest/json')\n assert S({\n 'info': Partial({\n 'package_url': 'http://pypi.python.org/pypi/pytest',\n 'platform': 'INVALID VALUE',\n 'description': Length(max=10),\n 'downloads': list,\n 'classifiers': dict,\n }),\n 'urls': int\n }) == resp.json()\n\nIf validation fails, comparison returns ``False`` and assert fails, printing error details::\n\n E AssertionError: assert failed to validation error(s):\n E - info.platform: not a valid value for dictionary value @ data[u'info'][u'platform']\n E - info.description: length of value must be at most 10 for dictionary value @ data[u'info'][u'description']\n E - info.downloads: expected list for dictionary value @ data[u'info'][u'downloads']\n E - info.classifiers: expected dict for dictionary value @ data[u'info'][u'classifiers']\n E - urls: expected int for dictionary value @ data[u'urls']\n\nInstall\n=======\n\nWorks on python 2.7 and 3.4+::\n\n pip install pytest-voluptuous\n\nChangelog\n=========\n\nSee `CHANGELOG `_.\n\nFeatures\n========\n\n- Provides **utility schemas** (``S``, ``Exact`` and ``Partial``) to cut down boilerplate.\n- Implement a **pytest hook** to provide error details on ``assert`` failure.\n- Print descriptive validation **failure messages**.\n- ``Equal`` and ``Unordered`` validators (contributed to voluptuous project, available in 0.10+).\n\nWhy?\n====\n\nBecause writing:\n\n >>> r = {'info': {'package_url': 'http://pypi.python.org/pypi/pytest'}}\n >>> assert 'info' in r\n >>> assert 'package_url' in r['info']\n >>> assert r['info']['package_url'] == 'http://pypi.python.org/pypi/pytest'\n\n...is just *way* too annoying.\n\nWhy not `JSON schema `_? It's **too verbose**, too inconvenient. JSON schema will never\nmatch the convenience of a validation library that can utilize the goodies of the platform.\n\nWhy voluptuous and not some other library? No special reason - but it's pretty easy to use and understand. Also, the\nsyntax is quite compact.\n\nUsage\n=====\n\nIntro\n-----\n\nStart by specifying a schema:\n\n >>> from pytest_voluptuous import S, Partial, Exact\n >>> from voluptuous.validators import All, Length\n >>> schema = S({\n ... 'info': Partial({\n ... 'package_url': 'http://pypi.python.org/pypi/pytest',\n ... 'platform': 'unix',\n ... 'description': Length(min=100),\n ... 'downloads': dict,\n ... 'classifiers': list,\n ... }),\n ... 'urls': list\n ... })\n\nThen load up the data to validate:\n\n >>> import requests\n >>> data = requests.get('https://pypi.python.org/pypi/pytest/json').json()\n\nNow if you assert this, the data will be validated against the schema, but instead of raising an error, the comparison\nwill just evaluate to ``False`` which fails the assert:\n\n >>> assert data == schema\n Traceback (most recent call last):\n ...\n AssertionError\n\nNow getting ``AssertionError`` in case the data doesn't match the schema is not very nice but don't worry - there's\nno pytest magic in play here but once you run through pytest you'll rather get::\n\n E AssertionError: assert failed to validation error(s):\n E - info.platform: not a valid value for dictionary value @ data[u'info'][u'platform']\n E - info.description: length of value must be at most 10 for dictionary value @ data[u'info'][u'description']\n E - info.downloads: expected list for dictionary value @ data[u'info'][u'downloads']\n E - info.classifiers: expected dict for dictionary value @ data[u'info'][u'classifiers']\n E - urls: expected int for dictionary value @ data[u'urls']\n\nDetails\n-------\n\nUse ``==`` operator to do exact validation:\n\n >>> data = {'foo': 1, 'bar': True}\n >>> S({'foo': 1, 'bar': True}) == data\n True\n\nWe omit ``assert`` in these examples (for easier doctesting).\n\nUse ``<=`` to do *partial* validation (to allow extra keys, that is):\n\n >>> S({'foo': 1}) == data # not valid\n False\n >>> S({'foo': 1}) <= data # valid\n True\n\nThe operator you choose gets inherited, so with test data of:\n\n >>> data = {\n ... 'outer1': {\n ... 'inner1': 1,\n ... 'inner2': True\n ... },\n ... 'outer2': 'foo'\n ... }\n\nWith ``==`` you must provide exact value *also in nested context*:\n\n >>> S({\n ... 'outer1': {\n ... 'inner1': 1, # this would be valid but...\n ... # missing 'inner2'\n ... },\n ... 'outer2': 'foo'\n ... }) == data\n False\n >>> S({\n ... 'outer1': {\n ... 'inner1': int, # exact/partial matching\n ... 'inner2': bool # is for keys only\n ... },\n ... 'outer2': 'foo'\n ... }) == data\n True\n\n``<=`` implies partial matching:\n\n >>> S({\n ... 'outer1': {\n ... 'inner1': int,\n ... # 'inner2' missing but that's ok\n ... },\n ... # 'outer2' is missing too\n ... }) <= data\n True\n\nWhen you need to mix and match operators, you can loosen matching with ``Partial``:\n\n >>> S({\n ... 'outer1': Partial({\n ... 'inner1': int\n ... # 'inner2' ok to omit as scope is partial\n ... }),\n ... 'outer2': 'foo' # can't be missing as outer scope is exact\n ... }) == data\n True\n\nAnd stricten with ``Exact``:\n\n >>> S({\n ... 'outer1': Exact({\n ... 'inner1': int,\n ... 'inner2': bool\n ... }),\n ... # 'outer2' can be missing as outer scope is partial\n ... }) <= data\n True\n\nRemember, matching mode is inherited, so you may end up doing stuff like this:\n\n >>> data['outer1']['inner1'] = {'prop': 1}\n >>> S({\n ... 'outer1': Partial({\n ... 'inner1': Exact({\n ... 'prop': 1\n ... })\n ... }),\n ... 'outer2': 'foo'\n ... }) == data\n True\n\nThere is no ``>=``. If you want to declare *schema keys that may be missing*, use ``Optional``:\n\n >>> from voluptuous.schema_builder import Optional\n >>> S({Optional('foo'): str}) == {'extra': 1}\n False\n >>> S({'foo': str}) == {}\n False\n >>> S({'foo': str}) <= {}\n False\n >>> S({Optional('foo'): str}) == {}\n True\n >>> S({Optional('foo'): str}) <= {'extra': 1}\n True\n\nOr, if you want to make all keys optional, override ``required``:\n\n >>> from voluptuous.schema_builder import Required\n >>> S({'foo': str}, required=False) == {}\n True\n\nIn these cases, if you want to *require* a key:\n\n >>> S({'foo': str, Required('bar'): 1}, required=False) == {}\n False\n >>> S({'foo': str, Required('bar'): 1}, required=False) == {'bar': 1}\n True\n\nThat's it. For available validators, look into `voluptuous docs `_.\n\nGotchas\n=======\n\n**Voluptuous 0.9.3 and earlier:**\n\nIn voluptuous pre-0.10.2 ``[]`` matches *any* list, not an empty list. To declare an empty list, use ``Equal([])``.\n\nSimilarly, in voluptuous pre-0.10.2, ``{}`` doesn't *always* match an empty dict. If you're inside a\n``Schema({...}, extra=PREVENT_EXTRA)`` (or ``Exact``), ``{}`` does indeed match exactly ``{}``. However, inside\n``Schema({...}, extra=ALLOW_EXTRA) (or ``Partial``), it matches *any* dict (because any extra keys are allowed).\nTo declare an empty dict, use ``Equal({})``.\n\n**Voluptuous 0.10.0+:**\n\nIn voluptuous 0.10.0+ ``{}`` and ``[]`` evaluate as *empty* dict and *empty* list, so you don't need above workarounds.\n\nAlways use ``dict`` and ``list`` to validate dict or list of any size. It works despite voluptuous version.\n\n**Any version:**\n\n``[str, int]`` matches any list that contains both strings and ints (in any order and 1-n times). To validate\na list of fixed length with those types in it, use ``ExactSequence([str, int])`` and ``Unordered([str, int])``\nwhen the order has no meaning. You can also use values inside these as in ``ExactSequence([2, 3])``.\n\nLicense\n=======\n\nApache 2.0 licensed. See `LICENSE `_ for more details.\n\nChangelog\n=========\n\n1.1.0 (2018-10-31)\n------------------\n\n**New**:\n\n- `#3 `_:\n Include actual value in error messages for easier debugging (and remove duplication of error path in error message).\n Thanks `@Turbo87 `_!\n\n**Fix**:\n\n- `Commit `_:\n Skip path prefix in error output, if path is empty (when error is on \"main level\").\n Thanks `@Turbo87 `_!\n\n1.0.2 (2018-02-16)\n------------------\n\n**Fix**:\n\n- `#1 `_:\n Error reporting failed on lists.\n Thanks `@rytilahti `_!\n\n1.0.1 (2017-01-10)\n------------------\n\nFirst public version.\n\n1.0.0 (2016-12-07)\n------------------\n\nFirst version.\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/f-secure/pytest-voluptuous", "keywords": "", "license": "ASL 2.0", "maintainer": "", "maintainer_email": "", "name": "pytest-voluptuous", "package_url": "https://pypi.org/project/pytest-voluptuous/", "platform": "", "project_url": "https://pypi.org/project/pytest-voluptuous/", "project_urls": { "Homepage": "https://github.com/f-secure/pytest-voluptuous" }, "release_url": "https://pypi.org/project/pytest-voluptuous/1.1.0/", "requires_dist": [ "pytest", "voluptuous (>=0.9.0)" ], "requires_python": "", "summary": "Pytest plugin for asserting data against voluptuous schema.", "version": "1.1.0" }, "last_serial": 4435960, "releases": { "1.0.1": [ { "comment_text": "", "digests": { "md5": "e7b6b903778af242b8408f8e5abf6682", "sha256": "97563c4233ba4e8001b67fbf6e7b7ee4039604857a1cff24ad1e3433cb96fb17" }, "downloads": -1, "filename": "pytest_voluptuous-1.0.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "e7b6b903778af242b8408f8e5abf6682", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 10896, "upload_time": "2018-01-10T22:16:42", "url": "https://files.pythonhosted.org/packages/ea/c5/a495b9f0ef720e58bd45bafe27c8e53c63ee9a954c753c32c1cdfa01942f/pytest_voluptuous-1.0.1-py2.py3-none-any.whl" } ], "1.0.2": [ { "comment_text": "", "digests": { "md5": "8d90597c86dfd64a9b58e9eb11709757", "sha256": "e0268c7a0aef6c2687d59f275cdb09ce89cf837bb89c1c4ad8c787f6fcbc2ac6" }, "downloads": -1, "filename": "pytest_voluptuous-1.0.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "8d90597c86dfd64a9b58e9eb11709757", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 11195, "upload_time": "2018-02-16T11:04:08", "url": "https://files.pythonhosted.org/packages/44/59/bce4e24c49e90813790d94cd962fa9aaec6a3201802d28195331a708d872/pytest_voluptuous-1.0.2-py2.py3-none-any.whl" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "c8239f64d13f12b36420d53ce9274bb5", "sha256": "65e6c66f0d28f4ead0fc5903cca9892328df4fe73a45751fd1089498e36cd9d7" }, "downloads": -1, "filename": "pytest_voluptuous-1.1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "c8239f64d13f12b36420d53ce9274bb5", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 7940, "upload_time": "2018-10-31T14:09:29", "url": "https://files.pythonhosted.org/packages/ab/eb/09c72e85a35e80826532493fe155ca256a5a4938a79bfa2f9d95343981ed/pytest_voluptuous-1.1.0-py2.py3-none-any.whl" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "c8239f64d13f12b36420d53ce9274bb5", "sha256": "65e6c66f0d28f4ead0fc5903cca9892328df4fe73a45751fd1089498e36cd9d7" }, "downloads": -1, "filename": "pytest_voluptuous-1.1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "c8239f64d13f12b36420d53ce9274bb5", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 7940, "upload_time": "2018-10-31T14:09:29", "url": "https://files.pythonhosted.org/packages/ab/eb/09c72e85a35e80826532493fe155ca256a5a4938a79bfa2f9d95343981ed/pytest_voluptuous-1.1.0-py2.py3-none-any.whl" } ] }