Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/wilsonwang371/betterschema
a python library for define and validate schema data
https://github.com/wilsonwang371/betterschema
Last synced: about 6 hours ago
JSON representation
a python library for define and validate schema data
- Host: GitHub
- URL: https://github.com/wilsonwang371/betterschema
- Owner: wilsonwang371
- License: mit
- Created: 2024-05-18T05:40:09.000Z (6 months ago)
- Default Branch: master
- Last Pushed: 2024-06-16T23:47:30.000Z (5 months ago)
- Last Synced: 2024-11-06T10:36:43.296Z (14 days ago)
- Language: C
- Size: 104 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# BetterSchema
BetterSchema is a schema validation and data watching framework for Python. It allows you to define data schemas, validate data against these schemas, and watch for changes in the data with custom watchers.
## Features
- **Schema Definition**: Easily define data schemas using type hints and decorators.
- **Data Validation**: Automatically validate data against the defined schemas.
- **Nested Schemas**: Support for nested schemas within data models.
- **Watchers**: Monitor changes to data attributes and perform custom actions.
- **Optional Fields**: Handle optional fields within your schemas.
- **Type Checking**: Enforce type constraints on data attributes.## Installation
You can install BetterSchema via pip:
```sh
pip install betterschema==0.1.18
```## Usage
### Defining Schemas
To define a schema, you can use the `@core.schema` decorator on a class definition. Inside the class, you can define the attributes of the schema using type hints. You can also define nested schemas within the main schema class.
```python
import betterschema.core as core@core.schema
class Foo:
foo1: str # String field
foo2: int # Integer field
foo3: bool # Boolean field
foo4: list[str] # List of strings
foo5: core.Optional[int] # Optional field
foo6: dict[str, int] # Dictionary field
foo7: core.Optional[core.Union[int, str]] # Optional field with multiple types@core.schema
class EmbeddedSchema:
bar1: str
bar2: int
bar3: boolbar: EmbeddedSchema # Nested schema field
```### Creating Instances
You can create instances of the schema by passing a dictionary with the required fields to the schema class constructor. The data will be validated against the schema automatically.
```python
# Creating an instance of the schema
foo = Foo({
"foo1": "hello",
"foo2": 0,
"foo3": True,
"foo4": ["a", "b", "c"],
"foo6": {"a": 1, "b": 2},
"bar": Foo.EmbeddedSchema({
"bar1": "world",
"bar2": 20,
"bar3": False,
})
})# For nested schemas, you can also give a dictionary directly
foo = Foo({
"foo1": "hello",
"foo2": 0,
"foo3": True,
"foo4": ["a", "b", "c"],
"foo6": {"a": 1, "b": 2},
"bar": {
"bar1": "world",
"bar2": 20,
"bar3": False,
}
})```
### Updating Values
You can update the values of the schema attributes directly. The data will be validated on each update.
```python
# Updating values
foo.foo1 = "hello2"
foo.foo2 = 1# if you try to set an invalid value, it will raise an exception
foo.foo1 = 1 # This will raise a TypeError# you can also specify a dictionary to update multiple values at once
# in order to do this, you need to use the `<<` operator
foo << {
"foo1": "hello3",
"foo2": 2,
}# for nested schemas, you can update the whole schema at once
foo.bar = {
"bar1": "world2",
"bar2": 30,
"bar3": True,
}# or use the `<<` operator to do partial updates
foo.bar << {
"bar1": "world3",
"bar2": 40,
}```
### Watching Values
You can define watchers to monitor changes to specific attributes of the schema. Watchers are defined using the `@core.watch` decorator, which takes a tuple of the form `(schema_class, attribute_name)` as an argument.
```python
@core.watch((Foo, "foo1"), (Foo, "foo2"))
def watch_values(inst, name: str, old, new):
print(f"watch_values: {inst}.{name}, {old} -> {new}")
if name == "foo1" and new == "hi":
raise ValueError("foo1 cannot be 'hi'")```
Regular expressions can also be used to match multiple attributes at once.
```python
@core.watch((Foo, "foo[12]"))
def watch_values(inst, name: str, old, new):
print(f"watch_values: {inst}.{name}, {old} -> {new}")
if name == "foo1" and new == "hi":
raise ValueError("foo1 cannot be 'hi'")```
### Name Mapping
`core.schema` can take an optional argument `mapping` which is a dictionary that maps the schema attribute names to the corresponding data attribute names. This can be useful when the schema attribute names do not match the data attribute names. Example:
```python
@core.schema(mapping={"foo1": "data_foo1", "foo2": "data_foo2"})
class Foo:
foo1: str
foo2: int```
## Contributing
Contributions are welcome! Please submit a pull request or open an issue to discuss your ideas.
## License
This project is licensed under the MIT License.