Skip to content
Open
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
11 changes: 10 additions & 1 deletion fastapi_utils/cbv.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import inspect
import typing
from typing import (
Any,
Callable,
Expand All @@ -18,7 +19,15 @@

PYDANTIC_VERSION = pydantic.VERSION
if PYDANTIC_VERSION[0] == "2":
from typing_inspect import is_classvar
# Implemented with the stdlib `typing` module rather than the optional
# `typing-inspect` dependency, so that simply importing `fastapi_utils`
# doesn't require an extra to be installed. This covers both the bare
# `ClassVar` and the subscripted `ClassVar[...]` forms, matching the
# behavior of `typing_inspect.is_classvar` for the annotations `cbv`
# encounters here.
def is_classvar(hint: Any) -> bool:
return hint is typing.ClassVar or typing.get_origin(hint) is typing.ClassVar

else:
from pydantic.typing import is_classvar # type: ignore[no-redef]

Expand Down
22 changes: 22 additions & 0 deletions tests/test_cbv.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,36 @@
from __future__ import annotations

import importlib
import sys
from typing import Any, ClassVar, Optional

import pytest
from fastapi import APIRouter, Depends, Request
from starlette.testclient import TestClient

import fastapi_utils.cbv
from fastapi_utils.cbv import cbv


def test_cbv_importable_without_typing_inspect(monkeypatch: pytest.MonkeyPatch) -> None:
"""`typing-inspect` is an optional dependency (only pulled in by the `all` extra), so
importing `fastapi_utils.cbv` must not require it to be installed. See #318.

Setting `sys.modules["typing_inspect"] = None` makes the import machinery raise
`ImportError` for any `import typing_inspect` / `from typing_inspect import ...`
statement, simulating an environment where the package isn't installed.
"""
monkeypatch.setitem(sys.modules, "typing_inspect", None)

try:
reloaded = importlib.reload(fastapi_utils.cbv)
assert reloaded.cbv is not None
finally:
# Restore the real module state for any tests that run afterwards.
monkeypatch.undo()
importlib.reload(fastapi_utils.cbv)


class TestCBV:
@pytest.fixture(autouse=True)
def router(self) -> APIRouter:
Expand Down