Skip to content

Commit

Permalink
General maintainance (#7)
Browse files Browse the repository at this point in the history
* Bump dependencies
* Migrate from flake8 to Ruff
* Add support for Python 3.12
  • Loading branch information
drhagen authored Oct 14, 2023
1 parent 9322394 commit a05d8aa
Show file tree
Hide file tree
Showing 19 changed files with 565 additions and 864 deletions.
21 changes: 13 additions & 8 deletions .github/workflows/python.yml → .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
name: python
name: CI

on: [push]
on:
pull_request:
push:
branches:
- master

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

jobs:
test:
strategy:
matrix:
python-version: ["3.10", "3.11"]
python-version: ["3.10", "3.11", "3.12"]
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
Expand Down Expand Up @@ -60,7 +64,7 @@ jobs:
uses: codecov/codecov-action@v3
with:
fail_ci_if_error: true
quality:
lint:
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
Expand All @@ -75,14 +79,15 @@ jobs:
- name: Install environment
run: poetry install
- name: Run code quality checks
run: poetry run nox -s black isort flake8
run: poetry run nox -s lint
poetry-check:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Checkout repo
uses: actions/checkout@v3
- name: Install Poetry
run: pipx install poetry==${{ env.poetry-version }}
- name: Set up Python
- name: Install Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.python-version }}
Expand Down
62 changes: 31 additions & 31 deletions noxfile.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,44 @@
import nox_poetry
import nox
from nox_poetry import Session, session

nox.options.sessions = ["test", "coverage", "lint"]

@nox_poetry.session(python=["3.10", "3.11"])
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")

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

@nox_poetry.session(python=["3.10", "3.11"])
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")

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

@nox_poetry.session(python=["3.10", "3.11"])
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")

@session(python=["3.10", "3.11", "3.12"])
def test_numpy(s: Session):
s.install(".[numpy]", "pytest", "pytest-cov")
s.env["COVERAGE_FILE"] = f".coverage.numpy.{s.python}"
s.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")

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

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

@session(venv_backend="none")
def fmt(s: Session) -> None:
s.run("ruff", "check", ".", "--select", "I", "--fix")
s.run("black", ".")

@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")
@session(venv_backend="none")
def lint(s: Session) -> None:
s.run("black", "--check", ".")
s.run("ruff", "check", ".")
1,108 changes: 396 additions & 712 deletions poetry.lock

Large diffs are not rendered by default.

