# valdec
[![PyPI](https://img.shields.io/pypi/v/valdec)](https://pypi.org/project/valdec) [![Build Status](https://travis-ci.com/EvgeniyBurdin/valdec.svg?branch=main)](https://travis-ci.com/EvgeniyBurdin/valdec) [![Coverage Status](https://coveralls.io/repos/github/EvgeniyBurdin/valdec/badge.svg?branch=main)](https://coveralls.io/github/EvgeniyBurdin/valdec?branch=main) [![Total alerts](https://img.shields.io/lgtm/alerts/g/EvgeniyBurdin/valdec.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/EvgeniyBurdin/valdec/alerts/) [![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/EvgeniyBurdin/valdec.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/EvgeniyBurdin/valdec/context:python) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/valdec)
Decorator for validating arguments and/or result of functions and methods of a class against annotations.
Can be used in synchronous or asynchronous version.
Validation is supported using [pydantic.BaseModel](https://github.com/samuelcolvin/pydantic) and [validated_dc.ValidatedDC](https://github.com/EvgeniyBurdin/validated_dc).
You can easily add support for any other validator. To do this, you need to write your own validator-function and specify it in the settings for decorator (how to do this - see the examples below).
*Note: if the result of the function has no annotation, the result is expected to be `None`.*
## Installation
```bash
pip install valdec
```
## Quick example
### Default
Based on the validator `pydantic.BaseModel`:
```bash
pip install pydantic
```
```python
from typing import List, Optional
from pydantic import BaseModel, StrictInt, StrictStr
from valdec.decorators import validate
# from valdec.decorators import async_validate # Use for async functions
@validate # all arguments with annotations and return
def func(i: StrictInt, s: StrictStr) -> StrictInt:
return i
assert func(1, "s") == 1
@validate("i", exclude=True) # exclude "i" (only "s" and return)
def func(i: StrictInt, s: StrictStr) -> StrictInt:
return int(i)
assert func("1", "s") == 1
# For example, an Api got a json-string and decoded it:
data_for_group = [
{"name": "Peter", "profile": {"age": 22, "city": "Samara"}},
{"name": "Elena", "profile": {"age": 20, "city": "Kazan"}},
]
class Profile(BaseModel):
age: StrictInt
city: StrictStr
class Student(BaseModel):
name: StrictStr
profile: Profile
@validate("s", "group") # only "s" and "group"
def func(i: StrictInt, s: StrictStr, group: Optional[List[Student]] = None):
assert i == "i not int" # because "i" is not validated
assert isinstance(s, str)
for student in group:
# `student` is now an instance of `Student`
# (This conversion can be disabled!)
assert isinstance(student, Student)
assert isinstance(student.name, str)
assert isinstance(student.profile.age, int)
return group
result = func("i not int", "string", data_for_group)
# The result can be any, because it is not validated.., now this is a list:
assert isinstance(result, list)
```
### validated_dc_validator
It is possible to enable validation with [validated_dc.ValidatedDC](https://github.com/EvgeniyBurdin/validated_dc). To do this, change the reference to the validator-function in the decorator settings:
```bash
pip install validated-dc
```
```python
from dataclasses import dataclass
from typing import List, Optional
from valdec.data_classes import Settings
from valdec.decorators import validate as _validate
from valdec.validator_validated_dc import validator
from validated_dc import ValidatedDC
custom_settings = Settings(
validator=validator, # function for validation
is_replace_args=True, # default
is_replace_result=True, # default
extra={} # default
)
def validate(*args, **kwargs): # define new decorator
kwargs["settings"] = custom_settings
return _validate(*args, **kwargs)
# The new decorator can now be used:
@validate # all arguments with annotations and return
def func(i: int, s: str) -> int:
return i
assert func(1, "s") == 1
@validate("s") # only "s"
def func(i: int, s: str) -> int:
return i
assert func("not int", "s") == "not int"
@validate("s", "return") # only "s" and return
def func(i: int, s: str) -> int:
return int(i)
assert func("1", "s") == 1
@validate("i", exclude=True) # exclude "i" (only "s" and return)
def func(i: int, s: str) -> int:
return int(i)
assert func("1", "s") == 1
data_for_group = [
{"name": "Peter", "profile": {"age": 22, "city": "Samara"}},
{"name": "Elena", "profile": {"age": 20, "city": "Kazan"}},
]
@dataclass
class Profile(ValidatedDC):
age: int
city: str
@dataclass
class Student(ValidatedDC):
name: str
profile: Profile
@validate("s", "group") # only "s" and "group"
def func(i: int, s: str, group: Optional[List[Student]] = None):
assert i == "i not int" # because "i" is not validated
assert isinstance(s, str)
for student in group:
# `student` is now an instance of `Student`
# (This behavior can be changed by is_replace_args=False in the
# Settings instance)
assert isinstance(student, Student)
assert isinstance(student.name, str)
assert isinstance(student.profile.age, int)
return group
result = func("i not int", "string", data_for_group)
# The result can be any, because it is not validated.., now this is a list:
assert isinstance(result, list)
```
### Errors
`ValidationArgumentsError` is raised on an arguments validation error, and a `ValidationReturnError` on a result validation error:
```python
from typing import Optional
from pydantic import StrictInt, StrictStr
from valdec.decorators import validate
from valdec.errors import ValidationArgumentsError, ValidationReturnError
class Foo:
@validate # all arguments with annotations and return
def bar_1(self, i: StrictInt, s: Optional[StrictStr] = None):
pass
@validate("return") # only "return"
def bar_2(self, i: StrictInt, s: Optional[StrictStr] = None):
return i
foo = Foo()
foo.bar_1(1, "2") # ok
try:
foo.bar_1(1, 2)
except ValidationArgumentsError as error:
print(type(error), error, "\n")
foo.bar_2(None, 1) # ok
try:
foo.bar_2(1, 2)
except ValidationReturnError as error:
print(type(error), error)
```
Raw data
{
"_id": null,
"home_page": "https://github.com/EvgeniyBurdin/valdec",
"name": "valdec",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.7",
"maintainer_email": "",
"keywords": "validation function",
"author": "Evgeniy Burdin",
"author_email": "e.s.burdin@mail.ru",
"download_url": "https://files.pythonhosted.org/packages/67/76/ee4414475d712c781d76fad49accece76fd77dc57385502b6864f3f68e6a/valdec-1.1.0.tar.gz",
"platform": "",
"description": "# valdec\n\n[![PyPI](https://img.shields.io/pypi/v/valdec)](https://pypi.org/project/valdec) [![Build Status](https://travis-ci.com/EvgeniyBurdin/valdec.svg?branch=main)](https://travis-ci.com/EvgeniyBurdin/valdec) [![Coverage Status](https://coveralls.io/repos/github/EvgeniyBurdin/valdec/badge.svg?branch=main)](https://coveralls.io/github/EvgeniyBurdin/valdec?branch=main) [![Total alerts](https://img.shields.io/lgtm/alerts/g/EvgeniyBurdin/valdec.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/EvgeniyBurdin/valdec/alerts/) [![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/EvgeniyBurdin/valdec.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/EvgeniyBurdin/valdec/context:python) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/valdec)\n\nDecorator for validating arguments and/or result of functions and methods of a class against annotations.\n\nCan be used in synchronous or asynchronous version.\n\nValidation is supported using [pydantic.BaseModel](https://github.com/samuelcolvin/pydantic) and [validated_dc.ValidatedDC](https://github.com/EvgeniyBurdin/validated_dc).\n\nYou can easily add support for any other validator. To do this, you need to write your own validator-function and specify it in the settings for decorator (how to do this - see the examples below).\n\n*Note: if the result of the function has no annotation, the result is expected to be `None`.*\n\n## Installation\n\n```bash\npip install valdec\n```\n\n## Quick example\n\n### Default\n\nBased on the validator `pydantic.BaseModel`:\n\n```bash\npip install pydantic\n```\n\n```python\nfrom typing import List, Optional\n\nfrom pydantic import BaseModel, StrictInt, StrictStr\nfrom valdec.decorators import validate\n# from valdec.decorators import async_validate # Use for async functions\n\n\n@validate # all arguments with annotations and return\ndef func(i: StrictInt, s: StrictStr) -> StrictInt:\n return i\n\nassert func(1, \"s\") == 1\n\n\n@validate(\"i\", exclude=True) # exclude \"i\" (only \"s\" and return)\ndef func(i: StrictInt, s: StrictStr) -> StrictInt:\n return int(i)\n\nassert func(\"1\", \"s\") == 1\n\n\n# For example, an Api got a json-string and decoded it:\ndata_for_group = [\n {\"name\": \"Peter\", \"profile\": {\"age\": 22, \"city\": \"Samara\"}},\n {\"name\": \"Elena\", \"profile\": {\"age\": 20, \"city\": \"Kazan\"}},\n]\n\nclass Profile(BaseModel):\n age: StrictInt\n city: StrictStr\n\nclass Student(BaseModel):\n name: StrictStr\n profile: Profile\n\n@validate(\"s\", \"group\") # only \"s\" and \"group\"\ndef func(i: StrictInt, s: StrictStr, group: Optional[List[Student]] = None):\n\n assert i == \"i not int\" # because \"i\" is not validated\n assert isinstance(s, str)\n for student in group:\n # `student` is now an instance of `Student`\n # (This conversion can be disabled!)\n assert isinstance(student, Student)\n assert isinstance(student.name, str)\n assert isinstance(student.profile.age, int)\n\n return group\n\nresult = func(\"i not int\", \"string\", data_for_group)\n# The result can be any, because it is not validated.., now this is a list:\nassert isinstance(result, list)\n```\n\n### validated_dc_validator\n\nIt is possible to enable validation with [validated_dc.ValidatedDC](https://github.com/EvgeniyBurdin/validated_dc). To do this, change the reference to the validator-function in the decorator settings:\n\n```bash\npip install validated-dc\n```\n\n```python\nfrom dataclasses import dataclass\nfrom typing import List, Optional\n\nfrom valdec.data_classes import Settings\nfrom valdec.decorators import validate as _validate\nfrom valdec.validator_validated_dc import validator \nfrom validated_dc import ValidatedDC\n\ncustom_settings = Settings(\n validator=validator, # function for validation\n is_replace_args=True, # default\n is_replace_result=True, # default\n extra={} # default\n)\ndef validate(*args, **kwargs): # define new decorator\n kwargs[\"settings\"] = custom_settings\n return _validate(*args, **kwargs)\n\n\n# The new decorator can now be used:\n\n@validate # all arguments with annotations and return\ndef func(i: int, s: str) -> int:\n return i\n\nassert func(1, \"s\") == 1\n\n\n@validate(\"s\") # only \"s\"\ndef func(i: int, s: str) -> int:\n return i\n\nassert func(\"not int\", \"s\") == \"not int\"\n\n\n@validate(\"s\", \"return\") # only \"s\" and return\ndef func(i: int, s: str) -> int:\n return int(i)\n\nassert func(\"1\", \"s\") == 1\n\n\n@validate(\"i\", exclude=True) # exclude \"i\" (only \"s\" and return)\ndef func(i: int, s: str) -> int:\n return int(i)\n\nassert func(\"1\", \"s\") == 1\n\n\ndata_for_group = [\n {\"name\": \"Peter\", \"profile\": {\"age\": 22, \"city\": \"Samara\"}},\n {\"name\": \"Elena\", \"profile\": {\"age\": 20, \"city\": \"Kazan\"}},\n]\n\n@dataclass\nclass Profile(ValidatedDC):\n age: int\n city: str\n\n@dataclass\nclass Student(ValidatedDC):\n name: str\n profile: Profile\n\n@validate(\"s\", \"group\") # only \"s\" and \"group\"\ndef func(i: int, s: str, group: Optional[List[Student]] = None):\n\n assert i == \"i not int\" # because \"i\" is not validated\n assert isinstance(s, str)\n for student in group:\n # `student` is now an instance of `Student`\n # (This behavior can be changed by is_replace_args=False in the\n # Settings instance)\n assert isinstance(student, Student)\n assert isinstance(student.name, str)\n assert isinstance(student.profile.age, int)\n\n return group\n\nresult = func(\"i not int\", \"string\", data_for_group)\n# The result can be any, because it is not validated.., now this is a list:\nassert isinstance(result, list)\n```\n\n### Errors\n\n`ValidationArgumentsError` is raised on an arguments validation error, and a `ValidationReturnError` on a result validation error: \n\n```python\nfrom typing import Optional\n\nfrom pydantic import StrictInt, StrictStr\nfrom valdec.decorators import validate\nfrom valdec.errors import ValidationArgumentsError, ValidationReturnError\n\n\nclass Foo:\n\n @validate # all arguments with annotations and return\n def bar_1(self, i: StrictInt, s: Optional[StrictStr] = None):\n pass\n\n @validate(\"return\") # only \"return\"\n def bar_2(self, i: StrictInt, s: Optional[StrictStr] = None):\n return i\n\n\nfoo = Foo()\n\nfoo.bar_1(1, \"2\") # ok\n\ntry:\n foo.bar_1(1, 2)\nexcept ValidationArgumentsError as error:\n print(type(error), error, \"\\n\")\n\n\nfoo.bar_2(None, 1) # ok\n\ntry:\n foo.bar_2(1, 2)\nexcept ValidationReturnError as error:\n print(type(error), error)\n```\n\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Decorator for validating function arguments and result.",
"version": "1.1.0",
"project_urls": {
"Homepage": "https://github.com/EvgeniyBurdin/valdec"
},
"split_keywords": [
"validation",
"function"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "4f4bffc694d25a2079bc2f7765f5b94a95f72c772ae5d9e1ae025cfc8205bfc0",
"md5": "5416917c56a8f7ae046632b6339566ae",
"sha256": "d252ec0d0a841a26d897915cd06d98f962acdba2acb6d8ccc0b9b90ab008f566"
},
"downloads": -1,
"filename": "valdec-1.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "5416917c56a8f7ae046632b6339566ae",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7",
"size": 12132,
"upload_time": "2021-02-03T16:48:30",
"upload_time_iso_8601": "2021-02-03T16:48:30.892960Z",
"url": "https://files.pythonhosted.org/packages/4f/4b/ffc694d25a2079bc2f7765f5b94a95f72c772ae5d9e1ae025cfc8205bfc0/valdec-1.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "6776ee4414475d712c781d76fad49accece76fd77dc57385502b6864f3f68e6a",
"md5": "7ed8ba6b8df2931606bd97bf4b368bf2",
"sha256": "bc8570e62a148ff81e5061916b7cc22af64d1524dad8c78873edc092e4a114a1"
},
"downloads": -1,
"filename": "valdec-1.1.0.tar.gz",
"has_sig": false,
"md5_digest": "7ed8ba6b8df2931606bd97bf4b368bf2",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7",
"size": 10895,
"upload_time": "2021-02-03T16:48:32",
"upload_time_iso_8601": "2021-02-03T16:48:32.554185Z",
"url": "https://files.pythonhosted.org/packages/67/76/ee4414475d712c781d76fad49accece76fd77dc57385502b6864f3f68e6a/valdec-1.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2021-02-03 16:48:32",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "EvgeniyBurdin",
"github_project": "valdec",
"travis_ci": true,
"coveralls": true,
"github_actions": false,
"requirements": [],
"lcname": "valdec"
}