Skip to content

Conversation

@m-aciek
Copy link

@m-aciek m-aciek commented Dec 20, 2025

Fixes #9015.

This PR introduces unsafe-subtype error code, which makes mypy check fail for unsafe date and datetime classes inheritance, following @JukkaL's comment in the related issue.

Before:

from datetime import date, datetime

if datetime.now() < date.today():  # passing the type checking
    print("that's a surprise!")

After:

from datetime import date, datetime

if datetime.now() < date.today():  # E: Unsupported operand types for < ("datetime" and "date")
    print("that's a surprise!")

This is Copilot agent's work with my prompts' guidance. I reviewed the produced code. The change includes tests and documentation.

Alternatives considered

Possible addition

Copilot AI and others added 11 commits December 19, 2025 22:59
Co-authored-by: m-aciek <9288014+m-aciek@users.noreply.github.com>
Co-authored-by: m-aciek <9288014+m-aciek@users.noreply.github.com>
Co-authored-by: m-aciek <9288014+m-aciek@users.noreply.github.com>
Co-authored-by: m-aciek <9288014+m-aciek@users.noreply.github.com>
Co-authored-by: m-aciek <9288014+m-aciek@users.noreply.github.com>
Co-authored-by: m-aciek <9288014+m-aciek@users.noreply.github.com>
…em redundant

Co-authored-by: m-aciek <9288014+m-aciek@users.noreply.github.com>
Co-authored-by: m-aciek <9288014+m-aciek@users.noreply.github.com>
Co-authored-by: m-aciek <9288014+m-aciek@users.noreply.github.com>
@github-actions
Copy link
Contributor

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

Copy link
Collaborator

@sterliakov sterliakov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding str vs Iterable[str]: are you sure it's actually a good thing to do? I hate pandas decision to prohibit passing a single string as DataFrame columns, it adds unnecessary friction to quickly creating a one-off dataframe (one has to do columns=list('abc')). Is there a reason to reject passing str as Iterable[str] intentionally? Is it a common source of bugs in some codebase you know/work with?

Comment on lines +541 to +545
if self.options and codes.UNSAFE_SUBTYPE in self.options.enabled_error_codes:
# Block unsafe subtyping relationships when the error code is enabled
for subclass, superclass in UNSAFE_SUBTYPING_PAIRS:
if lname == subclass and rname == superclass:
return False
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if self.options and codes.UNSAFE_SUBTYPE in self.options.enabled_error_codes:
# Block unsafe subtyping relationships when the error code is enabled
for subclass, superclass in UNSAFE_SUBTYPING_PAIRS:
if lname == subclass and rname == superclass:
return False
if (
self.options
and codes.UNSAFE_SUBTYPE in self.options.enabled_error_codes
and (lname, rname) in UNSAFE_SUBTYPING_PAIRS
):
return False

If I'm not mistaken, you don't actually have to iterate manually for this

# Each tuple is (subclass_fullname, superclass_fullname).
# These are cases where a class is a subclass at runtime but treating it
# as a subtype can cause runtime errors.
UNSAFE_SUBTYPING_PAIRS: Final = [("datetime.datetime", "datetime.date")]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a set, but I'm not sure whether it is actually faster than a single-element list

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 this pull request may close these issues.

datetimes and dates are not substitutable but mypy thinks they are

2 participants