-
-
Notifications
You must be signed in to change notification settings - Fork 387
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add support for Python 3.13 #3850
base: main
Are you sure you want to change the base?
Changes from 33 commits
997a19c
882cabb
5da2293
7654cd2
f7a3f8b
bc23712
8d911ef
f0fb146
82e8166
4aa8e03
10df3f3
e2e0839
cda3f86
bfc24ec
6f66d3e
eed94c7
f3e0dbf
837f2ca
80f8e7d
343c29d
15c7cb8
2e53fac
0d76eed
9fd3141
d83b5b9
0c04845
490ce3f
1fe9e55
08d429d
0e110c6
7cacc10
0b57b71
4f6902a
1824379
0d3e6f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ classifiers = [ | |
"Programming Language :: Python :: 3.10", | ||
"Programming Language :: Python :: 3.11", | ||
"Programming Language :: Python :: 3.12", | ||
"Programming Language :: Python :: 3.13", | ||
"Programming Language :: Python", | ||
"Topic :: Internet :: WWW/HTTP", | ||
"Topic :: Software Development :: Libraries", | ||
|
@@ -31,22 +32,25 @@ classifiers = [ | |
"Topic :: Software Development :: Libraries :: Python Modules", | ||
] | ||
dependencies = [ | ||
"anyio>=3", | ||
"httpx>=0.22", | ||
"exceptiongroup; python_version < \"3.11\"", | ||
"importlib-metadata; python_version < \"3.10\"", | ||
"importlib-resources>=5.12.0; python_version < \"3.9\"", | ||
"msgspec>=0.18.2", | ||
"multidict>=6.0.2", | ||
"polyfactory>=2.6.3", | ||
"pyyaml", | ||
"typing-extensions", | ||
"click", | ||
"rich>=13.0.0", | ||
"rich-click", | ||
"multipart>=1.2.0", | ||
# default litestar plugins | ||
"litestar-htmx>=0.4.0" | ||
"anyio>=3 ; python_version < \"3.9\"", | ||
"anyio>=4 ; python_version >= \"3.9\"", | ||
"httpx>=0.22", | ||
"exceptiongroup; python_version < \"3.11\"", | ||
"eval_type_backport; python_version <= \"3.9\"", | ||
"importlib-metadata; python_version < \"3.10\"", | ||
"importlib-resources>=5.12.0; python_version < \"3.9\"", | ||
"litestar-msgspec; python_version > \"3.8\"", | ||
"msgspec>=0.18.2,<0.19.0; python_version < \"3.9\"", | ||
"multidict>=6.0.2", | ||
"polyfactory>=2.6.3", | ||
"pyyaml", | ||
"typing-extensions", | ||
"click", | ||
"rich>=13.0.0", | ||
"rich-click", | ||
"multipart>=1.2.0", | ||
# default litestar plugins | ||
"litestar-htmx>=0.3.0" | ||
] | ||
description = "Litestar - A production-ready, highly performant, extensible ASGI API Framework" | ||
keywords = ["api", "rest", "asgi", "litestar", "starlite"] | ||
|
@@ -83,7 +87,8 @@ brotli = ["brotli"] | |
cli = ["jsbeautifier", "uvicorn[standard]", "uvloop>=0.18.0; sys_platform != 'win32'"] | ||
cryptography = ["cryptography"] | ||
full = [ | ||
"litestar[annotated-types,attrs,brotli,cli,cryptography,jinja,jwt,mako,minijinja,opentelemetry,piccolo,picologging,prometheus,pydantic,redis,sqlalchemy,standard,structlog,valkey]", | ||
"litestar[annotated-types,attrs,brotli,cli,cryptography,jinja,jwt,mako,minijinja,opentelemetry,piccolo,picologging,prometheus,pydantic,redis,sqlalchemy,standard,structlog,valkey]; python_version < \"3.13\"", | ||
"litestar[annotated-types,attrs,brotli,cli,cryptography,jinja,jwt,mako,minijinja,opentelemetry,piccolo,prometheus,pydantic,redis,sqlalchemy,standard,structlog,valkey]; python_version >= \"3.13\"", | ||
] | ||
jinja = ["jinja2>=3.1.2"] | ||
jwt = [ | ||
|
@@ -94,9 +99,9 @@ mako = ["mako>=1.2.4"] | |
minijinja = ["minijinja>=1.0.0"] | ||
opentelemetry = ["opentelemetry-instrumentation-asgi"] | ||
piccolo = ["piccolo"] | ||
picologging = ["picologging"] | ||
picologging = ["picologging; python_version < \"3.13\""] | ||
prometheus = ["prometheus-client"] | ||
pydantic = ["pydantic", "email-validator", "pydantic-extra-types"] | ||
pydantic = ["pydantic", "email-validator", "pydantic-extra-types<=2.9.0; python_version < \"3.9\"", "pydantic-extra-types>=2.9.0; python_version >= \"3.9\""] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's an open build issue re: this. |
||
redis = ["redis[hiredis]>=4.4.4"] | ||
valkey = ["valkey[libvalkey]>=6.0.2"] | ||
sqlalchemy = ["advanced-alchemy>=0.2.2"] | ||
|
@@ -130,13 +135,14 @@ dev = [ | |
"trio", | ||
"aiosqlite", | ||
"asyncpg>=0.29.0", | ||
"psycopg[pool,binary]>=3.1.10,<3.2", | ||
"psycopg[pool,binary]>=3.1.10,<3.2; python_version < \"3.13\"", | ||
"psycopg[pool,c]; python_version >= \"3.13\"", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Package needs to be built for 3.13 |
||
"psycopg2-binary", | ||
"psutil>=5.9.8", | ||
"hypercorn>=0.16.0", | ||
"daphne>=4.0.0", | ||
"opentelemetry-sdk", | ||
"httpx-sse" | ||
"httpx-sse", | ||
] | ||
|
||
docs = [ | ||
|
@@ -281,6 +287,8 @@ module = [ | |
"pytimeparse.*", | ||
"importlib_resources", | ||
"exceptiongroup", | ||
"picologging", | ||
"picologging.*" | ||
] | ||
|
||
[[tool.mypy.overrides]] | ||
|
@@ -305,6 +313,13 @@ module = [ | |
] | ||
disable_error_code = "arg-type" | ||
|
||
[[tool.mypy.overrides]] | ||
warn_unused_ignores = false | ||
module = [ | ||
"tests.unit.test_logging.test_logging_config", | ||
] | ||
disable_error_code = "assignment" | ||
|
||
[tool.pydantic-mypy] | ||
init_forbid_extra = true | ||
init_typed = true | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,32 @@ | ||
from pathlib import Path | ||
|
||
import pytest | ||
from pytest import MonkeyPatch | ||
from sqlalchemy.ext.asyncio import create_async_engine | ||
from sqlalchemy.pool import NullPool | ||
|
||
from litestar.plugins.sqlalchemy import AsyncSessionConfig, SQLAlchemyAsyncConfig | ||
from litestar.testing import TestClient | ||
|
||
pytestmark = pytest.mark.xdist_group("sqlalchemy_examples") | ||
|
||
|
||
def test_sqlalchemy_declarative_models() -> None: | ||
from docs.examples.contrib.sqlalchemy.sqlalchemy_declarative_models import app | ||
async def test_sqlalchemy_declarative_models(tmp_path: Path, monkeypatch: MonkeyPatch) -> None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Without this additional monkey-patching, there were intermittent failures on this job. This is the only way I could make the tests consistently pass on all python versions. |
||
engine = create_async_engine("sqlite+aiosqlite:///test.sqlite", poolclass=NullPool) | ||
|
||
session_config = AsyncSessionConfig(expire_on_commit=False) | ||
sqlalchemy_config = SQLAlchemyAsyncConfig( | ||
session_config=session_config, | ||
create_all=True, | ||
engine_instance=engine, | ||
) # Create 'async_session' dependency. | ||
from docs.examples.contrib.sqlalchemy import sqlalchemy_declarative_models | ||
|
||
with TestClient(app) as client: | ||
monkeypatch.setattr(sqlalchemy_declarative_models, "sqlalchemy_config", sqlalchemy_config) | ||
async with engine.begin() as connection: | ||
await connection.run_sync(sqlalchemy_declarative_models.Author.metadata.create_all) | ||
await connection.commit() | ||
with TestClient(sqlalchemy_declarative_models.app) as client: | ||
response = client.get("/authors") | ||
assert response.status_code == 200 | ||
assert len(response.json()) > 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,16 @@ | ||
from __future__ import annotations | ||
|
||
import atexit | ||
import importlib.util | ||
import inspect | ||
import logging | ||
import random | ||
import sys | ||
from contextlib import AbstractContextManager, contextmanager | ||
from pathlib import Path | ||
from typing import Any, AsyncContextManager, Awaitable, ContextManager, Generator, TypeVar, cast, overload | ||
|
||
import picologging | ||
import pytest | ||
from _pytest.logging import LogCaptureHandler, _LiveLoggingNullHandler | ||
|
||
from litestar._openapi.schema_generation import SchemaCreator | ||
|
@@ -90,9 +92,9 @@ def cleanup_logging_impl() -> Generator: | |
# Don't interfere with PyTest handler config | ||
if not isinstance(std_handler, (_LiveLoggingNullHandler, LogCaptureHandler)): | ||
std_root_logger.removeHandler(std_handler) | ||
|
||
picologging = pytest.importorskip("picologging") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TIL about this. It will auto-skip if this import fails |
||
# Reset root logger (`picologging` module) | ||
pico_root_logger: picologging.Logger = picologging.getLogger() | ||
pico_root_logger: picologging.Logger = picologging.getLogger() # type: ignore[name-defined,unused-ignore] # pyright: ignore[reportPrivateUsage,reportGeneralTypeIssues,reportAssignmentType,reportInvalidTypeForm] | ||
for pico_handler in pico_root_logger.handlers: | ||
pico_root_logger.removeHandler(pico_handler) | ||
|
||
|
@@ -111,3 +113,10 @@ def cleanup_logging_impl() -> Generator: | |
def not_none(val: T | None) -> T: | ||
assert val is not None | ||
return val | ||
|
||
|
||
def purge_module(module_names: list[str], path: str | Path) -> None: | ||
for name in module_names: | ||
if name in sys.modules: | ||
del sys.modules[name] | ||
Path(importlib.util.cache_from_source(path)).unlink(missing_ok=True) # type: ignore[arg-type] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious: Why do we need this?