Skip to content

Commit

Permalink
chore: Update dev tools, style (#44)
Browse files Browse the repository at this point in the history
---------

Signed-off-by: Jan Kowalleck <jan.kowalleck@gmail.com>
  • Loading branch information
jkowalleck authored Dec 19, 2023
1 parent 7e77788 commit b05e50b
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 55 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/manual-release-candidate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ env:
DIST_ARTIFACT: python-dist
PYTHON_SEMANTIC_RELEASE_VERSION: "7.34.6"
PYTHON_VERSION_DEFAULT: "3.11"
POETRY_VERSION: "1.4.1"
POETRY_VERSION: "1.7.1"

jobs:
release_candidate:
Expand Down
10 changes: 7 additions & 3 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ concurrency:
env:
REPORTS_DIR: CI_reports
PYTHON_VERSION_DEFAULT: "3.11"
POETRY_VERSION: "1.4.1"
POETRY_VERSION: "1.7.1"

jobs:
coding-standards:
Expand Down Expand Up @@ -80,10 +80,10 @@ jobs:
- # test with the locked dependencies
python-version: '3.11'
toxenv-factor: 'locked'

- # test with the lowest dependencies
python-version: '3.7'
toxenv-factor: 'lowest'
poetry-version: '1.4.1' # py3.7 does not allow latest poetry
steps:
- name: Checkout
# see https://github.com/actions/checkout
Expand All @@ -100,7 +100,7 @@ jobs:
# see https://github.com/marketplace/actions/setup-poetry
uses: Gr1N/setup-poetry@v8
with:
poetry-version: ${{ env.POETRY_VERSION }}
poetry-version: ${{ matrix.poetry-version || env.POETRY_VERSION }}

- name: Install Dependencies
run: poetry install --no-interaction --no-root
Expand Down Expand Up @@ -145,6 +145,10 @@ jobs:
python-version: ${{ matrix.python-version }}
architecture: 'x64'

- name: override POETRY_VERSION
if: startsWith(matrix.python-version, '3.7')
run: echo "POETRY_VERSION=1.4.1" >> "$GITHUB_ENV"
shell: bash
- name: Install poetry
# see https://github.com/marketplace/actions/setup-poetry
uses: Gr1N/setup-poetry@v8
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ concurrency:

env:
PYTHON_VERSION_DEFAULT: "3.11"
POETRY_VERSION: "1.4.1"
POETRY_VERSION: "1.7.1"

jobs:
release:
Expand Down
9 changes: 8 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ Pull requests are welcome, but please read this guidelines first.

This project uses [poetry]. Have it installed and setup first.

Attention:
Even though this library is designed to be runnable on python>=3.7
some development-tools require python>=3.8.1

To install dev-dependencies and tools:

```shell
Expand All @@ -20,9 +24,12 @@ Get it all applied via:

```shell
poetry run isort .
poetry run flake8 serializable/ tests/
poetry run autopep8 -ir serializable/ tests/
```

This project prefers `f'strings'` over `'string'.format()`.
This project prefers `'single quotes'` over `"double quotes"`.

## Documentation

This project uses [Sphinx] to generate documentation which is automatically published to [RTFD][link_rtfd].
Expand Down
22 changes: 14 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,22 @@ python = "^3.7"
defusedxml = "^0.7.1"

[tool.poetry.dev-dependencies]
coverage = "7.2.7"
flake8 = "5.0.4" # last version supporting Python 3.7
flake8-annotations = "2.9.1" # last version supporting Python 3.7
flake8-bugbear = "23.3.12" # last version supporting Python 3.7
flake8-isort = "6.0.0"
isort = "5.11.5" # last version supporting Python 3.7
mypy = "1.4.1"
tox = "3.28.0"
typing-extensions = {version = "4.7.1", python = "<3.8"}
tox = "3.28.0"
coverage = "7.2.7"
xmldiff = "2.6.3"
mypy = [
{version = "1.7.1", python = ">=3.8"},
{version = "1.4.1", python = "<3.8"}
]
autopep8 = {version = "2.0.4", python = ">=3.8"}
isort = {version = "5.13.2", python = ">=3.8"}
flake8 = { version="6.1.0", python=">=3.8.1" }
flake8-annotations = { version="3.0.1", python=">=3.8.1" }
flake8-bugbear = { version="23.12.2", python=">=3.8.1" }
flake8-isort = {version = "6.1.1", python = ">=3.8"}
flake8-quotes = {version = "3.3.2", python = ">=3.8"}
flake8-use-fstring = {version = "1.4", python = ">=3.8"}

[build-system]
requires = ["poetry-core>=1.0.0"]
Expand Down
57 changes: 38 additions & 19 deletions serializable/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,38 @@
from decimal import Decimal
from io import StringIO, TextIOBase
from json import JSONEncoder
from sys import version_info
from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, Type, TypeVar, Union, cast, overload
from typing import (
TYPE_CHECKING,
Any,
Callable,
Dict,
Iterable,
List,
Optional,
Set,
Tuple,
Type,
TypeVar,
Union,
cast,
overload,
)
from xml.etree.ElementTree import Element, SubElement

from defusedxml import ElementTree as SafeElementTree # type: ignore

from .formatters import BaseNameFormatter, CurrentFormatter
from .helpers import BaseHelper

if version_info >= (3, 8):
from typing import Literal, Protocol # type:ignore[attr-defined]
if TYPE_CHECKING:
import sys
if sys.version_info >= (3, 8):
from typing import Literal, Protocol # type:ignore[attr-defined]
else:
from typing_extensions import Literal, Protocol # type:ignore[assignment]
else:
from typing_extensions import Literal, Protocol # type:ignore[assignment]
from abc import ABC
Protocol = ABC

# `Intersection` is still not implemented, so it is interim replaced by Union for any support
# see section "Intersection" in https://peps.python.org/pep-0483/
Expand All @@ -63,7 +82,7 @@ class ViewType:
pass


_F = TypeVar("_F", bound=Callable[..., Any])
_F = TypeVar('_F', bound=Callable[..., Any])
_T = TypeVar('_T')
_E = TypeVar('_E', bound=enum.Enum)

Expand Down Expand Up @@ -838,7 +857,7 @@ def parse_type_deferred(self) -> None:
def _parse_type(self, type_: Any) -> None:
self._type_ = type_ = self._handle_forward_ref(t_=type_)

if type(type_) == str:
if type(type_) is str:
type_to_parse = str(type_)
# Handle types that are quoted strings e.g. 'SortedSet[MyObject]' or 'Optional[SortedSet[MyObject]]'
if type_to_parse.startswith('typing.Optional['):
Expand All @@ -852,30 +871,30 @@ def _parse_type(self, type_: Any) -> None:
if match:
results = match.groupdict()
if results.get('array_type', None) in self._SORTED_CONTAINERS_TYPES:
mapped_array_type = self._SORTED_CONTAINERS_TYPES.get(str(results.get("array_type")))
mapped_array_type = self._SORTED_CONTAINERS_TYPES.get(str(results.get('array_type')))
self._is_array = True
try:
# Will load any class already loaded assuming fully qualified name
self._type_ = eval(f'{mapped_array_type}[{results.get("array_of")}]')
self._concrete_type = eval(str(results.get("array_of")))
self._concrete_type = eval(str(results.get('array_of')))
except NameError:
# Likely a class that is missing its fully qualified name
_k: Optional[Any] = None
for _k_name, _oml_sc in ObjectMetadataLibrary.klass_mappings.items():
if _oml_sc.name == results.get("array_of"):
if _oml_sc.name == results.get('array_of'):
_k = _oml_sc.klass

if _k is None:
# Perhaps a custom ENUM?
for _enum_klass in ObjectMetadataLibrary.custom_enum_klasses:
if _enum_klass.__name__ == results.get("array_of"):
if _enum_klass.__name__ == results.get('array_of'):
_k = _enum_klass

if _k is None:
self._type_ = type_ # type: ignore
self._deferred_type_parsing = True
ObjectMetadataLibrary.defer_property_type_parsing(
prop=self, klasses=[str(results.get("array_of"))]
prop=self, klasses=[str(results.get('array_of'))]
)
return

Expand All @@ -890,25 +909,25 @@ def _parse_type(self, type_: Any) -> None:
try:
# Will load any class already loaded assuming fully qualified name
self._type_ = eval(f'{mapped_array_type}[{results.get("array_of")}]')
self._concrete_type = eval(str(results.get("array_of")))
self._concrete_type = eval(str(results.get('array_of')))
except NameError:
# Likely a class that is missing its fully qualified name
_l: Optional[Any] = None
for _k_name, _oml_sc in ObjectMetadataLibrary.klass_mappings.items():
if _oml_sc.name == results.get("array_of"):
if _oml_sc.name == results.get('array_of'):
_l = _oml_sc.klass

if _l is None:
# Perhaps a custom ENUM?
for _enum_klass in ObjectMetadataLibrary.custom_enum_klasses:
if _enum_klass.__name__ == results.get("array_of"):
if _enum_klass.__name__ == results.get('array_of'):
_l = _enum_klass

if _l is None:
self._type_ = type_ # type: ignore
self._deferred_type_parsing = True
ObjectMetadataLibrary.defer_property_type_parsing(
prop=self, klasses=[str(results.get("array_of"))]
prop=self, klasses=[str(results.get('array_of'))]
)
return

Expand Down Expand Up @@ -945,7 +964,7 @@ def _parse_type(self, type_: Any) -> None:

def _handle_forward_ref(self, t_: Any) -> Any:
if 'ForwardRef' in str(t_):
return str(t_).replace('ForwardRef(\'', '"').replace('\')', '"')
return str(t_).replace("ForwardRef('", '"').replace("')", '"')
else:
return t_

Expand Down Expand Up @@ -1116,7 +1135,7 @@ def register_property_type_mapping(cls, qual_name: str, mapped_type: type) -> No


@overload
def serializable_enum(cls: Literal[None] = None) -> Callable[[Type[_E]], Type[_E]]:
def serializable_enum(cls: 'Literal[None]' = None) -> Callable[[Type[_E]], Type[_E]]:
...


Expand Down Expand Up @@ -1146,7 +1165,7 @@ def decorate(kls: Type[_E]) -> Type[_E]:

@overload
def serializable_class(
cls: Literal[None] = None, *,
cls: 'Literal[None]' = None, *,
name: Optional[str] = ...,
serialization_types: Optional[Iterable[SerializationType]] = ...,
ignore_during_deserialization: Optional[Iterable[str]] = ...
Expand Down
2 changes: 1 addition & 1 deletion serializable/formatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,4 @@ def decode(cls, property_name: str) -> str:


class CurrentFormatter:
formatter: Type["BaseNameFormatter"] = CamelCasePropertyNameFormatter
formatter: Type['BaseNameFormatter'] = CamelCasePropertyNameFormatter
2 changes: 1 addition & 1 deletion serializable/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ def deserialize(cls, o: Any) -> datetime:
o = str(o)[1:]

# Ensure any milliseconds are 6 digits
o = re.sub(r"\.(\d{1,6})", lambda v: f'.{int(v.group()[1:]):06}', str(o))
o = re.sub(r'\.(\d{1,6})', lambda v: f'.{int(v.group()[1:]):06}', str(o))

if str(o).endswith('Z'):
# Replace ZULU time with 00:00 offset
Expand Down
12 changes: 6 additions & 6 deletions tests/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ def serialize(cls, o: Any) -> Set[str]:
raise ValueError(f'Attempt to serialize a non-set: {o.__class__}')

@classmethod
def deserialize(cls, o: Any) -> Set["BookReference"]:
def deserialize(cls, o: Any) -> Set['BookReference']:
print(f'Deserializing {o} ({type(o)})')
references: Set["BookReference"] = set()
references: Set['BookReference'] = set()
if isinstance(o, list):
for v in o:
references.add(BookReference(ref=v))
Expand Down Expand Up @@ -141,7 +141,7 @@ def address(self) -> Optional[str]:

@property
@serializable.include_none(SchemaVersion2)
@serializable.include_none(SchemaVersion3, "RUBBISH")
@serializable.include_none(SchemaVersion3, 'RUBBISH')
def email(self) -> Optional[str]:
return self._email

Expand Down Expand Up @@ -189,7 +189,7 @@ def __hash__(self) -> int:
@serializable.serializable_class
class BookReference:

def __init__(self, *, ref: str, references: Optional[Iterable["BookReference"]] = None) -> None:
def __init__(self, *, ref: str, references: Optional[Iterable['BookReference']] = None) -> None:
self.ref = ref
self.references = set(references or {})

Expand All @@ -207,11 +207,11 @@ def ref(self, ref: str) -> None:
@serializable.json_name('refersTo')
@serializable.type_mapping(ReferenceReferences)
@serializable.xml_array(serializable.XmlArraySerializationType.FLAT, 'reference')
def references(self) -> Set["BookReference"]:
def references(self) -> Set['BookReference']:
return self._references

@references.setter
def references(self, references: Iterable["BookReference"]) -> None:
def references(self, references: Iterable['BookReference']) -> None:
self._references = set(references)

def __eq__(self, other: object) -> bool:
Expand Down
Loading

0 comments on commit b05e50b

Please sign in to comment.