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 details)

Uploaded Source

Built Distribution

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

Uploaded Python 3

File details

Details for the file pynsure-0.1.0.tar.gz.

File metadata

  • Download URL: pynsure-0.1.0.tar.gz
  • Upload date:
  • Size: 3.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.7.1 CPython/3.10.8 Linux/6.2.0-1016-azure

File hashes

Hashes for pynsure-0.1.0.tar.gz
Algorithm Hash digest
SHA256 ffb82f89bbc98a25bcaeaf7e4af24bba6b6b2fa4bfbde6d9a596a8ea9c0ce115
MD5 501780f7acb4db942e63eff699f10eaf
BLAKE2b-256 80f0b39310cef863641dbafd92f98cca347f506cc07070096e34ce44249269eb

See more details on using hashes here.

File details

Details for the file pynsure-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pynsure-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 3.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.7.1 CPython/3.10.8 Linux/6.2.0-1016-azure

File hashes

Hashes for pynsure-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2ee0c328472945236b3162b543616b07044a6556266208249cc72bfbec8a7f82
MD5 16fc638a9e847f88511524d07f962748
BLAKE2b-256 6c4c3ef95303fc49016a8c39eb2c8db8bd4129213516f04b1cc5c492ff6f40ff

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