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

[BUG] Required dependency typing_inspect? #318

Open
kevinhikaruevans opened this issue Jun 21, 2024 · 4 comments
Open

[BUG] Required dependency typing_inspect? #318

kevinhikaruevans opened this issue Jun 21, 2024 · 4 comments
Labels
bug Something isn't working

Comments

@kevinhikaruevans
Copy link

Describe the bug
If I'm using Pydantic 2, cbv.py imports package typing_inspect. However this is listed as an optional dependency.

To Reproduce
Steps to reproduce the behavior:

  1. Install latest Fast API, fastapi-utils
  2. Add a Resource
  3. Run service using fastapi dev ...
  4. See error

Expected behavior
It doesn't crash

Screenshots

│ /home/kevin/.../venv/lib/python3.11/site-packages/fastapi │
│ _utils/cbv_base.py:5 in <module>                                                                 │
│                                                                                                  │
│    2                                                                                             │
│    3 from fastapi import APIRouter, FastAPI                                                      │
│    4                                                                                             │
│ ❱  5 from .cbv import INCLUDE_INIT_PARAMS_KEY, RETURN_TYPES_FUNC_KEY, _cbv                       │
│    6                                                                                             │
│    7                                                                                             │
│    8 class Resource:                                                                             │
│                                                                                                  │
│ ╭────────────────────── locals ──────────────────────╮                                           │
│ │       Any = typing.Any                             │                                           │
│ │ APIRouter = <class 'fastapi.routing.APIRouter'>    │                                           │
│ │      Dict = typing.Dict                            │                                           │
│ │   FastAPI = <class 'fastapi.applications.FastAPI'> │                                           │
│ │  Optional = typing.Optional                        │                                           │
│ │     Tuple = typing.Tuple                           │                                           │
│ ╰────────────────────────────────────────────────────╯                                           │
│                                                                                                  │
│ /home/kevin/.../venv/lib/python3.11/site-packages/fastapi │
│ _utils/cbv.py:21 in <module>                                                                     │
│                                                                                                  │
│    18                                                                                            │
│    19 PYDANTIC_VERSION = pydantic.VERSION                                                        │
│    20 if PYDANTIC_VERSION[0] == "2":                                                             │
│ ❱  21 │   from typing_inspect import is_classvar                                                 │
│    22 else:                                                                                      │
│    23 │   from pydantic.typing import is_classvar  # type: ignore[no-redef]                      │
│    24                                                                                            │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │              Any = typing.Any                                                                │ │
│ │         APIRoute = <class 'fastapi.routing.APIRoute'>                                        │ │
│ │        APIRouter = <class 'fastapi.routing.APIRouter'>                                       │ │
│ │         Callable = typing.Callable                                                           │ │
│ │             cast = <function cast at 0x74cc440459e0>                                         │ │
│ │          Depends = <function Depends at 0x74cc4224dda0>                                      │ │
│ │   get_type_hints = <function get_type_hints at 0x74cc44045b20>                               │ │
│ │          inspect = <module 'inspect' from '/usr/lib/python3.11/inspect.py'>                  │ │
│ │             List = typing.List                                                               │ │
│ │         pydantic = <module 'pydantic' from                                                   │ │
│ │                    '/home/kevin/.../venv/lib/python3… │ │
│ │ PYDANTIC_VERSION = '2.7.4'                                                                   │ │
│ │            Route = <class 'starlette.routing.Route'>                                         │ │
│ │            Tuple = typing.Tuple                                                              │ │
│ │             Type = typing.Type                                                               │ │
│ │          TypeVar = <class 'typing.TypeVar'>                                                  │ │
│ │            Union = typing.Union                                                              │ │
│ │   WebSocketRoute = <class 'starlette.routing.WebSocketRoute'>                                │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
ModuleNotFoundError: No module named 'typing_inspect'

