Skip to content

TypeError: _eval_type() missing 1 required positional argument: 'type_params' with import module #118418

Closed

Description

@enefry

Bug report

What happened?

import openai
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.12/site-packages/openai/__init__.py", line 8, in <module>
    from . import types
  File "/usr/local/lib/python3.12/site-packages/openai/types/__init__.py", line 5, in <module>
    from .batch import Batch as Batch
  File "/usr/local/lib/python3.12/site-packages/openai/types/batch.py", line 7, in <module>
    from .._models import BaseModel
  File "/usr/local/lib/python3.12/site-packages/openai/_models.py", line 35, in <module>
    from ._utils import (
  File "/usr/local/lib/python3.12/site-packages/openai/_utils/__init__.py", line 3, in <module>
    from ._utils import (
  File "/usr/local/lib/python3.12/site-packages/openai/_utils/_utils.py", line 24, in <module>
    from .._compat import parse_date as parse_date, parse_datetime as parse_datetime
  File "/usr/local/lib/python3.12/site-packages/openai/_compat.py", line 48, in <module>
    from pydantic.v1.typing import (
  File "/usr/local/lib/python3.12/site-packages/pydantic/v1/__init__.py", line 7, in <module>
    from .env_settings import BaseSettings
  File "/usr/local/lib/python3.12/site-packages/pydantic/v1/env_settings.py", line 23, in <module>
    class BaseSettings(BaseModel):
  File "/usr/local/lib/python3.12/site-packages/pydantic/v1/main.py", line 178, in __new__
    annotations = resolve_annotations(namespace.get('__annotations__', {}), namespace.get('__module__', None))
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pydantic/v1/typing.py", line 400, in resolve_annotations
    value = _eval_type(value, base_globals, None)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
import fastapi
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.12/site-packages/fastapi/__init__.py", line 7, in <module>
    from .applications import FastAPI as FastAPI
  File "/usr/local/lib/python3.12/site-packages/fastapi/applications.py", line 16, in <module>
    from fastapi import routing
  File "/usr/local/lib/python3.12/site-packages/fastapi/routing.py", line 22, in <module>
    from fastapi import params
  File "/usr/local/lib/python3.12/site-packages/fastapi/params.py", line 5, in <module>
    from fastapi.openapi.models import Example
  File "/usr/local/lib/python3.12/site-packages/fastapi/openapi/models.py", line 68, in <module>
    class Contact(BaseModelWithConfig):
  File "/usr/local/lib/python3.12/site-packages/pydantic/_internal/_model_construction.py", line 197, in __new__
    set_model_fields(cls, bases, config_wrapper, types_namespace)
  File "/usr/local/lib/python3.12/site-packages/pydantic/_internal/_model_construction.py", line 474, in set_model_fields
    fields, class_vars = collect_model_fields(cls, bases, config_wrapper, types_namespace, typevars_map=typevars_map)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pydantic/_internal/_fields.py", line 131, in collect_model_fields
    type_hints = get_cls_type_hints_lenient(cls, types_namespace)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pydantic/_internal/_typing_extra.py", line 226, in get_cls_type_hints_lenient
    hints[name] = eval_type_lenient(value, globalns, localns)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pydantic/_internal/_typing_extra.py", line 238, in eval_type_lenient
    return eval_type_backport(value, globalns, localns)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pydantic/_internal/_typing_extra.py", line 255, in eval_type_backport
    return typing._eval_type(value, globalns, localns)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: _eval_type() missing 1 required positional argument: 'type_params'

CPython versions tested on:

3.12

Operating systems tested on:

Linux

Output from running 'python -VV' on the command line:

Python 3.12.3+ (heads/3.12-dirty:817190c, Apr 29 2024, 12:17:20) [GCC 11.4.0]

Linked PRs

Activity

  1. added
    type-crashA hard crash of the interpreter, possibly with a core dump
    on Apr 30, 2024
  2. sobolevn commented on Apr 30, 2024

    @sobolevn
    Member

    Well, this has several points.

    1. This is a private API. It can change at any point and we don't provide any guarantees about it.
    2. But, people are clearly using this private API in rather big projects.
    3. And we can help them by simply providing type_params=None default in _eval_type, it won't change the code semantics. But, it will help multiple projects.
    4. But, there are still versions of 3.12 available that won't have this default. So, you would need a compat layer anyway.
    5. And using _eval_type without type_params (or with proposed None default) may just hide the bug (when working with PEP695's functions / classes) instead of being correct in all cases

    This is hard :)

  3. added a commit that references this issue on Apr 30, 2024
  4. sobolevn commented on Apr 30, 2024

    @sobolevn
    Member

    See my sobolevn@d7483de commit that adds None default. I am not sure that we should go for it.

  5. added
    type-bugAn unexpected behavior, bug, or error
    3.12only security fixes
    3.13only security fixes
    and removed
    type-crashA hard crash of the interpreter, possibly with a core dump
    on Apr 30, 2024
  6. enefry commented on Apr 30, 2024

    @enefry
    Author

    🤔
    Yes, I think default value will more better for exists code

    Well, this has several points.

    1. This is a private API. It can change at any point and we don't provide any guarantees about it.
    2. But, people are clearly using this private API in rather big projects.
    3. And we can help them by simply providing type_params=None default in _eval_type, it won't change the code semantics. But, it will help multiple projects.
    4. But, there are still versions of 3.12 available that won't have this default. So, you would need a compat layer anyway.
    5. And using _eval_type without type_params (or with proposed None default) may just hide the bug (when working with PEP695's functions / classes) instead of being correct in all cases

    This is hard :)

  7. sobolevn commented on Apr 30, 2024

    @sobolevn
    Member

    You closed the issue too early :)

  8. AlexWaygood commented on Apr 30, 2024

    @AlexWaygood
    Member

    @enefry, I can't reproduce this issue based on the snippet you gave above:

    (3123-env) % python -m pip freeze
    annotated-types==0.6.0
    anyio==4.3.0
    certifi==2024.2.2
    distro==1.9.0
    fastapi==0.110.3
    h11==0.14.0
    httpcore==1.0.5
    httpx==0.27.0
    idna==3.7
    openai==1.24.0
    pydantic==2.7.1
    pydantic_core==2.18.2
    sniffio==1.3.1
    starlette==0.37.2
    tqdm==4.66.2
    typing_extensions==4.11.0
    (3123-env) % python --version
    Python 3.12.3
    (3123-env) % python
    Python 3.12.3 (main, Apr 30 2024, 10:12:02) [Clang 15.0.0 (clang-1500.3.9.4)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import openai
    >>> import fastapi
    >>> exit()

    Could you post a minimal, reproducible example please? :-)

  9. enefry commented on Apr 30, 2024

    @enefry
    Author

    you may checkout commit before this commit
    See my https://github.com/sobolevn/cpython/commit/d7483de262e53059671946b147c1fe62986582c7 commit that adds None default. I am not sure that we should go for it.

    my library version is :
    fastapi>=0.110.1
    openai>=1.14.0

    @enefry, I can't reproduce this issue based on the snippet you gave above:

    (3123-env) % python -m pip freeze
    annotated-types==0.6.0
    anyio==4.3.0
    certifi==2024.2.2
    distro==1.9.0
    fastapi==0.110.3
    h11==0.14.0
    httpcore==1.0.5
    httpx==0.27.0
    idna==3.7
    openai==1.24.0
    pydantic==2.7.1
    pydantic_core==2.18.2
    sniffio==1.3.1
    starlette==0.37.2
    tqdm==4.66.2
    typing_extensions==4.11.0
    (3123-env) % python --version
    Python 3.12.3
    (3123-env) % python
    Python 3.12.3 (main, Apr 30 2024, 10:12:02) [Clang 15.0.0 (clang-1500.3.9.4)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import openai
    >>> import fastapi
    >>> exit()

    Could you post a minimal, reproducible example please? :-)

  10. 5 remaining items

  11. sobolevn commented on Apr 30, 2024

    @sobolevn
    Member

    I guess this would be the compat code to workaround this problem:

    if sys.version_info < (3, 12, 5):  # or whatever version it would be
        # Python has a bug in its `_eval_type` function.
        locals_namespace = {}  # whatever they are
        if sys.version_info >= (3, 12):
            # Python since 3.12.0 and until 3.12.4 also has a bug with NameError
            # for types using PEP695 type params:
            locals_namespace.update({
                param.__name__: param 
                for param in cls_or_func.__type_params__
            })
        result = typing._eval_type(type_, globals_namespace, locals_namespace, recursive_guard=...)
    else:
        result = typing._eval_type(
            type_, globals_namespace, locals_namespace, 
            cls_or_func.__type_params__,  # since 3.12.5 `_eval_type` has a correct way to do it
            recursive_guard=...,
        )

    PR is on its way.

  12. AlexWaygood commented on Apr 30, 2024

    @AlexWaygood
    Member

    Okay, if it was "just" pydantic that was going to be broken with this, then I would have argued that they'd have plenty of time to patch their code before the release of Python 3.12.4, and that adding a default value for this parameter would just mean that pydantic would have the same bug in their use of _eval_type that we had prior to 1e3e7ce. But it's not just pydantic. Unfortunately, typing._eval_type has many users outside the stdlib, much though I wish it didn't:

    We can't afford to break this many libraries in a patch release, so I now agree that we have no choice except to add a default value to the new parameter. After #118431 is merged, however, I would like to propose that we immediately make a Python 3.13-only change to start issuing a DeprecationWarning if None is passed to this parameter. Failing to pass a value to this parameter shouldn't be encouraged: it's going to lead to incorrect behaviour if _eval_type is called on a stringified annotation that references a PEP-695 type parameter.

  13. added a commit that references this issue on Apr 30, 2024
  14. added a commit that references this issue on Apr 30, 2024
  15. terryjreedy commented on Apr 30, 2024

    @terryjreedy
    Member

    Alternate suggestion: in 3.12.4 and successors, default to a private sentinel so as to detect defaulting versus explicitly passing None. If not overriden, convert it to None and issue a deprecation warning. Keep the proper signature in 3.13. There will be months for users of the private function to adjust before the latter's release. After .0b1, we can discuss making the function public, or making some other change.

  16. added a commit that references this issue on Apr 30, 2024
  17. AlexWaygood commented on Apr 30, 2024

    @AlexWaygood
    Member

    Alternate suggestion: in 3.12.4 and successors, default to a private sentinel so as to detect defaulting versus explicitly passing None. If not overriden, convert it to None and issue a deprecation warning.

    Unfortunately, lots of users run tests with -Werror in CI, so I think adding a deprecation warning in a patch release could be just as breaking as adding a new parameter without a default. So I think the earliest we can add this warning is Python 3.13.

    Possibly we could consider being aggressive with the deprecation period, but I think if we've conceded that this change could break quite a few libraries (even if it shouldn't), we may as well go through the standard two-cycle deprecation period for removing the default.

  18. sobolevn commented on Apr 30, 2024

    @sobolevn
    Member

    I propose not to touch _eval_type anymore. Let's just provide a new public API (since this one is already public de facto). And just ship the backport in typing_extensions?

  19. added a commit that references this issue on May 7, 2024
  20. AlexWaygood commented on May 7, 2024

    @AlexWaygood
    Member

    I propose not to touch _eval_type anymore. Let's just provide a new public API (since this one is already public de facto). And just ship the backport in typing_extensions?

    I don't think that's sufficient: there are already many libraries using the private API, and even if we provide a public API for them in 3.14, it will take time for them to migrate to the new public API. In the meantime, the footgun will continue to exist in the private API that they're already using.

    I've put up #118695 to deprecate failing to pass a value to the new parameter. It's a pretty simple change and I think we should go ahead with it.

  21. added a commit that references this issue on May 7, 2024
  22. added 2 commits that reference this issue on May 8, 2024
  23. samuelcolvin commented on Jun 16, 2024

    @samuelcolvin
    SponsorContributor

    Hi everyone, sorry I somehow missed the notification for this one.

    @AlexWaygood thanks so much for accommodating my egregious use of a private API.

    We'll get Pydantic working with 3.12.4 and 3.13 ASAP.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.12only security fixes3.13only security fixestopic-typingtype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions