Skip to content

Commit

Permalink
Update for old pythons compat
Browse files Browse the repository at this point in the history
  • Loading branch information
Guillaume Gauvrit committed Jan 9, 2024
1 parent 6c50e5c commit b038cf8
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 12 deletions.
1 change: 0 additions & 1 deletion src/blacksmith/domain/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from .exceptions import ConfigurationError, UnregisteredClientException
from .model import AbstractCollectionParser, Request, Response


TRequest = TypeVar("TRequest", bound=Request)

Schemas = Tuple[TRequest, Optional[Type[Response]]]
Expand Down
12 changes: 10 additions & 2 deletions src/blacksmith/service/_async/route_proxy.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Any, Dict, Generic, List, Mapping, Optional, Tuple, Type, Union

try:
from types import UnionType
from types import UnionType # type: ignore
except ImportError: # coverage: ignore
# python 3.7 compat
UnionType = Union # type: ignore
Expand Down Expand Up @@ -62,6 +62,14 @@ def is_union(typ: Type[Any]) -> bool:
return False


def is_instance_with_union(val: Any, typ: Type[Any]) -> bool:
# isinstance does not support union type in old interpreter,
if is_union(typ):
r = [isinstance(val, t) for t in typ.__args__] # type: ignore
return any(r)
return isinstance(val, typ)


