2

Consider the following

from datetime import datetime

def tle_request(date_time: list[int]) -> datetime:
    # Attempting to create a datetime object using unpacking
    general_date: datetime = datetime(*date_time[:3])  # This causes a mypy error
    return general_date

# Example usage
my_time = tle_request([2023, 9, 27])
print(f"Hello {my_time}")

Although it works:

❯ python main.py
Hello 2023-09-27 00:00:00

mypy is prompting the following error:

❯ mypy main.py
main.py:5: error: Argument 1 to "datetime" has incompatible type "*list[int]"; expected "tzinfo | None"  [arg-type]
Found 1 error in 1 file (checked 1 source file)

That is very weird because if I change datetime(*date_time[:3]) with datetime(date_time[0], date_time[1], date_time[2]). I works:

❯ mypy main.py
Success: no issues found in 1 source file

Why? Unpacking is really necessary and I see no reason for mypy to complain about it.

3
  • Clarification needed - in the first code example, did you mean to use *datetime[:3] as the parameter passed to datetime? You are currently passing date_time[0], date_time[1], date_time[2] but then later mention replacing *datetime[:3] with date_time[0], date_time[1], date_time[2] Commented Sep 27, 2024 at 23:07
  • 1
    @nigh_anxiety Corrected. Commented Sep 27, 2024 at 23:24
  • Let's begin with the trivial: this code is correctly flagged by mypy. Your signature allows tle_request([1]). It's fine to slice a list to a longer length ([1][:3] is [1]), so datetime call will fail. Slicing restricts the upper limit ("list no longer than three items"), but does not guarantee the lower limit. Commented Sep 28, 2024 at 15:50

1 Answer 1

4

Have a look at the signature of datetime.datetime.__new__:

    def __new__(
        cls,
        year: SupportsIndex,
        month: SupportsIndex,
        day: SupportsIndex,
        hour: SupportsIndex = ...,
        minute: SupportsIndex = ...,
        second: SupportsIndex = ...,
        microsecond: SupportsIndex = ...,
        tzinfo: _TzInfo | None = ...,  # Expected "tzinfo | None"
        *,
        fold: int = ...,
    ) -> Self: ...

mypy does not know the size of your list[int], so the best guess it does in this situation is to fill all the arguments to __new__ with int. The last positional parameter of __new__ is not allowed to be an int, which is what mypy is warning you of.

Using list[int] with an unpacking operation here simply won't work for static typing purposes - if you want to use unpacking, use a tuple:

def tle_request(date_time: tuple[int, int, int]) -> datetime: ...
Sign up to request clarification or add additional context in comments.

10 Comments

Maybe even define a custom datetime_params type, depending on how often you're planning to do this.
"so the best guess it does in this situation is to fill all the arguments to __new__ with int": I thought that, by writing *date_time[:3], mypy would understand that only the three first arguments of __new__ should be filled with int.
@RubemPacelli That won't work either: date_time: list[int] implies that you can pass a subtype of list[int], and subtypes can override __getitem__ and __iter__ to do anything in indexing and unpacking. The only interpretation where [:3] is guaranteed to work is if you had a literal list (e.g. [1, 2, 3, 4, 5][:3]), but if you were doing that, the slicing would be redundant.
@dROOOze So ... *[*date_time][:3]?
@RubemPacelli then # type: ignore is your best bet. It's python, not a statically-typed language. If some tooling prevents you from doing smth that seems necessary, you can tell that tool to STFU. It's not always the best course of action (e.g. I hardly understand how this function can be useful, but you probably have some better reason), but such option is always available. Your code is unsafe - if there's some meta-knowledge that makes it safe, you can just tell the linter to stop yelling at you.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.