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

Wrong type annotation for pydantic_core.TzInfo(0) #1605

Open
barisione opened this issue Jan 23, 2025 · 0 comments · May be fixed by #1611
Open

Wrong type annotation for pydantic_core.TzInfo(0) #1605

barisione opened this issue Jan 23, 2025 · 0 comments · May be fixed by #1611

Comments

@barisione
Copy link

barisione commented Jan 23, 2025

This code (only the first part is relevant to the bug, the second part with MyTzInfo shows why this happens):

import datetime

import pydantic_core


# Mypy complains:
#   Too many arguments for "TzInfo"  [call-arg]
# But running the code works and prints "UTC".
print(pydantic_core.TzInfo(0))

# Mypy is happy with this, but running the code results in:
#   TypeError: TzInfo.__new__() missing 1 required positional argument: 'seconds'
try:
    print(pydantic_core.TzInfo())
except TypeError as exc:
    print(f"Got error as expected: {exc}")
else:
    raise AssertionError("Expected TypeError")


class MyTzInfo(datetime.tzinfo):
    def tzname(self, dt: datetime.datetime | None) -> str:
        return "Fake timezone"

    def utcoffset(self, dt: datetime.datetime | None) -> datetime.timedelta:
        return datetime.timedelta(seconds=42)

    def dst(self, dt: datetime.datetime | None) -> datetime.timedelta:
        return datetime.timedelta(seconds=0)

    def __repr__(self) -> str:
        return f"MyTzInfo: {self.tzname(None)}"


# Mypy compains with this as well, but running the code works as `datetime.tzinfo` ignores its
# arguments.
print(MyTzInfo())
print(MyTzInfo(0))
print(MyTzInfo(0, "foo", 42))

Produces this output:

UTC
Got error as expected: TzInfo.__new__() missing 1 required positional argument: 'seconds'
MyTzInfo: Fake timezone
MyTzInfo: Fake timezone
MyTzInfo: Fake timezone

The problem is that datetime.tzinfo ignores any argument passed to it, but it's annotated in typeshed as not accepting any argument.

In pydantic_core, the interface for TzInfo is defined as a final class TzInfo(datetime.tzinfo) which doesn't have an __init__, so mypy assumes it doesn't accept arguments. On the other hand, the implementation requires the number of seconds.

I'm using the most recent version of pydantic and mypy from PyPi:

$ pip show mypy
Name: mypy
Version: 1.14.1
Summary: Optional static typing for Python
Home-page:
Author:
Author-email: Jukka Lehtosalo <[email protected]>
License: MIT
Location: /Users/bari/.pyenv/versions/3.10.1/envs/tmp3.10/lib/python3.10/site-packages
Requires: typing_extensions, mypy_extensions, tomli
Required-by:

$ pip show pydantic_core
Name: pydantic_core
Version: 2.27.2
Summary: Core functionality for Pydantic validation and serialization
Home-page: https://github.com/pydantic/pydantic-core
Author:
Author-email: Samuel Colvin <[email protected]>
License: MIT
Location: /Users/bari/.pyenv/versions/3.10.1/envs/tmp3.10/lib/python3.10/site-packages
Requires: typing-extensions
Required-by: pydantic

Note that, in practice, I don't need to use pydantic.TzInfo in my production code. I'm only using it in some unit tests as my code previously tripped up when dealing with pydantic converting Python's native datetime.timezone.utc.

@Viicos Viicos linked a pull request Jan 28, 2025 that will close this issue
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant