# serious
[![PyPI](https://img.shields.io/pypi/v/serious)][pypi]
[![Build Status](https://img.shields.io/azure-devops/build/misha-drachuk/serious/2)](https://dev.azure.com/misha-drachuk/serious/_build/latest?definitionId=1&branchName=master)
[![Test Coverage](https://img.shields.io/coveralls/github/mdrachuk/serious/master)](https://coveralls.io/github/mdrachuk/serious)
[![Supported Python](https://img.shields.io/pypi/pyversions/serious)][pypi]
[![Documentation](https://img.shields.io/readthedocs/serious)][docs]
A dataclass model toolkit: serialization, validation, and more.
[Documentation][docs]
## Features
- Model definitions in pure Python.
- Validation showing up in code coverage.
- Type annotations for all public-facing APIs.
- (Optionally) ensures immutability.
- Easily extensible.
- Made for people.
- Documented rigorously.
## Basics
### Installation
Available from [PyPI][pypi]:
```shell
pip install serious
```
### Quick Example
Central part of Serious API are different [Models][doc-models].
Given a regular dataclass:
```python
from dataclasses import dataclass
@dataclass
class Person:
name: str
```
Let’s create a `JsonModel`:
```python
from serious.json import JsonModel
model = JsonModel(Person)
```
And use its [dump/load methods][doc-serialization]:
```python
person = Person('Albert Einstein')
model.dump(person) # {"name": "Albert Einstein"}
```
### Validation
To add validation to the example above all we need is to add `__validate__` method to person:
```python
from dataclasses import dataclass
from typing import Optional
from serious import ValidationError, Email
@dataclass
class Person:
name: str
email: Optional[Email]
phone: Optional[str]
def __validate__(self):
if len(self.name) == 0:
raise ValidationError('Every person needs a name')
if self.phone is None and self.email is None:
raise ValidationError('At least some contact should be present')
```
[More on validation.][doc-validation]
### Supported formats:
- [x] [JSON][doc-json-model]
- [x] [Python Dictionaries][doc-dict-model]
- [ ] YAML
- [ ] Form data
### Supported field types
[More in docs.][doc-types]
- Other dataclasses
- Primitives: `str`, `int`, `float`, `bool`
- Dictionaries: only with string keys: `Dict[str, Any]`
- Lists, [sets][set], [deques][deque]: python collections of any serializable type
- [Tuples][tuple] both with and without ellipsis:
- tuples as set of independent elements (e.g. `Tuple[str, int, date]`)
- with ellipses, acting as a frozen list (`Tuple[str, ...]`)
- [Enumerations][enum] by value:
- of primitives (e.g. `OperatingSystem(Enum)`)
- typed enums (`Color(str, Enum)` and `FilePermission(IntFlag)`)
- [Decimal][decimal]: encoded to JSON as string
- [Datetime][datetime], [date][date] and [time][time]: encoded to the [ISO 8601][iso8601] formatted string
- [UUID][uuid]
- `serious.types.Timestamp`: a UTC timestamp since [UNIX epoch][epoch] as float ms value
- `serious.types.Email`: a string Tiny Type that supports validation and contains additional properties
- custom immutable alternatives to native python types in `serious.types`: `FrozenList`, `FrozenDict`
## A bigger example
```python
from dataclasses import dataclass
from serious import JsonModel, ValidationError
from typing import List
from enum import Enum
class Specialty(Enum):
Worker = 1
Fool = 2
@dataclass(frozen=True)
class Minion:
name: str
type: Specialty
@dataclass(frozen=True)
class Boss:
name: str
minions: List[Minion]
def __validate__(self):
if len(self.minions) < 2:
raise ValidationError('What kind of boss are you?')
boss = Boss("me", [Minion('evil minion', Specialty.Fool), Minion('very evil minion', Specialty.Worker)])
boss_json = """{
"name": "me",
"minions": [
{
"name": "evil minion",
"type": "Fool"
},
{
"name": "very evil minion",
"type": "Worker"
}
]
}"""
model = JsonModel(Boss, indent=4)
assert model.dump(boss) == boss_json
assert model.load(boss_json) == boss
```
## Acknowledgements
Initially, a fork of [@lidatong/dataclasses-json](https://github.com/lidatong/dataclasses-json).
[pypi]: https://pypi.org/project/serious/
[dataclass]: https://docs.python.org/3/library/dataclasses.html
[iso8601]: https://en.wikipedia.org/wiki/ISO_8601
[epoch]: https://en.wikipedia.org/wiki/Unix_time
[enum]: https://docs.python.org/3/library/enum.html
[decimal]: https://docs.python.org/3/library/decimal.html
[tuple]: https://docs.python.org/3/library/stdtypes.html#tuple
[list]: https://docs.python.org/3/library/stdtypes.html#list
[set]: https://docs.python.org/3/library/stdtypes.html#set
[deque]: https://docs.python.org/3.7/library/collections.html#collections.deque
[datetime]: https://docs.python.org/3.7/library/datetime.html#datetime.datetime
[date]: https://docs.python.org/3.7/library/datetime.html#datetime.date
[time]: https://docs.python.org/3.7/library/datetime.html#datetime.time
[uuid]: https://docs.python.org/3.7/library/uuid.html?highlight=uuid#uuid.UUID
[doc-types]: https://serious.readthedocs.io/en/latest/types/
[doc-models]: https://serious.readthedocs.io/en/latest/models/
[doc-json-model]: https://serious.readthedocs.io/en/latest/models/#jsonmodel
[doc-dict-model]: https://serious.readthedocs.io/en/latest/models/#dictmodel
[doc-serialization]: https://serious.readthedocs.io/en/latest/serialization/ (Serialization documentation)
[doc-validation]: https://serious.readthedocs.io/en/latest/validation/ (Validation documentation)
[docs]: https://serious.readthedocs.io/en/latest/
Raw data
{
"_id": null,
"home_page": "https://github.com/mdrachuk/serious",
"name": "serious",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "dataclasses json serialization",
"author": "mdrachuk",
"author_email": "misha@drach.uk",
"download_url": "https://files.pythonhosted.org/packages/c2/0d/4521c2b0c8ed477414bd1f53f3b4fcd6d2a28c2a7a001221dd589cc9322a/serious-1.6.2.tar.gz",
"platform": null,
"description": "# serious\n[![PyPI](https://img.shields.io/pypi/v/serious)][pypi]\n[![Build Status](https://img.shields.io/azure-devops/build/misha-drachuk/serious/2)](https://dev.azure.com/misha-drachuk/serious/_build/latest?definitionId=1&branchName=master)\n[![Test Coverage](https://img.shields.io/coveralls/github/mdrachuk/serious/master)](https://coveralls.io/github/mdrachuk/serious)\n[![Supported Python](https://img.shields.io/pypi/pyversions/serious)][pypi]\n[![Documentation](https://img.shields.io/readthedocs/serious)][docs]\n\nA dataclass model toolkit: serialization, validation, and more.\n\n[Documentation][docs]\n\n\n## Features\n- Model definitions in pure Python.\n- Validation showing up in code coverage.\n- Type annotations for all public-facing APIs.\n- (Optionally) ensures immutability.\n- Easily extensible.\n- Made for people.\n- Documented rigorously.\n\n## Basics\n### Installation\nAvailable from [PyPI][pypi]:\n```shell\npip install serious\n```\n\n### Quick Example\n\nCentral part of Serious API are different [Models][doc-models].\n\nGiven a regular dataclass:\n```python\nfrom dataclasses import dataclass\n\n@dataclass\nclass Person:\n name: str\n```\n\nLet\u2019s create a `JsonModel`: \n```python\nfrom serious.json import JsonModel\n \nmodel = JsonModel(Person)\n```\n\nAnd use its [dump/load methods][doc-serialization]:\n```python\nperson = Person('Albert Einstein')\n\nmodel.dump(person) # {\"name\": \"Albert Einstein\"}\n```\n\n### Validation\nTo add validation to the example above all we need is to add `__validate__` method to person:\n```python\nfrom dataclasses import dataclass\nfrom typing import Optional\nfrom serious import ValidationError, Email\n\n@dataclass\nclass Person:\n name: str\n email: Optional[Email]\n phone: Optional[str]\n\n def __validate__(self):\n if len(self.name) == 0:\n raise ValidationError('Every person needs a name')\n if self.phone is None and self.email is None:\n raise ValidationError('At least some contact should be present')\n```\n\n[More on validation.][doc-validation]\n\n\n### Supported formats:\n- [x] [JSON][doc-json-model]\n- [x] [Python Dictionaries][doc-dict-model]\n- [ ] YAML\n- [ ] Form data\n\n\n### Supported field types\n[More in docs.][doc-types]\n\n- Other dataclasses\n- Primitives: `str`, `int`, `float`, `bool`\n- Dictionaries: only with string keys: `Dict[str, Any]` \n- Lists, [sets][set], [deques][deque]: python collections of any serializable type\n- [Tuples][tuple] both with and without ellipsis:\n - tuples as set of independent elements (e.g. `Tuple[str, int, date]`) \n - with ellipses, acting as a frozen list (`Tuple[str, ...]`)\n- [Enumerations][enum] by value:\n - of primitives (e.g. `OperatingSystem(Enum)`) \n - typed enums (`Color(str, Enum)` and `FilePermission(IntFlag)`)\n- [Decimal][decimal]: encoded to JSON as string \n- [Datetime][datetime], [date][date] and [time][time]:\u00a0encoded to the [ISO 8601][iso8601] formatted string\n- [UUID][uuid]\n- `serious.types.Timestamp`: a UTC timestamp since [UNIX epoch][epoch] as float ms value \n- `serious.types.Email`: a string Tiny Type that supports validation and contains additional properties \n- custom immutable alternatives to native python types in `serious.types`: `FrozenList`, `FrozenDict`\n\n## A bigger example\n\n```python\nfrom dataclasses import dataclass\nfrom serious import JsonModel, ValidationError\nfrom typing import List\nfrom enum import Enum\n\nclass Specialty(Enum):\n Worker = 1\n Fool = 2\n\n\n@dataclass(frozen=True)\nclass Minion:\n name: str\n type: Specialty\n\n\n@dataclass(frozen=True)\nclass Boss:\n name: str\n minions: List[Minion]\n \n def __validate__(self):\n if len(self.minions) < 2:\n raise ValidationError('What kind of boss are you?')\n\n\nboss = Boss(\"me\", [Minion('evil minion', Specialty.Fool), Minion('very evil minion', Specialty.Worker)])\nboss_json = \"\"\"{\n \"name\": \"me\",\n \"minions\": [\n {\n \"name\": \"evil minion\",\n \"type\": \"Fool\"\n },\n {\n \"name\": \"very evil minion\",\n \"type\": \"Worker\"\n }\n ]\n}\"\"\"\n\nmodel = JsonModel(Boss, indent=4)\n\nassert model.dump(boss) == boss_json\nassert model.load(boss_json) == boss\n```\n\n\n## Acknowledgements\nInitially, a fork of [@lidatong/dataclasses-json](https://github.com/lidatong/dataclasses-json).\n\n[pypi]: https://pypi.org/project/serious/\n[dataclass]: https://docs.python.org/3/library/dataclasses.html\n[iso8601]: https://en.wikipedia.org/wiki/ISO_8601\n[epoch]: https://en.wikipedia.org/wiki/Unix_time\n[enum]: https://docs.python.org/3/library/enum.html\n[decimal]: https://docs.python.org/3/library/decimal.html\n[tuple]: https://docs.python.org/3/library/stdtypes.html#tuple\n[list]: https://docs.python.org/3/library/stdtypes.html#list\n[set]: https://docs.python.org/3/library/stdtypes.html#set\n[deque]: https://docs.python.org/3.7/library/collections.html#collections.deque\n[datetime]: https://docs.python.org/3.7/library/datetime.html#datetime.datetime\n[date]: https://docs.python.org/3.7/library/datetime.html#datetime.date\n[time]: https://docs.python.org/3.7/library/datetime.html#datetime.time\n[uuid]: https://docs.python.org/3.7/library/uuid.html?highlight=uuid#uuid.UUID\n[doc-types]: https://serious.readthedocs.io/en/latest/types/\n[doc-models]: https://serious.readthedocs.io/en/latest/models/\n[doc-json-model]: https://serious.readthedocs.io/en/latest/models/#jsonmodel\n[doc-dict-model]: https://serious.readthedocs.io/en/latest/models/#dictmodel\n[doc-serialization]: https://serious.readthedocs.io/en/latest/serialization/ (Serialization documentation)\n[doc-validation]: https://serious.readthedocs.io/en/latest/validation/ (Validation documentation)\n[docs]: https://serious.readthedocs.io/en/latest/ \n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Easily serialize dataclasses to and from JSON",
"version": "1.6.2",
"project_urls": {
"Homepage": "https://github.com/mdrachuk/serious",
"Issues": "https://github.com/mdrachuk/serious/issues",
"Pipelines": "https://dev.azure.com/misha-drachuk/serious",
"Source": "https://github.com/mdrachuk/serious/"
},
"split_keywords": [
"dataclasses",
"json",
"serialization"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "a8563a444766462825506d48fd71b08a6a41b3fe6d9bc17bc2e08f2de44b0307",
"md5": "53783268d2e66461b48ebdb9b8828a15",
"sha256": "5327ffe1c30a95d91d33c6d9b2d6a98840016fc149cd4569523ac601741bc774"
},
"downloads": -1,
"filename": "serious-1.6.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "53783268d2e66461b48ebdb9b8828a15",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 33708,
"upload_time": "2024-12-15T21:40:53",
"upload_time_iso_8601": "2024-12-15T21:40:53.219549Z",
"url": "https://files.pythonhosted.org/packages/a8/56/3a444766462825506d48fd71b08a6a41b3fe6d9bc17bc2e08f2de44b0307/serious-1.6.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "c20d4521c2b0c8ed477414bd1f53f3b4fcd6d2a28c2a7a001221dd589cc9322a",
"md5": "500660544684d3c019917a020bc6c4c8",
"sha256": "fd1bf279d2ae860f057a893c214d2e5b538991b935ca0d1791ad9cb7b9c81ce5"
},
"downloads": -1,
"filename": "serious-1.6.2.tar.gz",
"has_sig": false,
"md5_digest": "500660544684d3c019917a020bc6c4c8",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 28086,
"upload_time": "2024-12-15T21:40:55",
"upload_time_iso_8601": "2024-12-15T21:40:55.821907Z",
"url": "https://files.pythonhosted.org/packages/c2/0d/4521c2b0c8ed477414bd1f53f3b4fcd6d2a28c2a7a001221dd589cc9322a/serious-1.6.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-12-15 21:40:55",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "mdrachuk",
"github_project": "serious",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [
{
"name": "coveralls",
"specs": [
[
"==",
"3.3.1"
]
]
},
{
"name": "cv",
"specs": [
[
"==",
"1.0.0"
]
]
},
{
"name": "mkdocs",
"specs": [
[
"==",
"1.5.2"
]
]
},
{
"name": "mypy",
"specs": [
[
"==",
"1.4.1"
]
]
},
{
"name": "pytest",
"specs": [
[
"==",
"7.4.0"
]
]
},
{
"name": "pytest-cov",
"specs": [
[
"==",
"4.1.0"
]
]
},
{
"name": "twine",
"specs": [
[
"==",
"5.1.1"
]
]
},
{
"name": "wheel",
"specs": [
[
"==",
"0.41.1"
]
]
},
{
"name": "sqlalchemy",
"specs": [
[
"==",
"2.0.34"
]
]
},
{
"name": "pydantic",
"specs": [
[
"==",
"2.9.1"
]
]
}
],
"lcname": "serious"
}