Skip to content

Commit

Permalink
restore compatibility with python 3.11
Browse files Browse the repository at this point in the history
  • Loading branch information
livioribeiro committed Oct 28, 2023
1 parent c6ecaf8 commit 8369532
Show file tree
Hide file tree
Showing 22 changed files with 64 additions and 56 deletions.
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ jobs:
strategy:
matrix:
version:
- "3.11"
- "3.12"
steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Selva is a tool for creating ASGI applications that are easy to build and maintain.

It is built on top of [asgikit](https://pypi.org/project/asgikit/) and comes with
a dependency injection system built upon Python type annotations.
a dependency injection system built upon Python type annotations. It is compatible with python 3.11+.

## Quickstart

Expand Down
5 changes: 2 additions & 3 deletions examples/middleware/application/middleware.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import base64
from datetime import datetime
from http import HTTPStatus

from asgikit.requests import Request
from asgikit.responses import respond_status
from loguru import logger
Expand Down Expand Up @@ -34,9 +35,7 @@ async def __call__(self, chain, request: Request):
request_line = f"{request.method} {request.path} HTTP/{request.http_version}"
status = request.response.status

logger.info(
'{} "{}" {} {}', client, request_line, status.value, status.phrase
)
logger.info('{} "{}" {} {}', client, request_line, status.value, status.phrase)


class AuthMiddleware(Middleware):
Expand Down
2 changes: 1 addition & 1 deletion examples/settings/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ class Controller:

@get
async def index(self, request: Request):
await respond_text(request.response, self.settings.application.message)
await respond_text(request.response, self.settings.message)
8 changes: 1 addition & 7 deletions examples/settings/configuration/settings.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
message: Hello, World!
application:
message: ${MESSAGE:Hello, World!}
logging:
root: INFO
level:
application: WARNING
message: ${MESSAGE:Hello, World!}
3 changes: 1 addition & 2 deletions examples/settings/configuration/settings_dev.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
application:
message: ${MESSAGE:Hello, dev World!}
message: ${MESSAGE:Hello, dev World!}
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ classifiers = [
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: Implementation :: CPython",
"Topic :: Internet :: WWW/HTTP",
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
Expand All @@ -27,7 +27,7 @@ packages = [
]

[tool.poetry.dependencies]
python = "^3.12"
python = "^3.11"
asgikit = "^0.5"
pydantic = "^2.4"
loguru = "^0.7"
Expand Down
1 change: 0 additions & 1 deletion src/selva/_util/import_item.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from importlib import import_module


__all__ = ("import_item",)


Expand Down
6 changes: 4 additions & 2 deletions src/selva/_util/maybe_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
import functools
import inspect
from collections.abc import Awaitable, Callable
from typing import Any
from typing import Any, ParamSpec

P = ParamSpec("P")

async def maybe_async[**P](

async def maybe_async(
target: Awaitable | Callable[P, Any], *args: P.args, **kwargs: P.kwargs
) -> Any:
if inspect.isawaitable(target):
Expand Down
5 changes: 4 additions & 1 deletion src/selva/configuration/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
from loguru import logger

from selva.configuration.defaults import default_settings
from selva.configuration.environment import parse_settings_from_env, replace_variables_recursive
from selva.configuration.environment import (
parse_settings_from_env,
replace_variables_recursive,
)

__all__ = ("Settings", "SettingsError", "get_settings")

Expand Down
14 changes: 6 additions & 8 deletions src/selva/di/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import inspect
from collections.abc import AsyncGenerator, Awaitable, Generator, Iterable
from types import FunctionType, ModuleType
from typing import Any, Type
from typing import Any, Type, TypeVar

from loguru import logger

Expand All @@ -19,6 +19,8 @@
from selva.di.service.parse import get_dependencies, parse_service_spec
from selva.di.service.registry import ServiceRegistry

T = TypeVar("T")


class Container:
def __init__(self):
Expand All @@ -28,11 +30,7 @@ def __init__(self):
self.interceptors: list[Type[Interceptor]] = []

def register(
self,
service: InjectableType,
*,
provides: type = None,
name: str = None,
self, service: InjectableType, *, provides: type = None, name: str = None
):
self._register_service_spec(service, provides, name)

Expand Down Expand Up @@ -116,8 +114,8 @@ def iter_all_services(
for name, definition in record.providers.items():
yield interface, definition.service, name

async def get[T](self, service_type: T, *, name: str = None, optional=False,) -> T:
dependency = ServiceDependency(service_type, name=name, optional=optional)
async def get(self, service: T, *, name: str = None, optional=False) -> T:
dependency = ServiceDependency(service, name=name, optional=optional)
return await self._get(dependency)

async def create(self, service: type) -> Any:
Expand Down
5 changes: 4 additions & 1 deletion src/selva/di/decorator.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import inspect
from collections.abc import Callable
from typing import TypeVar

from selva.di.inject import Inject
from selva.di.service.model import InjectableType, ServiceInfo
Expand All @@ -8,12 +9,14 @@

DI_SERVICE_ATTRIBUTE = "__selva_di_service__"

T = TypeVar("T")


def _is_inject(value) -> bool:
return isinstance(value, Inject) or value is Inject


def service[T](
def service(
injectable: T = None, /, *, provides: type = None, name: str = None
) -> T | Callable[[T], T]:
"""Declare a class or function as a service
Expand Down
8 changes: 2 additions & 6 deletions src/selva/logging/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,13 @@ def setup_logger(settings: Settings):
log_config = settings.get("logging", {})
root_level = logger.level(log_config.get("root", "WARNING"))
log_level = {
name: logger.level(value)
for name, value in log_config.get("level", {}).items()
name: logger.level(value) for name, value in log_config.get("level", {}).items()
}

filter_func = filter_func_factory(root_level, log_level)
handler = {"sink": sys.stderr, "filter": filter_func}

logger.configure(
handlers=[handler],
activation=activation
)
logger.configure(handlers=[handler], activation=activation)

setup_loguru_std_logging_interceptor()

Expand Down
2 changes: 1 addition & 1 deletion src/selva/web/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
from loguru import logger

from selva._util.base_types import get_base_types
from selva._util.maybe_async import maybe_async
from selva._util.import_item import import_item
from selva._util.maybe_async import maybe_async
from selva.configuration.settings import Settings, get_settings
from selva.di.container import Container
from selva.di.decorator import DI_SERVICE_ATTRIBUTE
Expand Down
6 changes: 4 additions & 2 deletions src/selva/web/converter/from_request.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
from collections.abc import Awaitable
from typing import Any, Protocol, Type, runtime_checkable
from typing import Any, Protocol, Type, TypeVar, runtime_checkable

from asgikit.requests import Request

__all__ = ("FromRequest",)

T = TypeVar("T")


@runtime_checkable
class FromRequest[T](Protocol[T]):
class FromRequest(Protocol[T]):
"""Base class for services that extract values from the request"""

def from_request(
Expand Down
6 changes: 4 additions & 2 deletions src/selva/web/converter/param_converter.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from collections.abc import Awaitable
from typing import Protocol, runtime_checkable
from typing import Protocol, TypeVar, runtime_checkable

__all__ = ("ParamConverter",)

T = TypeVar("T")


@runtime_checkable
class ParamConverter[T](Protocol[T]):
class ParamConverter(Protocol[T]):
"""Convert values from and to request parameters
Request parameters come from path, querystring or headers.
Expand Down
6 changes: 4 additions & 2 deletions src/selva/web/converter/param_extractor.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from typing import Protocol, Type, runtime_checkable
from typing import Protocol, Type, TypeVar, runtime_checkable

from asgikit.requests import Request

T = TypeVar("T")


@runtime_checkable
class ParamExtractor[T](Protocol[T]):
class ParamExtractor(Protocol[T]):
def extract(self, request: Request, parameter_name: str, metadata: T | Type[T]):
raise NotImplementedError()
8 changes: 5 additions & 3 deletions src/selva/web/exception_handler.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
from typing import Protocol, Type, runtime_checkable
from typing import Protocol, Type, TypeVar, runtime_checkable

from asgikit.requests import Request

from selva.di.decorator import service

TExc = TypeVar("TExc", bound=BaseException)


@runtime_checkable
class ExceptionHandler[TExc: BaseException](Protocol[TExc]):
class ExceptionHandler(Protocol[TExc]):
async def handle_exception(self, request: Request, exc: TExc):
raise NotImplementedError()


def exception_handler(exc: Type[Exception]):
def exception_handler(exc: Type[BaseException]):
def inner(cls):
assert issubclass(cls, ExceptionHandler)
return service(cls, provides=ExceptionHandler[exc])
Expand Down
14 changes: 7 additions & 7 deletions tests/configuration/test_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

from selva.configuration.environment import (
parse_settings_from_env,
replace_variables_with_env,
replace_variables_recursive,
replace_variables_with_env,
)


Expand Down Expand Up @@ -79,14 +79,14 @@ def test_replace_variables_recursive():
"dict": {
"var": "3",
"subdict": {"var": "4"},
}
},
}


def test_replace_variables_recursive_with_invalid_value_should_fail():
settings = {
"prop": 1
}
settings = {"prop": 1}

with pytest.raises(TypeError, match="settings should contain only str, list or dict"):
replace_variables_recursive(settings, {})
with pytest.raises(
TypeError, match="settings should contain only str, list or dict"
):
replace_variables_recursive(settings, {})
2 changes: 1 addition & 1 deletion tests/configuration/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ def test_invalid_yaml_should_fail(monkeypatch):
"replace nested value",
"replace dict with value",
"replace value with dict",
]
],
)
def test_merge_recursive(settings, extra, expected):
merge_recursive(settings, extra)
Expand Down
6 changes: 5 additions & 1 deletion tests/di/services/scan_package/generic.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from typing import Generic, TypeVar

from selva.di import service

T = TypeVar("T")


class Interface[T]:
class Interface(Generic[T]):
pass


Expand Down
6 changes: 4 additions & 2 deletions tests/di/test_generics.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Annotated, TypeVar
from typing import Annotated, Generic, TypeVar

import pytest

Expand All @@ -8,8 +8,10 @@

from .fixtures import ioc

T = TypeVar("T")

class GenericService[T]:

class GenericService(Generic[T]):
pass


Expand Down

0 comments on commit 8369532

Please sign in to comment.