Skip to content
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(internal): msgspec signature model and pydantic v2 support. #1854

Closed
wants to merge 7 commits into from
Closed
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
7 changes: 5 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ jobs:
fail-fast: true
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11"]
pydantic-version: ["1", "2"]
uses: ./.github/workflows/test.yaml
with:
coverage: ${{ matrix.python-version == '3.11' && matrix.pydantic-version == '2' }}
integration: ${{ matrix.python-version == '3.11' && matrix.pydantic-version == '2' }}
pydantic-version: ${{ matrix.pydantic-version }}
python-version: ${{ matrix.python-version }}
coverage: ${{ matrix.python-version == '3.11' }}
integration: ${{ matrix.python-version == '3.11' }}

test-platform-compat:
if: github.event_name == 'push'
Expand All @@ -44,6 +46,7 @@ jobs:
uses: ./.github/workflows/test.yaml
with:
python-version: "3.11"
pydantic-version: "2"
os: ${{ matrix.os }}

sonar:
Expand Down
13 changes: 11 additions & 2 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
python-version:
required: true
type: string
pydantic-version:
required: true
type: string
coverage:
required: false
type: boolean
Expand Down Expand Up @@ -45,16 +48,22 @@ jobs:
uses: actions/cache@v3
with:
path: .venv
key: v1-venv-${{ runner.os }}-${{ inputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
key: v1-venv-${{ runner.os }}-${{ inputs.python-version }}-${{ inputs.pydantic-version }}-${{ hashFiles('**/poetry.lock') }}
- name: Load cached pip wheels
if: runner.os == 'Windows'
id: cached-pip-wheels
uses: actions/cache@v3
with:
path: ~/.cache
key: cache-${{ runner.os }}-${{ inputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
key: cache-${{ runner.os }}-${{ inputs.python-version }}-${{ inputs.pydantic-version }}-${{ hashFiles('**/poetry.lock') }}
- name: Install dependencies
run: poetry install --no-interaction
- if: ${{ inputs.pydantic-version == '1' }}
name: Install pydantic v1
run: poetry add "pydantic>=1.10.10,<2" && poetry remove pydantic-extra-types
- if: ${{ inputs.pydantic-version == '2' }}
name: Install pydantic v2
run: poetry add "pydantic>=2" && poetry add pydantic-extra-types
- name: Set pythonpath
run: echo "PYTHONPATH=$PWD" >> $GITHUB_ENV
- name: Test
Expand Down
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ repos:
beanie,
beautifulsoup4,
brotli,
cattrs,
click,
fakeredis>=2.10.2,
fast-query-parsers,
Expand All @@ -100,7 +99,8 @@ repos:
piccolo,
picologging,
psycopg,
pydantic,
pydantic>=2,
pydantic_extra_types,
pytest,
pytest-lazy-fixture,
pytest-mock,
Expand Down Expand Up @@ -141,7 +141,6 @@ repos:
beanie,
beautifulsoup4,
brotli,
cattrs,
click,
fakeredis>=2.10.2,
fast-query-parsers,
Expand All @@ -160,7 +159,8 @@ repos:
piccolo,
picologging,
psycopg,
pydantic,
pydantic>=2,
pydantic_extra_types,
pytest,
pytest-lazy-fixture,
pytest-mock,
Expand Down
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
(PY_CLASS, "litestar.response.RedirectResponse"),
(PY_CLASS, "anyio.abc.BlockingPortal"),
(PY_CLASS, "litestar.typing.ParsedType"),
(PY_CLASS, "pydantic.BaseModel"),
]

nitpick_ignore_regex = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@
class BaseModel(_BaseModel):
"""Extend Pydantic's BaseModel to enable ORM mode"""

class Config:
orm_mode = True
model_config = {"from_attributes": True}


# the SQLAlchemy base includes a declarative model for you to use in your models.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@
class BaseModel(_BaseModel):
"""Extend Pydantic's BaseModel to enable ORM mode"""

class Config:
orm_mode = True
model_config = {"from_attributes": True}


# we are going to add a simple "slug" to our model that is a URL safe surrogate key to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@
class BaseModel(_BaseModel):
"""Extend Pydantic's BaseModel to enable ORM mode"""

class Config:
orm_mode = True
model_config = {"from_attributes": True}


# the SQLAlchemy base includes a declarative model for you to use in your models.
Expand Down
11 changes: 3 additions & 8 deletions docs/examples/startup_and_shutdown.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import os
from typing import cast

from pydantic import BaseSettings
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine

from litestar import Litestar


class AppSettings(BaseSettings):
DATABASE_URI: str = "postgresql+asyncpg://postgres:mysecretpassword@pg.db:5432/db"


settings = AppSettings()
DB_URI = os.environ.get("DATABASE_URI", "postgresql+asyncpg://postgres:mysecretpassword@pg.db:5432/db")


def get_db_connection(app: Litestar) -> AsyncEngine:
Expand All @@ -19,7 +14,7 @@ def get_db_connection(app: Litestar) -> AsyncEngine:
If it doesn't exist, creates it and saves it in on the application state object
"""
if not getattr(app.state, "engine", None):
app.state.engine = create_async_engine(settings.DATABASE_URI)
app.state.engine = create_async_engine(DB_URI)
return cast("AsyncEngine", app.state.engine)


Expand Down
4 changes: 2 additions & 2 deletions litestar/_kwargs/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from inspect import isasyncgen, isgenerator
from typing import TYPE_CHECKING, Any

from litestar._signature.utils import get_signature_model
from litestar._signature import get_signature_model
from litestar.utils.compat import async_next

__all__ = ("Dependency", "create_dependency_batches", "map_dependencies_recursively", "resolve_dependency")
Expand Down Expand Up @@ -61,7 +61,7 @@ async def resolve_dependency(
signature_model = get_signature_model(dependency.provide)
dependency_kwargs = (
signature_model.parse_values_from_connection_kwargs(connection=connection, **kwargs)
if signature_model.fields
if signature_model._fields
else {}
)
value = await dependency.provide(**dependency_kwargs)
Expand Down
1 change: 1 addition & 0 deletions litestar/_kwargs/extractors.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from litestar.dto.interface import DTOInterface
from litestar.typing import FieldDefinition


__all__ = (
"body_extractor",
"cookies_extractor",
Expand Down
4 changes: 2 additions & 2 deletions litestar/_kwargs/kwargs_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ def create_for_signature_model(
An instance of KwargsModel
"""

field_definitions = signature_model.fields
field_definitions = signature_model._fields

cls._validate_raw_kwargs(
path_parameters=path_parameters,
Expand Down Expand Up @@ -405,7 +405,7 @@ def _create_dependency_graph(cls, key: str, dependencies: dict[str, Provide]) ->
list.
"""
provide = dependencies[key]
sub_dependency_keys = [k for k in get_signature_model(provide).fields if k in dependencies]
sub_dependency_keys = [k for k in get_signature_model(provide)._fields if k in dependencies]
return Dependency(
key=key,
provide=provide,
Expand Down
5 changes: 2 additions & 3 deletions litestar/_kwargs/parameter_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
from litestar.enums import ParamType
from litestar.params import ParameterKwarg

__all__ = ("ParameterDefinition", "create_parameter_definition", "merge_parameter_sets")


if TYPE_CHECKING:
from litestar.typing import FieldDefinition

__all__ = ("ParameterDefinition", "create_parameter_definition", "merge_parameter_sets")


class ParameterDefinition(NamedTuple):
"""Tuple defining a kwarg representing a request parameter."""
Expand Down
2 changes: 1 addition & 1 deletion litestar/_openapi/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def get_recursive_handler_parameters(
)
]

dependency_fields = dependency_providers[field_name].signature_model.fields
dependency_fields = dependency_providers[field_name].signature_model._fields
return create_parameter_for_handler(
route_handler=route_handler,
handler_fields=dependency_fields,
Expand Down
2 changes: 1 addition & 1 deletion litestar/_openapi/path_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def create_path_item(
route_handler, _ = handler_tuple

if route_handler.include_in_schema:
handler_fields = route_handler.signature_model.fields if route_handler.signature_model else {}
handler_fields = route_handler.signature_model._fields if route_handler.signature_model else {}
parameters = (
create_parameter_for_handler(
route_handler=route_handler,
Expand Down
Loading