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

AttributeError: __qualname__ on Python 3.9 with typing.Sequence, Collection, Iterable or other generic aliases #493

Closed
palotasb opened this issue Dec 6, 2024 · 4 comments · Fixed by #497
Assignees

Comments

@palotasb
Copy link

palotasb commented Dec 6, 2024

To reproduce:

$ python3.9 -c 'from pydantic_settings import BaseSettings, CliSettingsSource
from typing import *
class S(BaseSettings):
    s: Sequence[int]  # or Collection, Iterable, etc.
CliSettingsSource(S)'
Traceback (most recent call last):
  File "<string>", line 5, in <module>
  File "/.../.venv/lib/python3.9/site-packages/pydantic_settings/sources.py", line 1165, in __init__
    self._connect_root_parser(
  File "/.../.venv/lib/python3.9/site-packages/pydantic_settings/sources.py", line 1574, in _connect_root_parser
    self._add_parser_args(
  File "/.../.venv/lib/python3.9/site-packages/pydantic_settings/sources.py", line 1656, in _add_parser_args
    kwargs['metavar'] = self._metavar_format(field_info.annotation)
  File "/.../.venv/lib/python3.9/site-packages/pydantic_settings/sources.py", line 1886, in _metavar_format
    return self._metavar_format_recurse(obj).replace(', ', ',')
  File "/.../.venv/lib/python3.9/site-packages/pydantic_settings/sources.py", line 1874, in _metavar_format_recurse
    list(map(self._metavar_format_recurse, self._get_modified_args(obj))), obj_qualname=obj.__qualname__
  File "/opt/homebrew/Cellar/python@3.9/3.9.20/Frameworks/Python.framework/Versions/3.9/lib/python3.9/typing.py", line 711, in __getattr__
    raise AttributeError(attr)
AttributeError: __qualname__

The error also happens when using types based on typing._BaseGenericAlias, like typing.Collection or typing.Iterable

The call ultimately fails on this line:
https://github.com/python/cpython/blob/3.9/Lib/typing.py#L711

The support for .__qualname__ for generic aliases was added only in Python 3.10:
https://github.com/python/cpython/blob/3.10/Lib/typing.py#L977

Hence this call on Python 3.9 is wrong:
https://github.com/pydantic/pydantic-settings/blob/v2.6.1/pydantic_settings/sources.py#L1874

@palotasb
Copy link
Author

palotasb commented Dec 6, 2024

Somehow pydantic~=2.10.0 is causing this break.

uv run --isolated --no-project --with pydantic-settings --with pydantic==2.10.0 --python python3.9 - <<EOF
from pydantic_settings import BaseSettings, CliSettingsSource
from typing import *
class S(BaseSettings):
    s: Sequence[int]  # or Collection, Iterable, etc.
CliSettingsSource(S)
print("success")
EOF

Results in:

Traceback (most recent call last):
  File "<string>", line 5, in <module>
  File "/.../lib/python3.9/site-packages/pydantic_settings/sources.py", line 1165, in __init__
    self._connect_root_parser(
  File "/.../lib/python3.9/site-packages/pydantic_settings/sources.py", line 1574, in _connect_root_parser
    self._add_parser_args(
  File "/.../lib/python3.9/site-packages/pydantic_settings/sources.py", line 1656, in _add_parser_args
    kwargs['metavar'] = self._metavar_format(field_info.annotation)
  File "/.../lib/python3.9/site-packages/pydantic_settings/sources.py", line 1886, in _metavar_format
    return self._metavar_format_recurse(obj).replace(', ', ',')
  File "/.../lib/python3.9/site-packages/pydantic_settings/sources.py", line 1874, in _metavar_format_recurse
    list(map(self._metavar_format_recurse, self._get_modified_args(obj))), obj_qualname=obj.__qualname__
  File "/opt/homebrew/Cellar/python@3.9/3.9.20/Frameworks/Python.framework/Versions/3.9/lib/python3.9/typing.py", line 711, in __getattr__
    raise AttributeError(attr)
AttributeError: __qualname__

But:

uv run --isolated --no-project --with pydantic-settings --with pydantic~=2.9.0 --python python3.9 - <<EOF
from pydantic_settings import BaseSettings, CliSettingsSource
from typing import *
class S(BaseSettings):
    s: Sequence[int]  # or Collection, Iterable, etc.
CliSettingsSource(S)
print("success")
EOF

Prints:

success

@hramezani
Copy link
Member

Thanks @palotasb for reporting this issue. I could reproduce it.

question: what do you want to achieve by CliSettingsSource(S)?

@palotasb
Copy link
Author

palotasb commented Dec 7, 2024

That line creates an instance of class CliSettingsSource and then immediately throws it away, just to demonstrate this bug.

The code I shared is just a minimal reproducible example for a more complex bug that we encountered in our internal codebase. The original code is proprietary so I can't share it, but this smaller example also makes it easier to identify and fix the underlying issue.

In our real codebase class S(BaseSettings): ... is a much more complex class where only one of the fields is a generic alias. Also in our case cli_settings_source = CliSettingSource(S) is not just initialized and thrown away, but instead it's assigned to a variable and used later on to do something useful.

@hramezani
Copy link
Member

@palotasb Thanks for your reply. Yes, it is related to a change in Pydantic. we have some custom logic ported from Pydantic.

I created #497 to fix the problem.
Could you please test it?

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

Successfully merging a pull request may close this issue.

3 participants