Skip to content

Commit

Permalink
Public release
Browse files Browse the repository at this point in the history
  • Loading branch information
drhagen committed Oct 13, 2022
0 parents commit 6dd19a7
Show file tree
Hide file tree
Showing 65 changed files with 5,435 additions and 0 deletions.
90 changes: 90 additions & 0 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: python

on: [push]

env:
python-version: "3.10"
poetry-version: "1.2.1"

jobs:
test:
strategy:
matrix:
python-version: ['3.10']
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Install Poetry
run: pipx install poetry==${{ env.poetry-version }}
- name: Install Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
cache: poetry
- name: Install environment
run: poetry install
- name: Test with nox
run: >-
poetry run nox -s
test-${{ matrix.python-version }}
test_fastapi-${{ matrix.python-version }}
test_numpy-${{ matrix.python-version }}
- name: Store coverage
uses: actions/upload-artifact@v3
with:
name: coverage
path: .coverage.*
coverage:
needs: test
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Install Poetry
run: pipx install poetry==${{ env.poetry-version }}
- name: Install Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.python-version }}
cache: poetry
- name: Install environment
run: poetry install
- name: Fetch coverage
uses: actions/download-artifact@v3
with:
name: coverage
- name: Combine coverage and generate report
run: poetry run nox -s coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
quality:
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Install Poetry
run: pipx install poetry==${{ env.poetry-version }}
- name: Install Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.python-version }}
cache: poetry
- name: Install environment
run: poetry install
- name: Run code quality checks
run: poetry run nox -s black isort flake8
poetry-check:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Install Poetry
run: pipx install poetry==${{ env.poetry-version }}
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.python-version }}
- name: Validate Poetry configuration and lockfile freshness
run: poetry lock --check
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
__pycache__
/.nox
/.coverage*
/coverage.xml
/dist
19 changes: 19 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright (c) 2022 David R Hagen

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Serialite

Serialite is a library serializing and deserializing arbitrarily complex objects in Python. You
apply the `@serializable` decorator to a dataclass to automatically create `to_data` and `from_data`
methods using the type annotations. Or for more control, inherit from the `SerializableMixin` and
implement the class attribute `__fields_serializer__`. For even more control, inherit from the
abstract base class `Serializer` and implement the `to_data` and `from_data` methods directly.

## Basics

The abstract base class is `Serializer`:

```python
class Serializer(Generic[Output]):
def from_data(self, data: Json) -> DeserializationSuccess[Output]: ...
def to_data(self, value: Output) -> Json: ...
```

The class is generic in the type of the object that it serializes. The two abstract methods
`from_data` and `to_data` are the key to the whole design, which revolves around getting objects to
and from JSON-serializable data, which are objects constructed entirely of `bool`s, `int`s,
`float`s, `list`s, and `dict`s. Such structures can be consumed by `json.dumps` to produce a string
and produced by `json.loads` after consuming a string. By basing the serialization around JSON
serializable data, complex structures can be built up or torn down piece by piece while
alternatively building up complex error messages during deserialization which pinpoint the location
in the structure where the bad data exist.

For new classes, it is recommended that the `Serializer` be implemented on the class itself. There is
an abstract base class `Serializable` that classes can inherit from to indicate this. There is a mixin
`SerializableMixin` that provides an implementation of `from_data` and `to_data` for any class that
implements the `__fields_serializer` class attribute.

For `dataclass`es, it is even easier. There is a decorator `serializable` that inserts
`SerializableMixin` into the list of base classes after the `dataclass` decorator has run and also
generates `__fields_serializer__` from the data class attributes.

Finding the correct serializer for each type can be a pain, so
`serializer(cls: type) -> Serializer` is provided as a convenience function. This is a single
dispatch function, which looks up the serializer registered for a particular type. For example,
`serializer(list[float])` will return `ListSerializer(FloatSerializer)`.
44 changes: 44 additions & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import nox_poetry


@nox_poetry.session(python=["3.10"])
def test(session: nox_poetry.Session):
session.install(".", "pytest", "pytest-cov")
session.env["COVERAGE_FILE"] = f".coverage.{session.python}"
session.run("python", "-m", "pytest", "--cov", "serialite")


@nox_poetry.session(python=["3.10"])
def test_fastapi(session: nox_poetry.Session):
session.install(".[fastapi]", "pytest", "pytest-cov", "requests")
session.env["COVERAGE_FILE"] = f".coverage.fastapi.{session.python}"
session.run("python", "-m", "pytest", "--cov", "serialite", "tests/fastapi")


@nox_poetry.session(python=["3.10"])
def test_numpy(session: nox_poetry.Session):
session.install(".[numpy]", "pytest", "pytest-cov")
session.env["COVERAGE_FILE"] = f".coverage.numpy.{session.python}"
session.run("python", "-m", "pytest", "--cov", "serialite", "tests/test_numpy.py")


@nox_poetry.session(venv_backend="none")
def coverage(session: nox_poetry.Session):
session.run("coverage", "combine")
session.run("coverage", "html")
session.run("coverage", "xml")


@nox_poetry.session(venv_backend="none")
def black(session: nox_poetry.Session):
session.run("black", "--check", ".")


@nox_poetry.session(venv_backend="none")
def isort(session: nox_poetry.Session):
session.run("isort", "--check", ".")


@nox_poetry.session(venv_backend="none")
def flake8(session: nox_poetry.Session):
session.run("pflake8", "src", "tests")

0 comments on commit 6dd19a7

Please sign in to comment.