Environment:

  • OS: Linux
  • FastAPI Utils, FastAPI, and Pydantic versions [e.g. 0.3.0], get them with:
import fastapi_utils
import fastapi
import pydantic.utils
print(fastapi_utils.__version__)
print(fastapi.__version__)
print(pydantic.utils.version_info())

^ This also fails to run w/o typing_inspect.

After installing it:

0.7.0
0.111.0
/home/kevin/.../venv/lib/python3.11/site-packages/pydantic/_migration.py:283: UserWarning: `pydantic.utils:version_info` has been moved to `pydantic.version:version_info`.
  warnings.warn(f'`{import_path}` has been moved to `{new_location}`.')
             pydantic version: 2.7.4
        pydantic-core version: 2.18.4
          pydantic-core build: profile=release pgo=true
                 install path: /home/kevin/.../venv/lib/python3.11/site-packages/pydantic
               python version: 3.11.6 (main, Oct  8 2023, 05:06:43) [GCC 13.2.0]
                     platform: Linux-6.5.0-41-generic-x86_64-with-glibc2.38
             related packages: typing_extensions-4.12.2 fastapi-0.111.0
                       commit: unknown
  • Python version, get it with:

3.11.6

Additional context
Add any other context about the problem here.

@frost-nzcr4
Copy link

frost-nzcr4 commented Aug 8, 2024

From the source of the function https://github.com/ilevkivskyi/typing_inspect/blob/master/typing_inspect.py#L261-L275:

NEW_TYPING = sys.version_info[:3] >= (3, 7, 0)  # it's always True, since fastapi-utils requires >=3.7

def is_classvar(tp):
    """Test if the type represents a class variable. Examples::

        is_classvar(int) == False
        is_classvar(ClassVar) == True
        is_classvar(ClassVar[int]) == True
        is_classvar(ClassVar[List[T]]) == True
    """
    if NEW_TYPING:
        return (tp is ClassVar or
                isinstance(tp, typingGenericAlias) and tp.__origin__ is ClassVar)
    elif WITH_CLASSVAR:
        return type(tp) is _ClassVar
    else:
        return False

we get that to solve this problem we need only a few lines instead of the undocumented requirement of typing_inspect:

from typing import _GenericAlias
if sys.version_info[:3] >= (3, 9, 0):
    from typing import _SpecialGenericAlias
    typingGenericAlias = (_GenericAlias, _SpecialGenericAlias, types.GenericAlias)
else:
    typingGenericAlias = (_GenericAlias,)

def is_classvar(tp):
    """Test if the type represents a class variable. Examples::

        is_classvar(int) == False
        is_classvar(ClassVar) == True
        is_classvar(ClassVar[int]) == True
        is_classvar(ClassVar[List[T]]) == True
    """
    return tp is ClassVar or isinstance(tp, typingGenericAlias) and tp.__origin__ is ClassVar

And after October 2024, when Python 3.8 reaches its end of life https://peps.python.org/pep-0569/, the number of lines will become even smaller.

@frost-nzcr4
Copy link

frost-nzcr4 commented Aug 8, 2024

There is another option using typing_extensions.get_origin. This function use the native typing.get_origin on Python >= 3.10 and has a workaround for older versions.

typing_extensions from version 4.6.1 (up to 4.12.2 at least) has this function and this module is required by Pydantic >= 2.0 (up to 2.8.2 at least) so it's always installed with the Pydantic and available to use like this:

def is_classvar(tp):
    return typing_extensions.get_origin(tp) is ClassVar

@laskmat
Copy link

laskmat commented Nov 13, 2024

hey, will this be fixed? The Issue still exists

@yuval9313
Copy link
Collaborator

yuval9313 commented Nov 15, 2024

Well, it seems like a misunderstanding, To use pydantic v2 it is required to install fastapi-utils[all].
However, I will not close this issue because I want to find a solution that makes sense.

I do think maybe upgrading typing_extensions will make the cut

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants