{"id":21578706,"url":"https://github.com/julian-nash/liaison","last_synced_at":"2025-06-13T20:40:56.199Z","repository":{"id":62575761,"uuid":"415026231","full_name":"Julian-Nash/liaison","owner":"Julian-Nash","description":"A Python library for defining schemas, parsing and validating payloads","archived":false,"fork":false,"pushed_at":"2021-10-10T14:39:23.000Z","size":40,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-03T15:27:41.718Z","etag":null,"topics":["parsing","payloads","python","validation"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Julian-Nash.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-10-08T14:52:58.000Z","updated_at":"2023-11-02T02:03:44.000Z","dependencies_parsed_at":"2022-11-03T17:32:28.309Z","dependency_job_id":null,"html_url":"https://github.com/Julian-Nash/liaison","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Julian-Nash/liaison","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Julian-Nash%2Fliaison","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Julian-Nash%2Fliaison/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Julian-Nash%2Fliaison/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Julian-Nash%2Fliaison/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Julian-Nash","download_url":"https://codeload.github.com/Julian-Nash/liaison/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Julian-Nash%2Fliaison/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259718432,"owners_count":22901181,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["parsing","payloads","python","validation"],"created_at":"2024-11-24T13:11:24.775Z","updated_at":"2025-06-13T20:40:56.163Z","avatar_url":"https://github.com/Julian-Nash.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Liaison\n\nA zero dependency Python library for defining schemas, parsing and validating payloads.\n\n[![CI](https://github.com/Julian-Nash/liaison/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/Julian-Nash/liaison/actions/workflows/main.yml)\n\nLiaison doesn't aim to be too clever. It doesn't use descriptors, fancy metaprogramming or type hints for defining \nyour schema. Simply inherit from the `Schema` base class, define your fields and call `parse`. In return, you'll \nreceive a simple `Namespace` object containing your parsed data.\n\nGoals:\n\n- Simplicity\n- Extensibility\n- Speed\n- 100% test coverage\n\nInstallation:\n\n```shell\npip install liaison\n```\n\nExample:\n\n```py3\nfrom liaison import Schema, ValidationError\nfrom liaison.fields import StringField, IntField, BoolField, ListField, DateTimeField\n\n\nclass UserSchema(Schema):\n\n    name = StringField(required=True)\n    email = StringField(required=True)\n    age = IntField(min_val=18)\n    date_of_birth = DateTimeField(date_format=\"%d-%m-%Y\")\n    subscribed = BoolField(default=False)\n    tags = ListField(min_len=1)\n\n\ndata = {\n    \"name\": \"Bar\",\n    \"email\": \"foo@bar.com\",\n    \"age\": 21,\n    \"tags\": [\"Python\"]\n}\n\nresult = UserSchema.parse(data)\n\nprint(result.name, result.email, result.age, result.tags)  # Bar foo@bar.com 21 ['Python']\n```\n\nHandling validation errors:\n\n```py3\ndata = {\n    \"name\": \"Bar\",\n    \"email\": \"foo@bar.com\",\n    \"age\": 16\n}\n\ntry:\n    result = UserSchema.parse(data)\nexcept ValidationError as e:\n    print(e)  # Value for 'age' must be at least 18\n```\n\nDefining custom field validators via the `\u003cfield\u003e.validator` decorator:\n\n```py3\nclass UserSchema(Schema):\n\n    name = StringField(required=True)\n    email = StringField(required=True)\n    age = IntField(min_val=18)\n\n    @name.validator\n    def validate_name(self, key, value):\n        # Define a custom validator, overrides the default validation method\n        if value == \"Foo\":\n            raise ValidationError(f\"'{value}' is not a valid value for '{key}'\")\n        return value\n```\n\nCustom validators can also be passed as a parameter to the field:\n\n```py3\ndef name_validator(schema_cls, key, value):\n    if value in (\"Foo\", \"Bar\", \"Baz\"):\n        raise ValidationError(f\"'{value}' is not a valid value for '{key}'\")\n    return value\n\n\nclass UserSchema(Schema):\n\n    name = StringField(required=True, validator=name_validator)\n    email = StringField(required=True)\n    age = IntField(min_val=18)\n```\n\n## Fields\n\nUse fields to define your schema. By default, all fields accept the following common parameters:\n\n| Parameter | Type | Description | Default |\n| --------- | ---- | ----------- | ------- |\n| `required` | `bool` | If the value is required | `False` |\n| `default` | `Any` | A default value  | `None` |\n| `choices` | `List[Any]` | A list of choices  | `None` |\n| `validator` | `Callable` | A function to override the default validation method  | `None` |\n| `strict_type` | `bool` | If `True`, only accept the fields data type  | `False` |\n\n### `StringField` - Defining strings\n\n| Parameter | Type | Description | Default |\n| --------- | ---- | ----------- | ------- |\n| `min_len` | `int` | The minimum length | `None` |\n| `max_len` | `int` | The maximum length | `None` |\n\n### `IntField` - Defining integers\n\n| Parameter | Type | Description | Default |\n| --------- | ---- | ----------- | ------- |\n| `min_val` | `int` | The minimum value | `None` |\n| `max_val` | `int` | The maximum value | `None` |\n\n### `FloatField` - Defining floats\n\n| Parameter | Type | Description | Default |\n| --------- | ---- | ----------- | ------- |\n| `min_val` | `int` | The minimum value | `None` |\n| `max_val` | `int` | The maximum value | `None` |\n\n### `BoolField` - Defining booleans\n\n### `ListField` - Defining lists\n\n| Parameter | Type | Description | Default |\n| --------- | ---- | ----------- | ------- |\n| `min_len` | `int` | The minimum length | `None` |\n| `max_len` | `int` | The maximum length | `None` |\n\n### `SetField` - Defining sets\n\n\u003e Note - `SetField` shares the same behaviour as `ListField`, returning a `set`.\n\n| Parameter | Type | Description | Default |\n| --------- | ---- | ----------- | ------- |\n| `min_len` | `int` | The minimum length | `None` |\n| `max_len` | `int` | The maximum length | `None` |\n\n### `DictField` - Defining dictionaries\n\n| Parameter | Type | Description | Default |\n| --------- | ---- | ----------- | ------- |\n| `min_len` | `int` | The minimum length | `None` |\n| `max_len` | `int` | The maximum length | `None` |\n\n### `DateTimeField` - Defining datetimes\n\n\u003e Note - `DateTimeField` fields will return `datetime` objects\n\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| `date_format` | `str` | The date format |\n\n### `UUIDField` - Defining UUIDs\n\n\u003e Note - `UUIDField` fields will NOT return a `UUID` obejct, it will return a string.\n\n## Namespace\n\nCalling the `parse` method on a `Schema` object will return a `Namespace` object, holding the parsed values as \nattributes.\n\n```py3\nfrom liaison import Schema\nfrom liaison.fields import StringField, IntField, BoolField, FloatField, UUIDField\n\n\nclass RESTBaseSchema(Schema):\n\n    offset = IntField(min_val=0, default=0)\n    limit = IntField(max_val=100)\n    search = StringField()\n\n\nclass ProductsRESTSchema(RESTBaseSchema):\n\n    product_id = UUIDField()\n    category = StringField()\n    price = FloatField()\n    in_stock = BoolField()\n\n\npayload = {\n    \"offset\": 10,\n    \"category\": \"shoes\",\n    \"in_stock\": True\n}\n\nresult = ProductsRESTSchema.parse(payload)  \n\nprint(result.offset, result.limit, result.search, result.category, result.in_stock)\n# 10 None None shoes True\n```\n\n`Namespace` objects have a `to_dict` method, returning a dictionary of the `Namespace` attributes and values:\n\n```py3\nprint(result.to_dict())\n# {'category': 'shoes', 'in_stock': True, 'limit': None, 'offset': 10, 'price': None, 'product_id': None, 'search': None}\n```\n\nAn optional `exclude` parameter can be included to exclude certain attributes:\n\n```py3\nprint(result.to_dict(exclude=(\"offset\", \"limit\", \"search\")))\n# {'category': 'shoes', 'in_stock': True, 'price': None, 'product_id': None}\n```\n\n## Defining custom fields\n\nCreate your own fields and validation logic by inheriting from any of the field classes and implementing a \n`validate` method.\n\n\u003e Note - The `validate` method must accept 2 params (key, value)\n\n```py3\nfrom liaison import Schema, ValidationError\nfrom liaison.fields import StringField\n\n\nclass PasswordField(StringField):\n\n    def validate(self, key, value):\n        value = super().validate(key, value)\n        if len(value) \u003c 9:\n            raise ValidationError(\"Value for 'password' must be at least 9 characters in length\")\n        # etc...\n        return value\n\n\nclass UserSchema(Schema):\n\n    username = StringField(required=True)\n    password = PasswordField()\n\n\npayload = {\n    \"username\": \"FooBar\",\n    \"password\": \"password\"\n}\n\ntry:\n    result = UserSchema.parse(payload)\nexcept ValidationError as e:\n    print(e)  # Value for 'password' must be at least 9 characters in length\n    \npayload = {\n    \"username\": \"FooBar\",\n    \"password\": \"password12345!\"\n}\n\nresult = UserSchema.parse(payload)\nprint(result.password)  # password12345!\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjulian-nash%2Fliaison","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjulian-nash%2Fliaison","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjulian-nash%2Fliaison/lists"}