Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/lint-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python_version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
python_version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Set up Python
Expand All @@ -38,7 +38,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python_version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
python_version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
os: ["ubuntu-latest", "windows-latest"]
steps:
- uses: actions/checkout@v4
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- [#30](https://github.com/WSH032/fastapi-proxy-lib/pull/30) - fix(internal): use `websocket` in favor of `websocket_route`. Thanks [@WSH032](https://github.com/WSH032)!

### Removed

- [#49](https://github.com/WSH032/fastapi-proxy-lib/pull/49) - Drop support for `Python 3.8`.

### Fixed

- [#49](https://github.com/WSH032/fastapi-proxy-lib/pull/49) - fix!: bump `httpx-ws >= 0.7.1` to fix frankie567/httpx-ws#29. Thanks [@WSH032](https://github.com/WSH032)!

### Internal

- [#47](https://github.com/WSH032/fastapi-proxy-lib/pull/47) - test: do not use deprecated and removed APIs of httpx. Thanks [@WSH032](https://github.com/WSH032)!
Expand Down
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build-backend = "hatchling.build"
# https://hatch.pypa.io/latest/config/metadata/
[project]
name = "fastapi-proxy-lib"
requires-python = ">=3.8"
requires-python = ">=3.9"
readme = "README.md"
license = { file = "LICENSE" }
authors = [
Expand All @@ -29,11 +29,11 @@ classifiers = [
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Internet :: WWW/HTTP :: HTTP Servers",
"Topic :: Internet :: WWW/HTTP :: Session",
Expand All @@ -50,7 +50,7 @@ dynamic = ["version"]

dependencies = [
"httpx",
"httpx-ws >= 0.4.2",
"httpx-ws >= 0.7.1",
"starlette",
"typing_extensions >=4.5.0",
]
Expand Down Expand Up @@ -202,7 +202,7 @@ convention = "google"
# https://microsoft.github.io/pyright/#/configuration
[tool.pyright]
typeCheckingMode = "strict"
pythonVersion = "3.8"
pythonVersion = "3.9"
reportUnusedImport = "warning"
reportUnusedFunction = "warning"
reportUnusedExpression = "warning"
Expand Down
14 changes: 6 additions & 8 deletions scripts/pre_commit_scripts/ver_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
from pathlib import Path
from typing import (
Any,
Dict,
List,
Union,
)

Expand All @@ -30,24 +28,24 @@
pre_commit_config_yaml_path = Path(".pre-commit-config.yaml")
pyproject_toml_path = Path("pyproject.toml")

RepoType = Dict[str, Any]
HookType = Dict[str, Any]
RepoType = dict[str, Any]
HookType = dict[str, Any]

if __name__ == "__main__":
# NOTE: 这三个键名应该对应
# pyproject_toml["tool"]["hatch"]["envs"]["default"]["dependencies"] 里的值
vers_in_pre_commit: Dict[str, Union[None, str]] = {
vers_in_pre_commit: dict[str, Union[None, str]] = {
"ruff": None,
"black": None,
"codespell": None,
}

# 找出pre-commit-config.yaml中的版本
pre_commit_yaml = yaml.load(pre_commit_config_yaml_path)
repos_lst: List[RepoType] = pre_commit_yaml["repos"]
repos_lst: list[RepoType] = pre_commit_yaml["repos"]

for repo in repos_lst:
hooks_lst: List[HookType] = repo["hooks"]
hooks_lst: list[HookType] = repo["hooks"]
hook = hooks_lst[0] # 特殊标记的只有一个hook
hook_alias = hook.get("alias") # 只有特殊标记的才有alias
if hook_alias is None:
Expand All @@ -56,7 +54,7 @@
vers_in_pre_commit[hook_alias] = repo["rev"]

# 检查是否正确
new_vers: Dict[str, Version] = {}
new_vers: dict[str, Version] = {}
for name, ver in vers_in_pre_commit.items():
if not isinstance(ver, str):
sys.exit(f"Error: version of `{name}` not found in pre-commit-config.yaml")
Expand Down
3 changes: 1 addition & 2 deletions src/fastapi_proxy_lib/core/_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
import ipaddress
import logging
import warnings
from collections.abc import Iterable, Mapping
from functools import lru_cache
from textwrap import dedent
from typing import (
Any,
Iterable,
Mapping,
Optional,
Protocol,
TypedDict,
Expand Down
3 changes: 1 addition & 2 deletions src/fastapi_proxy_lib/core/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from textwrap import dedent
from typing import (
Any,
List,
NamedTuple,
NoReturn,
Optional,
Expand Down Expand Up @@ -185,7 +184,7 @@ def _change_server_header(
Returns:
The **oringinal headers**, but **had been changed**.
"""
server_connection_header: List[str] = [
server_connection_header: list[str] = [
v.strip() for v in headers.get("connection", "").lower().split(",")
]

Expand Down
7 changes: 3 additions & 4 deletions src/fastapi_proxy_lib/core/websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from typing import (
TYPE_CHECKING,
Any,
List,
Literal,
NamedTuple,
NoReturn,
Expand Down Expand Up @@ -80,7 +79,7 @@ class _ClientServerProxyTask(NamedTuple):
_change_client_header = change_necessary_client_header_for_httpx


def _get_client_request_subprotocols(ws_scope: Scope) -> Union[List[str], None]:
def _get_client_request_subprotocols(ws_scope: Scope) -> Union[list[str], None]:
"""Get client request subprotocols.

Args:
Expand All @@ -91,7 +90,7 @@ def _get_client_request_subprotocols(ws_scope: Scope) -> Union[List[str], None]:
Else return `None`.
"""
# https://asgi.readthedocs.io/en/latest/specs/www.html#websocket-connection-scope
subprotocols: List[str] = ws_scope.get("subprotocols", [])
subprotocols: list[str] = ws_scope.get("subprotocols", [])
if not subprotocols: # 即为 []
return None
return subprotocols
Expand Down Expand Up @@ -484,7 +483,7 @@ async def send_request_to_target( # pyright: ignore [reportIncompatibleMethodOv
keepalive_ping_interval_seconds = self.keepalive_ping_interval_seconds
keepalive_ping_timeout_seconds = self.keepalive_ping_timeout_seconds

client_request_subprotocols: Union[List[str], None] = (
client_request_subprotocols: Union[list[str], None] = (
_get_client_request_subprotocols(websocket.scope)
)

Expand Down
14 changes: 6 additions & 8 deletions src/fastapi_proxy_lib/fastapi/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@

import asyncio
import warnings
from contextlib import asynccontextmanager
from collections.abc import AsyncIterator
from contextlib import AbstractAsyncContextManager, asynccontextmanager
from typing import (
Any,
AsyncContextManager,
AsyncIterator,
Callable,
Literal,
Optional,
Set,
TypeVar,
Union,
)
Expand Down Expand Up @@ -170,13 +168,13 @@ class RouterHelper:

def __init__(self) -> None:
"""Initialize RouterHelper."""
self._registered_proxy: Set[Union[_HttpProxyTypes, _WebSocketProxyTypes]] = (
self._registered_proxy: set[Union[_HttpProxyTypes, _WebSocketProxyTypes]] = (
set()
)
self._registered_router_id: Set[int] = set()
self._registered_router_id: set[int] = set()

@property
def registered_proxy(self) -> Set[Union[_HttpProxyTypes, _WebSocketProxyTypes]]:
def registered_proxy(self) -> set[Union[_HttpProxyTypes, _WebSocketProxyTypes]]:
"""The proxy that has been registered."""
return self._registered_proxy

Expand Down Expand Up @@ -253,7 +251,7 @@ def register_router(
self._registered_proxy.add(proxy)
return router

def get_lifespan(self) -> Callable[..., AsyncContextManager[None]]:
def get_lifespan(self) -> Callable[..., AbstractAsyncContextManager[None]]:
"""The lifespan event for closing registered proxy.

Returns:
Expand Down
3 changes: 2 additions & 1 deletion tests/app/echo_http_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
# pyright: reportUnusedFunction=false

import io
from typing import Literal, Mapping, Union
from collections.abc import Mapping
from typing import Literal, Union

from fastapi import FastAPI, Request, Response
from fastapi.responses import StreamingResponse
Expand Down
6 changes: 3 additions & 3 deletions tests/app/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import asyncio
import socket
from dataclasses import dataclass
from typing import Any, Callable, List, Optional, Type, TypedDict, TypeVar, Union
from typing import Any, Callable, Optional, TypedDict, TypeVar, Union

import httpx
import uvicorn
Expand All @@ -12,7 +12,7 @@
from starlette.websockets import WebSocket
from typing_extensions import Self, override

_Decoratable_T = TypeVar("_Decoratable_T", bound=Union[Callable[..., Any], Type[Any]])
_Decoratable_T = TypeVar("_Decoratable_T", bound=Union[Callable[..., Any], type[Any]])

ServerRecvRequestsTypes = Union[Request, WebSocket]

Expand Down Expand Up @@ -100,7 +100,7 @@ def __init__(
self.contx_exit_timeout = contx_exit_timeout

@override
async def startup(self, sockets: Optional[List[socket.socket]] = None) -> None:
async def startup(self, sockets: Optional[list[socket.socket]] = None) -> None:
"""The same as `uvicorn.Server.startup`."""
super_return = await super().startup(sockets=sockets)
self.contx_server_started_event.set()
Expand Down
3 changes: 1 addition & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@
# https://anyio.readthedocs.io/en/stable/testing.html

import typing
from collections.abc import AsyncIterator, Coroutine
from contextlib import AsyncExitStack
from dataclasses import dataclass
from typing import (
AsyncIterator,
Callable,
Coroutine,
Literal,
Protocol,
Union,
Expand Down
6 changes: 3 additions & 3 deletions tests/test_docs_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

def test_forward_http_proxy() -> None:
"""测试 ForwardHttpProxy 中的例子."""
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from typing import AsyncIterator

from fastapi import FastAPI
from fastapi_proxy_lib.core.http import ForwardHttpProxy
Expand Down Expand Up @@ -33,8 +33,8 @@ async def _(request: Request, path: str = ""):

def test_reverse_http_proxy() -> None:
"""测试 ReverseHttpProxy 中的例子."""
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from typing import AsyncIterator

from fastapi import FastAPI
from fastapi_proxy_lib.core.http import ReverseHttpProxy
Expand Down Expand Up @@ -71,8 +71,8 @@ async def _(request: Request, path: str = ""):

def test_reverse_ws_proxy() -> None:
"""测试 ReverseWebSocketProxy 中的例子."""
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from typing import AsyncIterator

from fastapi import FastAPI
from fastapi_proxy_lib.core.websocket import ReverseWebSocketProxy
Expand Down
8 changes: 4 additions & 4 deletions tests/test_ws.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import asyncio
from contextlib import AsyncExitStack
from multiprocessing import Process, Queue
from typing import Any, Dict, Literal, Optional
from typing import Any, Literal, Optional

import httpx
import httpx_ws
Expand Down Expand Up @@ -34,7 +34,7 @@
WS_BACKENDS_NEED_BE_TESTED = ("websockets",)

# https://www.python-httpx.org/advanced/transports/#no-proxy-support
NO_PROXIES: Dict[Any, Any] = {"all://": None}
NO_PROXIES: dict[Any, Any] = {"all://": None}


def _subprocess_run_echo_ws_uvicorn_server(queue: "Queue[str]", **kwargs: Any):
Expand Down Expand Up @@ -68,8 +68,8 @@ async def run():

def _subprocess_run_httpx_ws(
queue: "Queue[str]",
kwargs_async_client: Optional[Dict[str, Any]] = None,
kwargs_aconnect_ws: Optional[Dict[str, Any]] = None,
kwargs_async_client: Optional[dict[str, Any]] = None,
kwargs_aconnect_ws: Optional[dict[str, Any]] = None,
):
"""Run aconnect_ws in subprocess.

Expand Down
Loading