diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0135f3a..9b95e5e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,7 +30,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.8, 3.9, "3.10", "3.11", "3.12", "3.13"] + python-version: [ 3.9, "3.10", "3.11", "3.12", "3.13"] runs-on: windows-latest if: github.event_name == 'pull_request' || github.event_name == 'push' @@ -76,7 +76,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - python-version: [3.8, 3.9, "3.10", "3.11", "3.12", "3.13"] + python-version: [3.9, "3.10", "3.11", "3.12", "3.13"] runs-on: ${{ matrix.os }} steps: @@ -166,7 +166,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.8, 3.9, "3.10", "3.11", "3.12", "3.13"] + python-version: [3.9, "3.10", "3.11", "3.12", "3.13"] os: [ubuntu-latest, macos-latest, windows-latest] steps: diff --git a/blacksheep/asgi.pyi b/blacksheep/asgi.pyi index 38fcd2e..4fb8d93 100644 --- a/blacksheep/asgi.pyi +++ b/blacksheep/asgi.pyi @@ -1,10 +1,4 @@ -import sys -from typing import List, Tuple - -if sys.version_info >= (3, 8): - from typing import TypedDict -else: - from typing_extensions import TypedDict +from typing import List, Tuple, TypedDict class ASGIScopeInterface(TypedDict): type: str diff --git a/blacksheep/server/normalization.py b/blacksheep/server/normalization.py index 9c8dd75..8fad442 100644 --- a/blacksheep/server/normalization.py +++ b/blacksheep/server/normalization.py @@ -237,23 +237,6 @@ def __init__(self, parameter_name, route): Tuple[UUID], } -try: - # Note: try catch here is to support Python 3.8 - # it can be removed when support for Python 3.8 is dropped - _types_handled_with_query |= { - list[str], - list[int], - list[float], - list[bool], - list[UUID], - tuple[str], - tuple[int], - tuple[float], - tuple[bool], - } -except TypeError: - pass - def _check_union( parameter: ParamInfo, annotation: Any, method: Callable[..., Any] diff --git a/blacksheep/server/openapi/v3.py b/blacksheep/server/openapi/v3.py index 96636c2..50304f1 100644 --- a/blacksheep/server/openapi/v3.py +++ b/blacksheep/server/openapi/v3.py @@ -1,16 +1,12 @@ import collections.abc as collections_abc import inspect -import sys import warnings from abc import ABC, abstractmethod from dataclasses import dataclass, fields, is_dataclass from datetime import date, datetime from enum import Enum, IntEnum from typing import Any, Dict, Iterable, List, Optional, Tuple, Type, Union - -if sys.version_info >= (3, 9): - from typing import _AnnotatedAlias as AnnotatedAlias - +from typing import _AnnotatedAlias as AnnotatedAlias from typing import _GenericAlias as GenericAlias from typing import get_type_hints from uuid import UUID @@ -658,10 +654,9 @@ def _get_schema_by_type( if stored_ref: # pragma: no cover return stored_ref - if sys.version_info >= (3, 9): - if isinstance(object_type, AnnotatedAlias): - # Replace Annotated object type with the original type - object_type = getattr(object_type, "__origin__") + if isinstance(object_type, AnnotatedAlias): + # Replace Annotated object type with the original type + object_type = getattr(object_type, "__origin__") if self._can_handle_class_type(object_type): return self._get_schema_for_class(object_type) diff --git a/blacksheep/server/resources.py b/blacksheep/server/resources.py index b10f44a..2b4aa5a 100644 --- a/blacksheep/server/resources.py +++ b/blacksheep/server/resources.py @@ -4,18 +4,11 @@ package. """ -try: - from importlib.resources import files +from importlib.resources import files - def get_resource_file_path(anchor, file_name: str) -> str: - return str(files(anchor) / file_name) -except ImportError: - # Python 3.8 - import pkg_resources - - def get_resource_file_path(anchor, file_name: str) -> str: - return pkg_resources.resource_filename(anchor, file_name) +def get_resource_file_path(anchor, file_name: str) -> str: + return str(files(anchor) / file_name) def get_resource_file_content(file_name: str) -> str: diff --git a/blacksheep/utils/aio.py b/blacksheep/utils/aio.py index ef1d194..faae90d 100644 --- a/blacksheep/utils/aio.py +++ b/blacksheep/utils/aio.py @@ -1,6 +1,5 @@ import asyncio import json -import sys import urllib.error import urllib.parse import urllib.request @@ -75,13 +74,9 @@ async def post_form(self, url: str, data: Any) -> Any: return await self.loop.run_in_executor(None, lambda: post(url, data)) -def get_running_loop() -> AbstractEventLoop: # pragma: no cover +def get_running_loop() -> AbstractEventLoop: try: - if sys.version_info[:2] <= (3, 7): - # For Python 3.6 - return asyncio._get_running_loop() or asyncio.get_event_loop() - else: - return asyncio.get_running_loop() + return asyncio.get_running_loop() except RuntimeError: # TODO: fix deprecation warning happening in the test suite # DeprecationWarning: There is no current event loop diff --git a/pyproject.toml b/pyproject.toml index af16474..5c1894d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,6 @@ requires-python = ">=3.7" classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", diff --git a/requirements.txt b/requirements.txt index b0040fe..0e81001 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ Cython==3.0.11 essentials==1.1.5 essentials-openapi==1.0.9 Flask==3.0.3 -gevent==24.2.1 +gevent==24.11.1 greenlet==3.1.1 guardpost==1.0.2 h11==0.14.0 diff --git a/tests/test_application.py b/tests/test_application.py index e112363..6002eca 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -5,7 +5,7 @@ from dataclasses import dataclass from datetime import date, datetime from functools import wraps -from typing import Any, Dict, List, Optional, TypeVar +from typing import Annotated, Any, Dict, List, Optional, TypeVar from uuid import UUID, uuid4 import pytest @@ -1512,30 +1512,27 @@ async def home(item: FromJSON[Item]): assert app.response.status == 204 -if sys.version_info >= (3, 9): - from typing import Annotated - - @pytest.mark.asyncio - async def test_handler_from_json_annotated_parameter(app): - @app.router.post("/") - async def home(item: Annotated[Item, FromJSON]): - assert item is not None - value = item - assert value.a == "Hello" - assert value.b == "World" - assert value.c == 10 +@pytest.mark.asyncio +async def test_handler_from_json_annotated_parameter(app): + @app.router.post("/") + async def home(item: Annotated[Item, FromJSON]): + assert item is not None + value = item + assert value.a == "Hello" + assert value.b == "World" + assert value.c == 10 - app.normalize_handlers() - await app( - get_example_scope( - "POST", - "/", - [(b"content-type", b"application/json"), (b"content-length", b"32")], - ), - MockReceive([b'{"a":"Hello","b":"World","c":10}']), - MockSend(), - ) - assert app.response.status == 204 + app.normalize_handlers() + await app( + get_example_scope( + "POST", + "/", + [(b"content-type", b"application/json"), (b"content-length", b"32")], + ), + MockReceive([b'{"a":"Hello","b":"World","c":10}']), + MockSend(), + ) + assert app.response.status == 204 @pytest.mark.asyncio diff --git a/tests/test_openapi_v3.py b/tests/test_openapi_v3.py index 9d11252..05d565e 100644 --- a/tests/test_openapi_v3.py +++ b/tests/test_openapi_v3.py @@ -1,4 +1,3 @@ -import sys from dataclasses import dataclass from datetime import date, datetime from enum import IntEnum @@ -85,9 +84,7 @@ class PydExampleWithSpecificTypes(BaseModel): class PydCat(BaseModel): id: int name: str - - if sys.version_info >= (3, 9): - childs: list[UUID4] + childs: list[UUID4] class PydPaginatedSetOfCat(BaseModel): @@ -1056,11 +1053,10 @@ def home() -> PydPaginatedSetOfCat: ... yaml = serializer.to_yaml(docs.generate_documentation(app)) - if sys.version_info >= (3, 9): - if PYDANTIC_VERSION == 1: - assert ( - yaml.strip() - == """ + if PYDANTIC_VERSION == 1: + assert ( + yaml.strip() + == """ openapi: 3.0.3 info: title: Example @@ -1114,11 +1110,11 @@ def home() -> PydPaginatedSetOfCat: ... nullable: false tags: [] """.strip() - ) - else: - assert ( - yaml.strip() - == """ + ) + else: + assert ( + yaml.strip() + == """ openapi: 3.0.3 info: title: Example @@ -1173,58 +1169,6 @@ def home() -> PydPaginatedSetOfCat: ... format: int64 nullable: false tags: [] -""".strip() - ) - else: - assert ( - yaml.strip() - == """ -openapi: 3.0.3 -info: - title: Example - version: 0.0.1 -paths: - /: - get: - responses: - '200': - description: Success response - content: - application/json: - schema: - $ref: '#/components/schemas/PydPaginatedSetOfCat' - operationId: home -components: - schemas: - PydCat: - type: object - required: - - id - - name - properties: - id: - type: integer - format: int64 - nullable: false - name: - type: string - nullable: false - PydPaginatedSetOfCat: - type: object - required: - - items - - total - properties: - items: - type: array - nullable: false - items: - $ref: '#/components/schemas/PydCat' - total: - type: integer - format: int64 - nullable: false -tags: [] """.strip() ) @@ -1243,11 +1187,10 @@ def home() -> PydTypeWithChildModels: ... yaml = serializer.to_yaml(docs.generate_documentation(app)) - if sys.version_info >= (3, 9): - if PYDANTIC_VERSION == 1: - assert ( - yaml.strip() - == """ + if PYDANTIC_VERSION == 1: + assert ( + yaml.strip() + == """ openapi: 3.0.3 info: title: Example @@ -1322,11 +1265,11 @@ def home() -> PydTypeWithChildModels: ... $ref: '#/components/schemas/PydExampleWithSpecificTypes' tags: [] """.strip() - ) - else: - assert ( - yaml.strip() - == """ + ) + else: + assert ( + yaml.strip() + == """ openapi: 3.0.3 info: title: Example @@ -1401,79 +1344,6 @@ def home() -> PydTypeWithChildModels: ... $ref: '#/components/schemas/PydPaginatedSetOfCat' friend: $ref: '#/components/schemas/PydExampleWithSpecificTypes' -tags: [] - """.strip() - ) - else: - assert ( - yaml.strip() - == """ -openapi: 3.0.3 -info: - title: Example - version: 0.0.1 -paths: - /: - get: - responses: - '200': - description: Success response - content: - application/json: - schema: - $ref: '#/components/schemas/PydTypeWithChildModels' - operationId: home -components: - schemas: - PydCat: - type: object - required: - - id - - name - properties: - id: - type: integer - format: int64 - nullable: false - name: - type: string - nullable: false - PydPaginatedSetOfCat: - type: object - required: - - items - - total - properties: - items: - type: array - nullable: false - items: - $ref: '#/components/schemas/PydCat' - total: - type: integer - format: int64 - nullable: false - PydExampleWithSpecificTypes: - type: object - required: - - url - properties: - url: - type: string - format: uri - maxLength: 2083 - minLength: 1 - nullable: false - PydTypeWithChildModels: - type: object - required: - - child - - friend - properties: - child: - $ref: '#/components/schemas/PydPaginatedSetOfCat' - friend: - $ref: '#/components/schemas/PydExampleWithSpecificTypes' tags: [] """.strip() ) @@ -1493,11 +1363,10 @@ def home() -> PaginatedSet[PydCat]: ... yaml = serializer.to_yaml(docs.generate_documentation(app)) - if sys.version_info >= (3, 9): - if PYDANTIC_VERSION == 1: - assert ( - yaml.strip() - == """ + if PYDANTIC_VERSION == 1: + assert ( + yaml.strip() + == """ openapi: 3.0.3 info: title: Example @@ -1551,11 +1420,11 @@ def home() -> PaginatedSet[PydCat]: ... nullable: false tags: [] """.strip() - ) - else: - assert ( - yaml.strip() - == """ + ) + else: + assert ( + yaml.strip() + == """ openapi: 3.0.3 info: title: Example @@ -1609,58 +1478,6 @@ def home() -> PaginatedSet[PydCat]: ... type: integer format: int64 nullable: false -tags: [] - """.strip() - ) - else: - assert ( - yaml.strip() - == """ -openapi: 3.0.3 -info: - title: Example - version: 0.0.1 -paths: - /: - get: - responses: - '200': - description: Success response - content: - application/json: - schema: - $ref: '#/components/schemas/PaginatedSetOfPydCat' - operationId: home -components: - schemas: - PydCat: - type: object - required: - - id - - name - properties: - id: - type: integer - format: int64 - nullable: false - name: - type: string - nullable: false - PaginatedSetOfPydCat: - type: object - required: - - items - - total - properties: - items: - type: array - nullable: false - items: - $ref: '#/components/schemas/PydCat' - total: - type: integer - format: int64 - nullable: false tags: [] """.strip() ) @@ -2178,8 +1995,7 @@ def home() -> PydResponse[PydCat]: ... yaml = serializer.to_yaml(docs.generate_documentation(app)) if PYDANTIC_VERSION == 1: - if sys.version_info >= (3, 9): - expected_result = """ + expected_result = """ openapi: 3.0.3 info: title: Example @@ -2240,67 +2056,9 @@ def home() -> PydResponse[PydCat]: ... error: $ref: '#/components/schemas/Error' tags: [] -""".strip() - else: - expected_result = """ -openapi: 3.0.3 -info: - title: Example - version: 0.0.1 -paths: - /: - get: - responses: - '200': - description: Success response - content: - application/json: - schema: - $ref: '#/components/schemas/PydResponse[PydCat]' - operationId: home -components: - schemas: - PydCat: - type: object - required: - - id - - name - properties: - id: - type: integer - format: int64 - nullable: false - name: - type: string - nullable: false - Error: - type: object - required: - - code - - message - properties: - code: - type: integer - format: int64 - nullable: false - message: - type: string - nullable: false - PydResponse[PydCat]: - type: object - required: - - data - - error - properties: - data: - $ref: '#/components/schemas/PydCat' - error: - $ref: '#/components/schemas/Error' -tags: [] """.strip() elif PYDANTIC_VERSION == 2: - if sys.version_info >= (3, 9): - expected_result = """ + expected_result = """ openapi: 3.0.3 info: title: Example @@ -2367,67 +2125,6 @@ def home() -> PydResponse[PydCat]: ... - $ref: '#/components/schemas/Error' - type: 'null' tags: [] -""".strip() - else: - expected_result = """ -openapi: 3.0.3 -info: - title: Example - version: 0.0.1 -paths: - /: - get: - responses: - '200': - description: Success response - content: - application/json: - schema: - $ref: '#/components/schemas/PydResponse[PydCat]' - operationId: home -components: - schemas: - PydCat: - type: object - required: - - id - - name - properties: - id: - type: integer - format: int64 - nullable: false - name: - type: string - nullable: false - Error: - type: object - required: - - code - - message - properties: - code: - type: integer - format: int64 - nullable: false - message: - type: string - nullable: false - PydResponse[PydCat]: - type: object - required: - - data - - error - properties: - data: - anyOf: - - $ref: '#/components/schemas/PydCat' - - type: 'null' - error: - anyOf: - - $ref: '#/components/schemas/Error' - - type: 'null' -tags: [] """.strip() else: raise RuntimeError("Missing expected_result")