71 changes: 41 additions & 30 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,23 @@ classifiers = [

[tool.poetry.dependencies]
python = "^3.10"
typing_extensions = "^4.3.0"
fastapi = { version = "^0.78.0", optional = true }
pydantic = { version = "^1.9.1", optional = true }
numpy = { version = "^1.23.0", optional = true }
typing_extensions = "^4.3"
fastapi = { version = "^0.100", optional = true }
pydantic = { version = "^1.10", optional = true }
# Lie about numpy only being needed below 3.13 in order to satisfy its Python ceiling
numpy = { version = "^1.25", optional = true, python = "<3.13" }

[tool.poetry.dev-dependencies]
nox_poetry = "^1.0.2"
pre-commit = "^2.19.0"

# Test
pytest = "^7.1.1"
coverage = { version = "^6.3.2", extras = ["toml"] }
pytest-cov = "^3.0.0"
requests = "^2.28.1"
pytest = "^7.2"
pytest-cov = "*"
httpx = "*" # Needed by starlette testclient

# Black
black = ">=22.3.0"

# Isort
isort = "^5.9.3"

# Flake8
flake8 = "^4.0.1"
pyproject-flake8 = "^0.0.1a3"
pep8-naming = "^0.12.1"
flake8-noqa = "^1.2.1"
# Lint
black = ">=22.3"
ruff = ">=0.0.275"

[tool.poetry.extras]
fastapi = ["fastapi", "pydantic"]
Expand Down Expand Up @@ -72,18 +63,38 @@ line-length = 99
preview = true


[tool.isort]
profile = "black"
line_length = 99
extra_standard_library = ["typing_extensions"]
[tool.ruff]
src = ["src"]
line-length = 99
extend-select = [
"I", # isort
"N", # pep8-naming
"RUF", # ruff
"B", # flake8-bugbear
"N", # flake8-broken-line
"C4", # flake8-comprehensions
"PIE", # flake8-pie
"PT", # flake8-pytest-style
"PTH", # flake8-use-pathlib
"ERA", # flake8-eradicate
]
# F402: Variable shadowing is perfectly fine
# PT011: testing for broad exceptions is fine
extend-ignore = ["F402", "PT011"]

[tool.ruff.per-file-ignores]
# F401: unused-import; Allow unused imports in __init__.py files
"__init__.py" = ["F401"]

[tool.ruff.isort]
extra-standard-library = ["typing_extensions"]

[tool.ruff.pep8-naming]
classmethod-decorators = ["serialite._descriptors.classproperty"]

[tool.ruff.flake8-bugbear]
extend-immutable-calls = ["serialite.field", "fastapi.Body"]

[tool.flake8]
max-line-length = 99
noqa-require-code = true
classmethod-decorators = ["classmethod", "classproperty"]
# F402: Variable shadowing is perfectly fine
extend-ignore = ["F402"]

[build-system]
requires = ["poetry-core>=1.0.0"]
Expand Down
18 changes: 9 additions & 9 deletions src/serialite/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from ._base import Serializable, Serializer # noqa: F401
from ._dataclass import field # noqa: F401
from ._decorators import abstract_serializable, serializable # noqa: F401
from ._dispatcher import serializer # noqa: F401
from ._fields_serializer import ( # noqa: F401
from ._base import Serializable, Serializer
from ._dataclass import field
from ._decorators import abstract_serializable, serializable
from ._dispatcher import serializer
from ._fields_serializer import (
AccessPermissions,
FieldsSerializer,
FieldsSerializerField,
Expand All @@ -11,22 +11,22 @@
empty_default,
no_default,
)
from ._implementations import * # noqa: F401, F403
from ._mixins import AbstractSerializableMixin, SerializableMixin # noqa: F401
from ._implementations import * # noqa: F403
from ._mixins import AbstractSerializableMixin, SerializableMixin
from ._monkey_patches import (
monkey_patch_fastapi_create_cloned_field,
monkey_patch_pydantic_get_flat_models_from_model,
monkey_patch_pydantic_instancecheck,
monkey_patch_pydantic_model_type_schema,
monkey_patch_pydantic_subclasscheck,
)
from ._result import ( # noqa: F401
from ._result import (
DeserializationError,
DeserializationFailure,
DeserializationResult,
DeserializationSuccess,
)
from ._stable_set import StableSet # noqa: F401
from ._stable_set import StableSet

monkey_patch_pydantic_subclasscheck()
monkey_patch_pydantic_instancecheck()
Expand Down
2 changes: 1 addition & 1 deletion src/serialite/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def dict(
include: AbstractSetIntStr | MappingIntStrAny = None,
exclude: AbstractSetIntStr | MappingIntStrAny = None,
by_alias: bool = False,
skip_defaults: bool = None,
skip_defaults: bool = None, # noqa: RUF013
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
Expand Down
9 changes: 5 additions & 4 deletions src/serialite/_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
from ._fields_serializer import FieldsSerializer, SingleField, no_default
from ._mixins import AbstractSerializableMixin, SerializableMixin

# Allow commented out code in this file because it is important documentation
# ruff: noqa: ERA001


# Inspired by https://stackoverflow.com/a/14412901/1485877
def flexible_decorator(dec):
Expand Down Expand Up @@ -93,8 +96,6 @@ def infer_fields_serializer(cls):
field_default = maybe_default
elif maybe_factory_default is not MISSING:
field_default = maybe_factory_default()
else:
assert False

serializer_fields[field.name] = SingleField(field_serializer, default=field_default)

Expand All @@ -113,7 +114,7 @@ def serializable(cls):
"""
infer_fields_serializer(cls)

new_bases = (SerializableMixin,) + cls.__bases__
new_bases = (SerializableMixin, *cls.__bases__)
try:
# Try to mutate the bases of the class in place.
# This will fail if the only base is `object`.
Expand Down Expand Up @@ -189,7 +190,7 @@ def abstract_serializable(cls):
"""
infer_subclass_serializers(cls)

new_bases = (AbstractSerializableMixin,) + cls.__bases__
new_bases = (AbstractSerializableMixin, *cls.__bases__)
try:
# Try to mutate the bases of the class in place.
# This will fail if the only base is `object`.
Expand Down
2 changes: 1 addition & 1 deletion src/serialite/_dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def dispatch(cls: type):
# issubclass does not work on Union and Optional
# WeakKeyDictionary does not work on UnionType
# must bypass the dispatcher
if len(cls.__args__) == 2 and cls.__args__[1] is type(None): # noqa: E721
if len(cls.__args__) == 2 and cls.__args__[1] is type(None):
# Optional is just a Union with NoneType in the second argument
return optional_to_data
else:
Expand Down
2 changes: 1 addition & 1 deletion src/serialite/_fields_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def __init__(
default: Any = no_default,
hide_default: bool = True,
access: AccessPermissions = AccessPermissions.read_write,
to_data: str = None,
to_data: str = None, # noqa: RUF013
):
"""A field serializer with a direct data field to object field mapping.
Expand Down
32 changes: 16 additions & 16 deletions src/serialite/_implementations/__init__.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
from ._boolean import BooleanSerializer # noqa: F401
from ._date_time import DateTimeSerializer # noqa: F401
from ._dictionary import OrderedDictSerializer, RawDictSerializer # noqa: F401
from ._float import FloatSerializer # noqa: F401
from ._integer import ( # noqa: F401
from ._boolean import BooleanSerializer
from ._date_time import DateTimeSerializer
from ._dictionary import OrderedDictSerializer, RawDictSerializer
from ._float import FloatSerializer
from ._integer import (
IntegerSerializer,
NonnegativeIntegerSerializer,
PositiveIntegerSerializer,
)
from ._json import JsonSerializer # noqa: F401
from ._list import ListSerializer # noqa: F401
from ._literal import LiteralSerializer # noqa: F401
from ._none import NoneSerializer # noqa: F401
from ._path import PathSerializer # noqa: F401
from ._reserved import ReservedSerializer # noqa: F401
from ._string import StringSerializer # noqa: F401
from ._tuple import TupleSerializer # noqa: F401
from ._union import OptionalSerializer, TryUnionSerializer # noqa: F401
from ._uuid import UuidSerializer # noqa: F401
from ._json import JsonSerializer
from ._list import ListSerializer
from ._literal import LiteralSerializer
from ._none import NoneSerializer
from ._path import PathSerializer
from ._reserved import ReservedSerializer
from ._string import StringSerializer
from ._tuple import TupleSerializer
from ._union import OptionalSerializer, TryUnionSerializer
from ._uuid import UuidSerializer

try:
from ._array import ArraySerializer # noqa: F401
from ._array import ArraySerializer
except ImportError:
pass
2 changes: 1 addition & 1 deletion src/serialite/_implementations/_dictionary.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def __init__(
self,
value_serializer: Serializer[Value],
*,
key_serializer: Serializer[str] = StringSerializer(),
key_serializer: Serializer[str] = StringSerializer(), # noqa: B008
):
self.value_serializer = value_serializer
self.key_serializer = key_serializer
Expand Down
2 changes: 1 addition & 1 deletion src/serialite/_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class AbstractSerializableMixin(Serializable):
remainder of the dictionary to.
"""

__subclass_serializers__: dict[str, Serializable]
__subclass_serializers__: ClassVar[dict[str, Serializable]]

@classmethod
def from_data(cls, data):
Expand Down
2 changes: 1 addition & 1 deletion tests/fastapi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest

try:
import fastapi # noqa: F401
import fastapi
except ImportError:
pytest.skip("FastAPI not available", allow_module_level=True)
Loading

0 comments on commit a05d8aa

Please sign in to comment.