Skip to main content

No project description provided

Project description

pynsure

[!TIP] [pin-sure] (like insure 😄)

Predicate-based runtime constraint validator for Python

Check it out

Just use type hints and a decorator to verify parameter constraints at runtime.

from pynsure import validate, Unsigned

@validate()
def add_two(a: Unsigned, b: int) -> int:
    return a + b

add_two(3, 4) # OK
add_two(-2, 4) # BAD

Make your own types using typing.Annotated!

from pynsure import validate
from typing import Annotated

Odd = Annotated[int, (lambda _v: _v & 1 == 1, "{} must be odd but its value is {{}}")]

@validate()
def add_two(a: Odd, b: Odd):
    return a + b

add_two(3, 5) # OK
add_two(4, 5) # BAD

[!Note] {} is replaced with the parameter name and {{}} is replaced by the parameter value automatically when a ValidationError is raised.

You can even specify multiple predicates for your Annotated types:

from pynsure import validate
from typing import Annotated

PositiveAndOdd = Annotated[
    int,
    (lambda _v: _v & 1 == 1, "{} must be odd but its value is {{}}"),
    (lambda _v: _v > 0, "{} must be greater than 0 but its value is {{}}"),
]

@validate()
def add_two(a: PositiveAndOdd, b: PositiveAndOdd) -> int:
    return a + b

add_two(7, 9) # OK
add_two(-7, 9) # BAD
add_two(7, 8) # BAD

Only Annotated types following this form are validated:

MyType = Annotated[<base_type>, (<predicate>, <message>), <...>] where ... can be more predicate + message tuples

"Why not use asserts?"

Valid point.

Basically, pynsure makes it easier for users of your functions to understand the exact constraints surrounding a parameter (or return value). You can make and document your constraints and have them runtime-validated in just a few easy steps.

If my function expects four positive numbers, it's a whole lot easier to annotate like this:

@validate()
def add_four(a: Unsigned, b: Unsigned, c: Unsigned, d: Unsigned) -> Unsigned:
    return a + b + c + d

...than it is to do this:

def add_four(a, b, c, d):
    assert isinstance(a, int) and a >= 0, "a should be greater than or equal to 0"
    assert isinstance(b, int) and b >= 0, "b should be greater than or equal to 0"
    assert isinstance(c, int) and c >= 0, "c should be greater than or equal to 0"
    assert isinstance(d, int) and d >= 0, "d should be greater than or equal to 0"
    return

...plus pynsure will validate return types and return value constraints at runtime too! if add_four() doesn't return an integer greater than or equal to 0, a ValidationError will be raised.

"Why not use pydantic"

Great point.

pydantic is awesome (and battle-tested) but is a bit clunky if you want to do basic constraints and don't necessarily need giant serializable objects that you can convert from json to a dict and a whole bunch of other fancy stuff.

You have a function that expects non-empty strings? pynsure makes this a breeze. Let's compare:

Using pynsure

from pynsure import validate
from typing import Annotated

NonEmptyStr = Annotated[str, (lambda _s: len(_s) > 0, "{} shouldn't be empty")]

@validate()
def format_name(first_name: NonEmptyStr, last_name: NonEmptyStr) -> NonEmptyStr:
    return f"{last_name}, {first_name}"

format_name(first_name="Bob", last_name="Smith")

Using pydantic

from pydantic import BaseModel, constr

class NonEmptyStr(BaseModel):
    string: constr(min_length=1)

def format_name(first_name: NonEmptyStr, last_name: NonEmptyStr) -> str:
    return f"{last_name}, {first_name}"

format_name(first_name=NonEmptyStr(string="Bob"), last_name=NonEmptyStr(string="Smith"))

This is fine but a bit clunky having to fiddle around with BaseModels.

Conclusion

Ultimately, use whatever you want, this isn't some new standard and you'll probably get better mileage from pydantic (as far as runtime validation is concerned). But, if you want to set some quick and easy constraints on your methods, give pynsure a shot.

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

pynsure-0.1.0.tar.gz (3.1 kB view hashes)

Uploaded Source

Built Distribution

pynsure-0.1.0-py3-none-any.whl (3.8 kB view hashes)

Uploaded Python 3

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