valguard: A lightweight framework for defining and validating values in data pipelines.
Project description
valguard
Constraint-aware value types for semantic validation in Python pipelines
valguard is a lightweight framework for defining and validating values in data pipelines. It separates values from constraints. This allows a source to publish the constraint against which it will validate each value. An implies function can determine whether values satisfying an upstream constraint are guaranteed to satisfy a downstream constraint.
Key Features
- Declarative value types
- Validators for clean pipeline integration
- Type-safe abstractions compatible with Python 3.12+
Usage
Values
A Value stores a particular kind of value. It is immutable. Different methods are provided for accessing the value depending on its type: this provides a concise way to validate type and access the value simultaneously. If x is not an integer then x.as_int will raise an exception.
To avoid raising an exception, use isinstance() to test the type beforehand.
A subclass of NumericValue will have the method .to_float.
>>> from valguard import FloatValue, IntValue, NumericValue, BoolValue
>>> fv = FloatValue(1.0) # Must use 1.0 and not 1
>>> iv = IntValue(1)
>>> bv = BoolValue(True)
>>> isinstance(bv,NumericValue)
False
>>> isinstance(iv,NumericValue)
True
>>> bv.as_float
Traceback (most recent call last):
...
valguard.exceptions.TypeMismatchError: Incompatible accessor
>>> iv.as_float
Traceback (most recent call last):
...
valguard.exceptions.TypeMismatchError: Incompatible accessor
>>> iv.to_float
1.0
>>> iv.as_int
1
>>> fv.as_float
1.0
>>> fv.to_float
1.0
Constraints
Constraints can be placed on both type and value.
>>> from valguard import IntValue, BoolValue, IntervalConstraint, NumericConstraint, IntConstraint
>>> iv = IntValue(23)
>>> bv = BoolValue(False)
>>> interval_a = IntervalConstraint(0,100)
>>> interval_b = IntervalConstraint(10,20)
>>> interval_a.validate(iv)
IntValue(23)
>>> interval_b.validate(iv)
Traceback (most recent call last):
...
valguard.exceptions.ValidationError: Invalid value: 23.0 lies outside [10.0, 20.0]
>>> interval_a.validate(bv)
Traceback (most recent call last):
...
valguard.exceptions.ValidationError: Invalid value: expected a numeric, got BoolValue(False)
>>> IntConstraint().validate(iv)
IntValue(23)
>>> IntConstraint().validate(iv).as_int
23
>>> NumericConstraint().validate(iv).to_float
23.0
>>> NumericConstraint().validate(bv).to_float
Traceback (most recent call last):
...
valguard.exceptions.ValidationError: Invalid value: expected a numeric, got BoolValue(False)
The implies function
The implies function determines whether one constraint implies another constraint. Constraint A implies constraint B if every value that satisfies A will also satisfy B.
>>> from valguard import BoundedIntConstraint, NumericConstraint, FloatConstraint, implies
>>> interval_A = BoundedIntConstraint(0,100)
>>> interval_B = BoundedIntConstraint(20,80)
>>> implies(interval_A, interval_B)
False
>>> implies(interval_B, interval_A)
True
>>> implies(interval_A, FloatConstraint)
False
>>> implies(interval_A, NumericConstraint)
True
>>> implies(NumericConstraint, interval_A)
False
Constraints that are parametrised (such as IntervalConstraint and LiteralStrConstraint) can be used in two ways: as an instance with specific parameters, or as a class. When used as a class, implies behaves differently depending on whether the class appears as the first or second argument to implies. As the first argument, implies(class, B) is True if all instances of class would imply B. As the second argument, implies(A, class) is True if at least one instance of class would be implied by A. For non-parametrised classes, there is no difference whether a class or an instance is used.
Constrained Value Dictionary
A ConstrainedValueDict behaves like a dictionary but its values are validated against a constraint at the time of insertion.
It is intentional that no type casting is performed, not even from int to IntValue.
>>> from valguard import IntValue, IntConstraint, ConstrainedValueDict
>>> d = ConstrainedValueDict(IntConstraint())
>>> d["one"] = IntValue(1)
>>> d["one"]
IntValue(1)
>>> d["two"] = 2
Traceback (most recent call last):
...
valguard.exceptions.ValidationError: Invalid value: expected an integer, got 2
Installation
pip install valguard
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file valguard-1.0.0.tar.gz.
File metadata
- Download URL: valguard-1.0.0.tar.gz
- Upload date:
- Size: 13.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.1.3 CPython/3.13.5 Windows/11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f4dbba1f947d5151ba309940a2da61bd0b5352e484ac80a99388b0066c69c199
|
|
| MD5 |
d680790fd965b73804b37cdf4268ac57
|
|
| BLAKE2b-256 |
2b22f280f91be3e56a05a10c3a9f59bbcf99fd01f65ecb3e8f8a179c6aaa8f5d
|
File details
Details for the file valguard-1.0.0-py3-none-any.whl.
File metadata
- Download URL: valguard-1.0.0-py3-none-any.whl
- Upload date:
- Size: 14.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.1.3 CPython/3.13.5 Windows/11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
66ba8d3e8f44ad8f6e9b903b431e31ef8b8f31a365f36f972072b87d5442c42c
|
|
| MD5 |
24421a4364fc563d81b4b0f245db1dea
|
|
| BLAKE2b-256 |
441c98b9a00b930c803f48d52bacad578f918a23968cf83629346e70cf3a29f6
|