Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use mypy to type check a date that cannot be a datetime?

Tags:

python

mypy

Let's say I have this function:

def subtract_dates(date1: datetime.date, date2: datetime.date):
    return date1 - date2

If I try to call subtract_dates(datetime.date.today(), datetime.datetime.now()) this will raise a TypeError because I'm trying to subtract a datetime object from a date object.

However, because datetime inherits from date (see for instance this issue), MyPy does not raise an error on the above call to subtract_dates.

Is there a way to add a MyPy type hint that allows a date object but not a datetime object?

like image 213
YPCrumble Avatar asked Oct 22 '25 05:10

YPCrumble


2 Answers

I'd solve it by function overloading. Example:

import datetime
from typing import NoReturn, overload


@overload
def subtract_dates(date1: datetime.date, date2: datetime.datetime) -> NoReturn: ...

@overload
def subtract_dates(date1: datetime.datetime, date2: datetime.date) -> NoReturn: ...

@overload
def subtract_dates(date1: datetime.date, date2: datetime.date) -> datetime.timedelta: ...


# this is your original function implementation    
def subtract_dates(date1: datetime.date, date2: datetime.date):
    return date1 - date2

Now, subtracting dates is still allowed, while using datetime objects will be caught:

subtract_dates(datetime.date.today(), datetime.datetime.now()).days

will give you an error

<string>:1: error: "NoReturn" has no attribute "days"
Found 1 error in 1 file (checked 1 source file)
like image 53
hoefling Avatar answered Oct 23 '25 21:10

hoefling


The only way to really do this definitively is to recreate all the stubs for datetime in such a way that it pretends not to be a subclass of date. This is really tedious though.

That's why I did it for you and put it into an open source library.

You can see some examples of how to use it here.

like image 36
Glyph Avatar answered Oct 23 '25 20:10

Glyph