Skip to main content

A Zod-like validation library for Python

Project description

zon - Zod-like validator library for Python

Coverage Status

Want to have the validation power of zod but with the ease-of-use of Python?

Enter zon.

zon is a Python library that aims to provide a simple, easy-to-use API for validating data, similar to zod's own API'. In fact, the whole library and its name were inspired by zod: Zod + Python = Zon !!!.

Why

While doing a project for college, we were using both Python and JS/TS in the code-base. We were also using a JSON-Schema document to define a data schema for the data that we would be consuming.

There exists tooling that allows for one schema to generate TS types and from that Zod validator code, but there was nothing of the sort for Python. What's more, none of the validation libraries I found for Python had an API that was as easy to use as Zod's. So, I set out to build it.

Installation

Pip

This package is available on PyPI, so you can install it with pip:

pip install zon

Source

Alternatively, you can clone this repository and install it from source:

git clone https://github.com/Naapperas/zon
cd zon
pip install .

Usage

In its essence, zon behaves much like zod. If you have used zod, you will feel right at home with zon.

[!NOTE]
There are some differences in the public API between zon and zod. Those mostly stem from the fact that Python does not have type inference like Typescript has. There are other slight deviations between zon and zod.

Basic types

zon features most of zod's basic types:

zon.string()
zon.number()
zon.boolean()
zon.literal()
zon.enum()

zon.record()
zon.element_list()
zon.element_tuple()

zon.union()
zon.intersection()
zon.optional()

zon.never()
zon.anything()

Numbers

validator = zon.number()

validator.gt(5)
validator.gte(5) # alias for .min(5)
validator.lt(5)
validator.lte(5) # alias for .max(5)

validator.int()
validator.float()

validator.positive()
validator.negative()
validator.non_negative()
validator.non_positive()

validator.multiple_of(5) # alias for .step(5)

validator.finite()

Strings

For strings, there are also some extra methods:

validator = zon.string()

validator.min(5)
validator.max(10)
validator.length(5)

validator.email()
validator.url()
validator.uuid()
validator.regex(r"^\d{3}-\d{3}-\d{4}$")
validator.includes("needle")
validator.starts_with("prefix")
validator.ends_with("suffix")
validator.datetime()
validator.ip()

# transformers
validator.trim()
validator.to_lower_case()
validator.to_upper_case()

Datetime

zon accepts the same options as zod for datetime string validation:

validator.datetime({"precision": 3})
validator.datetime({"local": True})
validator.datetime({"offset": True})

[!NOTE] zod uses regex-based validation for datetimes, which must be valid ISO 8601 strings. However, due to an issue with most JavaScript engines' datetime validation, offsets cannot specify only hours, and zod reflects this in their API. While zon could reflect zod's API in this matter, it is best to not constrain users to the problems of another platform, making this one of the aspects where zon deviates from zod.

IP addresses

zon accepts the same options as zod for ip address string validation:

validator.ip({"version": "v4"})
validator.ip({"version": "v6"})

List

Lists are defined by calling the zon.element_list() method, passing as an argument a Zon instance. All elements in this list must be of the same type.

zon.element_list(zon.string())

Like strings, lists also have some extra methods that check the length of the list:

validator = zon.list(...)

validator.min(5)
validator.max(10)
validator.length(5)

There is also a method for validating that the list has at least one element, nonempty:

validator.nonempty()

You can get the type of the list's elements by accessing the element property:

zon.element_list(zon.string()).element is ZonString

Union

zon supports unions of types, which are defined by calling the zon.union() method, passing as arguments the Zon instances that are part of the union.

zod.union([zon.string(), zon.number()])

To access the various options, access the options property:

zod.union([zon.string(), zon.number()]).options = [ZonString, ZonNumber]

Record

zon supports validating objects according to a specified schema, using the zon.schema() method. This method takes as an argument a dictionary, where the keys are the keys of the object to be validated and the values are the Zon instances that define the type of each key.

validator = zon.record({
    "name": zon.string(),
    "age": zon.number(),
    "isAwesome": zon.boolean(),
    "friends": zon.array(zon.string()),
    "address": zon.record({
        "street": zon.string(),
        "city": zon.string(),
        "country": zon.string(),
    }),
})

Useful methods for ZonRecord instances:

validator.extend({...})
validator.merge(otherZon) == validator.extend(otherZon.shape)
validator.pick({"name": True}) == ZonRecord({"name": ...})
validator.omit({"name": True}) == ZonRecord({"<key that is different from name>": ..., ...})
validator.partial() # makes all attributes optional, shallowly
validator.partial({"name": True}) # makes only "name" optional (in this example)
validator.deepPartial() # makes all attributes optional, recursively
validator.required() # makes all attributes required, shallowly

You can access the shape of objects validated by any ZonRecord instance by accessing the shape property:

shape = validator.shape

If you want to validate only the keys of the shape, use the keyof method:

validator.keyof() == ZonEnum(["name", "age", "isAwesome", "friends", "address"])

Unknown Keys

As zod, zon normally strips unknown keys from records. This, however, can be configured:

validator.strict() # presence of unknown keys makes validation fail
validator.passthrough() # add unknown keys to the resulting record
validator.strip() # the default behavior, strip unknown keys from the resulting record

Catchall

In case you want to validate unknown keys, you can use the catchall method to specify the validator that is used:

validator.catchall(zon.string()) # unknown keys *must* be associated with string values

Tuple

zon supports tuples out-of-the-box, which are fixed-size containers whose elements might not have the same type:

validator = zon.tuple([...])

If you want to access the items of the tuples, use the items property:

validator.items = [...]

Variadic items can be validated using the rest method:

validator.rest(zon.string()) # after the defined items, everything must be a string

Enums

zon supports enumerations, which allow validating that any given data is one of many values:

validator = zon.enum([...])

If you want to access the possible valid values, use the enum property:

validator.enum = [...]

Examples

Example usage of zon can be found in the examples directory.

Documentation

Documentation is still not available, but it will be soon.

Tests

Tests can be found in the tests folder. zon uses pytest for unit testing.

To run the tests, simply run:

pytest test

Coverage can be found on Coveralls.

Contributing

Contribution guidelines can be found in CONTRIBUTING

Past and current contributors can be found in CONTRIBUTORS

License

This project is licensed under the MIT License - see the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

zon-2.0.1.tar.gz (17.2 kB view details)

Uploaded Source

Built Distribution

zon-2.0.1-py3-none-any.whl (14.4 kB view details)

Uploaded Python 3

File details

Details for the file zon-2.0.1.tar.gz.

File metadata

  • Download URL: zon-2.0.1.tar.gz
  • Upload date:
  • Size: 17.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.12.4

File hashes

Hashes for zon-2.0.1.tar.gz
Algorithm Hash digest
SHA256 7b24050de71603101b169ca8a99064c1aba8f7bdcf47abf4fa43e4c63b010ea7
MD5 c83b41d7a3600ccde091e1a4dba11cf8
BLAKE2b-256 c5e30317a145568cdb9e510acc687f7567efc0b618e311472988f7d18441224d

See more details on using hashes here.

File details

Details for the file zon-2.0.1-py3-none-any.whl.

File metadata

  • Download URL: zon-2.0.1-py3-none-any.whl
  • Upload date:
  • Size: 14.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.12.4

File hashes

Hashes for zon-2.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a65be8a581c66e8a42c4df179a456698707da3cf1f262a3a2f7973db113e0ac7
MD5 f2d52d16706ef60047b743eba15e2f21
BLAKE2b-256 74ac4ab56fd15a49999d924a7e15a8fba498a451457577b89d57db5b39d82896

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page