valguard


Namevalguard JSON
Version 1.0.1 PyPI version JSON
download
home_pagehttps://github.com/jhmanton/valguard
Summaryvalguard: A lightweight framework for defining and validating values in data pipelines.
upload_time2025-07-27 16:02:42
maintainerNone
docs_urlNone
authorJonathan Manton
requires_python<4.0,>=3.12
licenseApache-2.0
keywords data validation values constraints pipelines
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # 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.

[![License](https://img.shields.io/badge/license-Apache--2.0-blue)](https://www.apache.org/licenses/LICENSE-2.0)
[![PyPI](https://img.shields.io/pypi/v/valguard)](https://pypi.org/project/valguard/)
[![Python](https://img.shields.io/pypi/pyversions/valguard)](https://pypi.org/project/valguard/)
[![Docs](https://readthedocs.org/projects/valguard/badge/?version=latest)](https://valguard.readthedocs.io/en/latest/)

---

## 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`.

```python
>>> 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.

```python
>>> 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.

```python
>>> 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`.

```python
>>> 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

```bash
pip install valguard
```
            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/jhmanton/valguard",
    "name": "valguard",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.12",
    "maintainer_email": null,
    "keywords": "data, validation, values, constraints, pipelines",
    "author": "Jonathan Manton",
    "author_email": "j.manton@ieee.org",
    "download_url": "https://files.pythonhosted.org/packages/dc/91/8158c69c592003446a729f6f8b7df5941e882b587c8418f62a371d183659/valguard-1.0.1.tar.gz",
    "platform": null,
    "description": "# valguard  \n_Constraint-aware value types for semantic validation in Python pipelines_\n\n**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.\n\n[![License](https://img.shields.io/badge/license-Apache--2.0-blue)](https://www.apache.org/licenses/LICENSE-2.0)\n[![PyPI](https://img.shields.io/pypi/v/valguard)](https://pypi.org/project/valguard/)\n[![Python](https://img.shields.io/pypi/pyversions/valguard)](https://pypi.org/project/valguard/)\n[![Docs](https://readthedocs.org/projects/valguard/badge/?version=latest)](https://valguard.readthedocs.io/en/latest/)\n\n---\n\n## Key Features\n\n- Declarative value types\n- Validators for clean pipeline integration\n- Type-safe abstractions compatible with Python 3.12+\n\n## Usage\n\n### Values\n\nA `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.\n\nTo avoid raising an exception, use `isinstance()` to test the type beforehand.\n\nA subclass of `NumericValue` will have the method `.to_float`.\n\n```python\n>>> from valguard import FloatValue, IntValue, NumericValue, BoolValue\n>>> fv = FloatValue(1.0)    # Must use 1.0 and not 1\n>>> iv = IntValue(1)\n>>> bv = BoolValue(True)\n>>> isinstance(bv,NumericValue)\nFalse\n>>> isinstance(iv,NumericValue)\nTrue\n>>> bv.as_float\nTraceback (most recent call last):\n    ...\nvalguard.exceptions.TypeMismatchError: Incompatible accessor\n>>> iv.as_float\nTraceback (most recent call last):\n    ...\nvalguard.exceptions.TypeMismatchError: Incompatible accessor\n>>> iv.to_float\n1.0\n>>> iv.as_int\n1\n>>> fv.as_float\n1.0\n>>> fv.to_float\n1.0\n```\n\n### Constraints\n\nConstraints can be placed on both type and value.\n\n```python\n>>> from valguard import IntValue, BoolValue, IntervalConstraint, NumericConstraint, IntConstraint\n>>> iv = IntValue(23)\n>>> bv = BoolValue(False)\n>>> interval_a = IntervalConstraint(0,100)\n>>> interval_b = IntervalConstraint(10,20)\n>>> interval_a.validate(iv)\nIntValue(23)\n>>> interval_b.validate(iv)\nTraceback (most recent call last):\n    ...\nvalguard.exceptions.ValidationError: Invalid value: 23.0 lies outside [10.0, 20.0]\n>>> interval_a.validate(bv)\nTraceback (most recent call last):\n    ...\nvalguard.exceptions.ValidationError: Invalid value: expected a numeric, got BoolValue(False)\n>>> IntConstraint().validate(iv)\nIntValue(23)\n>>> IntConstraint().validate(iv).as_int\n23\n>>> NumericConstraint().validate(iv).to_float\n23.0\n>>> NumericConstraint().validate(bv).to_float\nTraceback (most recent call last):\n    ...\nvalguard.exceptions.ValidationError: Invalid value: expected a numeric, got BoolValue(False)\n```\n\n#### The `implies` function\n\nThe `implies` function determines whether one constraint implies another constraint. Constraint A implies constraint B if every value that satisfies A will also satisfy B.\n\n```python\n>>> from valguard import BoundedIntConstraint, NumericConstraint, FloatConstraint, implies\n>>> interval_A = BoundedIntConstraint(0,100)\n>>> interval_B = BoundedIntConstraint(20,80)\n>>> implies(interval_A, interval_B)\nFalse\n>>> implies(interval_B, interval_A)\nTrue\n>>> implies(interval_A, FloatConstraint)\nFalse\n>>> implies(interval_A, NumericConstraint)\nTrue\n>>> implies(NumericConstraint, interval_A)\nFalse\n```\n\nConstraints 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.\n\n### Constrained Value Dictionary\n\nA `ConstrainedValueDict` behaves like a dictionary but its values are validated against a constraint at the time of insertion.\n\nIt is intentional that no type casting is performed, not even from `int` to `IntValue`.\n\n```python\n>>> from valguard import IntValue, IntConstraint, ConstrainedValueDict\n>>> d = ConstrainedValueDict(IntConstraint())                             \n>>> d[\"one\"] = IntValue(1)\n>>> d[\"one\"]\nIntValue(1)\n>>> d[\"two\"] = 2\nTraceback (most recent call last):\n    ...\nvalguard.exceptions.ValidationError: Invalid value: expected an integer, got 2\n```\n\n## Installation\n\n```bash\npip install valguard\n```",
    "bugtrack_url": null,
    "license": "Apache-2.0",
    "summary": "valguard: A lightweight framework for defining and validating values in data pipelines.",
    "version": "1.0.1",
    "project_urls": {
        "Homepage": "https://github.com/jhmanton/valguard",
        "Repository": "https://github.com/jhmanton/valguard"
    },
    "split_keywords": [
        "data",
        " validation",
        " values",
        " constraints",
        " pipelines"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "d38e39ab08e72d05d3abafcba87541491001b706c66d9a01ed727ca12270a7e4",
                "md5": "9c12a39c4130cc8b702f31bd2edf8c7a",
                "sha256": "1406a2017ebc9b8984b56629e6ae278936c58a4eeeaeb640eca88561c07e0fe2"
            },
            "downloads": -1,
            "filename": "valguard-1.0.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "9c12a39c4130cc8b702f31bd2edf8c7a",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.12",
            "size": 14392,
            "upload_time": "2025-07-27T16:02:41",
            "upload_time_iso_8601": "2025-07-27T16:02:41.670287Z",
            "url": "https://files.pythonhosted.org/packages/d3/8e/39ab08e72d05d3abafcba87541491001b706c66d9a01ed727ca12270a7e4/valguard-1.0.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "dc918158c69c592003446a729f6f8b7df5941e882b587c8418f62a371d183659",
                "md5": "f23206d3068323875bfcaf09c4b83dd0",
                "sha256": "8a35797b99ccbbc28266d9fcd1868e31edc535a18b9f20ca767029f4d76bf975"
            },
            "downloads": -1,
            "filename": "valguard-1.0.1.tar.gz",
            "has_sig": false,
            "md5_digest": "f23206d3068323875bfcaf09c4b83dd0",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.12",
            "size": 13401,
            "upload_time": "2025-07-27T16:02:42",
            "upload_time_iso_8601": "2025-07-27T16:02:42.610287Z",
            "url": "https://files.pythonhosted.org/packages/dc/91/8158c69c592003446a729f6f8b7df5941e882b587c8418f62a371d183659/valguard-1.0.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-27 16:02:42",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "jhmanton",
    "github_project": "valguard",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "valguard"
}
        
Elapsed time: 0.43071s