def build_request(typ: Type[Any], params: Mapping[str, Any]) -> Request:
if is_union(typ):
err: Optional[Exception] = None
Expand Down Expand Up @@ -127,7 +135,7 @@ def _prepare_request(
build_params = build_request(param_schema, params)
elif params is None:
build_params = param_schema()
elif not isinstance(params, param_schema):
elif not is_instance_with_union(params, param_schema):
raise WrongRequestTypeException(
params.__class__, # type: ignore
method,
Expand Down
12 changes: 10 additions & 2 deletions src/blacksmith/service/_sync/route_proxy.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Any, Dict, Generic, List, Mapping, Optional, Tuple, Type, Union

try:
from types import UnionType
from types import UnionType # type: ignore
except ImportError: # coverage: ignore
# python 3.7 compat
UnionType = Union # type: ignore
Expand Down Expand Up @@ -62,6 +62,14 @@ def is_union(typ: Type[Any]) -> bool:
return False


def is_instance_with_union(val: Any, typ: Type[Any]) -> bool:
# isinstance does not support union type in old interpreter,
if is_union(typ):
r = [isinstance(val, t) for t in typ.__args__] # type: ignore
return any(r)
return isinstance(val, typ)


def build_request(typ: Type[Any], params: Mapping[str, Any]) -> Request:
if is_union(typ):
err: Optional[Exception] = None
Expand Down Expand Up @@ -127,7 +135,7 @@ def _prepare_request(
build_params = build_request(param_schema, params)
elif params is None:
build_params = param_schema()
elif not isinstance(params, param_schema):
elif not is_instance_with_union(params, param_schema):
raise WrongRequestTypeException(
params.__class__, # type: ignore
method,
Expand Down
4 changes: 2 additions & 2 deletions tests/functionals/test_api_client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from enum import Enum
from typing import Any, Optional
from typing import Any, Optional, Union

import pytest
from result import Result
Expand Down Expand Up @@ -65,7 +65,7 @@ class UpdateItem(GetItem):
collection_path="/items",
collection_contract={
"GET": (ListItem, Item),
"POST": (CreateItem | CreateItemIntSize, None),
"POST": (Union[CreateItem, CreateItemIntSize], None),
},
path="/items/{item_name}",
contract={
Expand Down
39 changes: 38 additions & 1 deletion tests/unittests/_async/test_service_route_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
AsyncRouteProxy,
build_request,
build_timeout,
is_instance_with_union,
is_union,
)
from blacksmith.typing import ClientName, Path
Expand Down Expand Up @@ -73,14 +74,50 @@ def test_build_timeout() -> None:
[
pytest.param({"type": int, "expected": False}, id="int"),
pytest.param({"type": str, "expected": False}, id="str"),
pytest.param({"type": int | str, "expected": True}, id="int | str"),
pytest.param({"type": Union[int, str], "expected": True}, id="Union[int, str]"),
],
)
def test_is_union(params: Mapping[str, Any]):
assert is_union(params["type"]) is params["expected"]


try:

@pytest.mark.parametrize(
"params",
[
pytest.param({"type": int | str, "expected": True}, id="int | str"),
],
)
def test_is_union_py310(params: Mapping[str, Any]):
assert is_union(params["type"]) is params["expected"]

except TypeError:
...


@pytest.mark.parametrize(
"params",
[
pytest.param({"type": str, "value": "bob", "expected": True}, id="str"),
pytest.param({"type": str, "value": 0.42, "expected": False}, id="str / float"),
pytest.param(
{"type": Union[int, str], "value": "bob", "expected": True}, id="int | str / str"
),
pytest.param(
{"type": Union[int, str], "value": 42, "expected": True}, id="int | str / int"
),
pytest.param(
{"type": Union[int, str], "value": 0.42, "expected": False},
id="int | str / float",
),
],
)
def test_is_instance_with_union(params: Mapping[str, Any]):
resp = is_instance_with_union(params["value"], params["type"])
assert resp == params["expected"]


class Foo(BaseModel):
typ: Literal["foo"]

Expand Down
26 changes: 25 additions & 1 deletion tests/unittests/_sync/test_service_route_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
SyncRouteProxy,
build_request,
build_timeout,
is_instance_with_union,
is_union,
)
from blacksmith.typing import ClientName, Path
Expand Down Expand Up @@ -73,7 +74,6 @@ def test_build_timeout() -> None:
[
pytest.param({"type": int, "expected": False}, id="int"),
pytest.param({"type": str, "expected": False}, id="str"),
pytest.param({"type": int | str, "expected": True}, id="int | str"),
pytest.param({"type": Union[int, str], "expected": True}, id="Union[int, str]"),
],
)
Expand All @@ -96,6 +96,30 @@ def test_is_union_py310(params: Mapping[str, Any]):
...


@pytest.mark.parametrize(
"params",
[
pytest.param({"type": str, "value": "bob", "expected": True}, id="str"),
pytest.param({"type": str, "value": 0.42, "expected": False}, id="str / float"),
pytest.param(
{"type": Union[int, str], "value": "bob", "expected": True},
id="int | str / str",
),
pytest.param(
{"type": Union[int, str], "value": 42, "expected": True},
id="int | str / int",
),
pytest.param(
{"type": Union[int, str], "value": 0.42, "expected": False},
id="int | str / float",
),
],
)
def test_is_instance_with_union(params: Mapping[str, Any]):
resp = is_instance_with_union(params["value"], params["type"])
assert resp == params["expected"]


class Foo(BaseModel):
typ: Literal["foo"]

Expand Down
7 changes: 4 additions & 3 deletions tests/unittests/test_registry.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Literal
from typing import Union

import pytest
from typing_extensions import Literal

import blacksmith
from blacksmith.domain import registry
Expand Down Expand Up @@ -131,7 +132,7 @@ class BarRequest(Request):
"v5",
path="/dummies/{name}",
contract={
"GET": (FooRequest | BarRequest, None),
"GET": (Union[FooRequest, BarRequest], None),
},
)

Expand All @@ -145,7 +146,7 @@ class BarRequest(Request):
assert api["dummies"].resource.contract is not None
assert api["dummies"].resource.path == "/dummies/{name}"
assert set(api["dummies"].resource.contract.keys()) == {"GET"}
assert api["dummies"].resource.contract["GET"][0] == FooRequest | BarRequest
assert api["dummies"].resource.contract["GET"][0] == Union[FooRequest, BarRequest]
assert api["dummies"].resource.contract["GET"][1] is None


Expand Down

0 comments on commit b038cf8

Please sign in to comment.