Fix typing and minor issues

This commit is contained in:
Andras Schmelczer 2022-07-07 14:12:44 +02:00
parent 2db2253578
commit 72ab627a34
54 changed files with 635 additions and 589 deletions

View file

@ -5,4 +5,3 @@ from .hashable_base_model import HashableBaseModel
from .snake_case_to_text import snake_case_to_text
from .strip_lines import strip_lines
from .text_to_hex_color import text_to_hex_color
from .use_http_exceptions import use_http_exceptions

View file

@ -1,10 +1,10 @@
from typing import Any, Callable
from typing import Callable
from ..exceptions import WrongDecoratorOrderError
from .get_function_metadata_store import get_function_metadata_store
def assert_function_is_not_finalised(func: Callable[..., Any]) -> None:
def assert_function_is_not_finalised(func: Callable) -> None:
error_message = (
"The outer-most (first) decorator has to be `@GreatAI.deploy`. "
+ f"In the case of `{func.__name__}`, it is not: fix this by moving `@GreatAI.deploy` to the top."

View file

@ -1,41 +1,50 @@
import inspect
from functools import wraps
from typing import Any, Callable, Dict, List, Set, Union
from typing import Any, Callable, Mapping, Sequence, Set, TypeVar, Union, cast
from pydantic import BaseModel
F = TypeVar("F", bound=Callable)
class FrozenDict(dict):
def __hash__(self) -> int:
def __hash__(self) -> int: # type: ignore
return hash(frozenset((k, freeze(v)) for k, v in self.items()))
class FrozenList(list):
def __hash__(self) -> int:
def __hash__(self) -> int: # type: ignore
return hash(tuple(freeze(i) for i in self))
class FrozenSet(set):
def __hash__(self) -> int:
def __hash__(self) -> int: # type: ignore
return hash(frozenset(freeze(i) for i in self))
def freeze_arguments(func: Callable[..., Any]) -> Callable[..., Any]:
"""Transform mutable dictionary
Into immutable
Useful to be compatible with cache
source: https://stackoverflow.com/questions/6358481/using-functools-lru-cache-with-dictionary-arguments
"""
def freeze_arguments(func: F) -> F:
@wraps(func)
def wrapper(*args: List[Any], **kwargs: Dict[str, Any]) -> Any:
def wrapper(*args: Any, **kwargs: Any) -> Any:
args = tuple(freeze(arg) for arg in args)
kwargs = {k: freeze(v) for k, v in kwargs.items()}
return func(*args, **kwargs)
return wrapper
@wraps(func)
async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
return await wrapper(*args, **kwargs)
return cast(F, async_wrapper if inspect.iscoroutinefunction(func) else wrapper)
def freeze(value: Union[List[Any], Dict[str, Any], Set[Any]]) -> Any:
def freeze(value: Union[Sequence[Any], Mapping[str, Any], Set[Any], BaseModel]) -> Any:
"""
>>> class MyClass(BaseModel):
... a: int
>>> my_object = MyClass(a=3)
>>> my_other_object = MyClass(a=4)
>>> freeze(my_object) == freeze(my_other_object), freeze(my_object) == freeze(my_object)
(False, True)
"""
if isinstance(value, dict):
return FrozenDict(value)
@ -47,7 +56,7 @@ def freeze(value: Union[List[Any], Dict[str, Any], Set[Any]]) -> Any:
if isinstance(value, BaseModel):
class HashableValue(type(value)):
class HashableValue(type(value)): # type: ignore
def __hash__(self) -> int:
return hash(frozenset((k, freeze(v)) for k, v in self.dict().items()))

View file

@ -3,7 +3,7 @@ from typing import Any, Callable, Dict, Mapping, Sequence
def get_arguments(
func: Callable[..., Any], args: Sequence[Any], kwargs: Mapping[str, Any]
func: Callable, args: Sequence[Any], kwargs: Mapping[str, Any]
) -> Dict[str, Any]:
"""Return mapping from parameter names to actual argument values"""

View file

@ -1,12 +1,14 @@
import inspect
from typing import Any, Callable, cast
from ..views.function_metadata import FunctionMetadata
def get_function_metadata_store(func: Callable[..., Any]) -> FunctionMetadata:
def get_function_metadata_store(func: Callable) -> FunctionMetadata:
any_func = cast(Any, func)
if not hasattr(any_func, "_great_ai_metadata"):
any_func._great_ai_metadata = FunctionMetadata()
is_asynchronous = inspect.iscoroutinefunction(func)
any_func._great_ai_metadata = FunctionMetadata(is_asynchronous=is_asynchronous)
return any_func._great_ai_metadata

View file

@ -4,10 +4,14 @@ from hashlib import md5
def text_to_hex_color(text: str) -> str:
ascii_bytes = text.encode("ascii")
digest = md5(
ascii_bytes
).hexdigest() # the built-in hash function is salted differently in each process
integer = int(digest, 16)
hue = integer % 6311 / 6311.0
rgb = colorsys.hsv_to_rgb(hue, 0.75, 0.6)
rgb = colorsys.hsv_to_rgb(hue, 0.8, 0.6)
return "#" + "".join("%02X" % round(i * 255) for i in rgb)

View file

@ -1,20 +0,0 @@
from functools import wraps
from typing import Any, Callable, Dict, List, TypeVar, cast
from fastapi import HTTPException, status
F = TypeVar("F", bound=Callable[..., Any])
def use_http_exceptions(func: F) -> F:
@wraps(func)
def wrapper(*args: List[Any], **kwargs: Dict[str, Any]) -> Any:
try:
return func(*args, **kwargs)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"The following exception has occurred: {type(e).__name__}: {e}",
)
return cast(F, wrapper)