Skip to content

False negatives with dict.__(r)or__ #10678

Closed

Description

@adamtheturtle

I have code equivalent to the following:

from requests.structures import CaseInsensitiveDict

CaseInsensitiveDict() | dict()

mypy does not error.

My environment has:

  • mypy==1.5.1
  • types-requests==2.31.0.2
  • Python 3.11.5

At runtime I get:

TypeError: unsupported operand type(s) for |: 'CaseInsensitiveDict' and 'dict'

I would expect an [operator] error. For example, if I swap the CaseInsensitiveDict() for 1, I get:

error: Unsupported operand types for | ("int" and "dict[str, str]")  [operator]

I am happy to contribute, but I'm not sure what change is needed. I imagine a change is needed in https://github.com/python/typeshed/blob/main/stubs/requests/requests/structures.pyi.

Activity

  1. AlexWaygood commented on Sep 7, 2023

    @AlexWaygood
    Member

    In actual fact, the issue is our stubs for dict.__or__ and dict.__ror__:

    typeshed/stdlib/builtins.pyi

    Lines 1122 to 1129 in aa39b99

    @overload
    def __or__(self, __value: Mapping[_KT, _VT]) -> dict[_KT, _VT]: ...
    @overload
    def __or__(self, __value: Mapping[_T1, _T2]) -> dict[_KT | _T1, _VT | _T2]: ...
    @overload
    def __ror__(self, __value: Mapping[_KT, _VT]) -> dict[_KT, _VT]: ...
    @overload
    def __ror__(self, __value: Mapping[_T1, _T2]) -> dict[_KT | _T1, _VT | _T2]: ...

    We currently claim that dict.__(r)or__ accepts any Mapping, but this isn't actually true. At runtime, the object passed to dict.__or__ has to be an instance of dict (and requests.structures.CaseInsensitiveDict is not a subclass of dict).

    Ideally we'd make this change, but I think we tried it recently, and it caused an unfortunate number of false positives:

    diff --git a/stdlib/builtins.pyi b/stdlib/builtins.pyi
    index 71cccee16..cf4f857c5 100644
    --- a/stdlib/builtins.pyi
    +++ b/stdlib/builtins.pyi
    @@ -1120,13 +1120,13 @@ class dict(MutableMapping[_KT, _VT], Generic[_KT, _VT]):
         if sys.version_info >= (3, 9):
             def __class_getitem__(cls, __item: Any) -> GenericAlias: ...
             @overload
    -        def __or__(self, __value: Mapping[_KT, _VT]) -> dict[_KT, _VT]: ...
    +        def __or__(self, __value: dict[_KT, _VT]) -> dict[_KT, _VT]: ...
             @overload
    -        def __or__(self, __value: Mapping[_T1, _T2]) -> dict[_KT | _T1, _VT | _T2]: ...
    +        def __or__(self, __value: dict[_T1, _T2]) -> dict[_KT | _T1, _VT | _T2]: ...
             @overload
    -        def __ror__(self, __value: Mapping[_KT, _VT]) -> dict[_KT, _VT]: ...
    +        def __ror__(self, __value: dict[_KT, _VT]) -> dict[_KT, _VT]: ...
             @overload
    -        def __ror__(self, __value: Mapping[_T1, _T2]) -> dict[_KT | _T1, _VT | _T2]: ...
    +        def __ror__(self, __value: dict[_T1, _T2]) -> dict[_KT | _T1, _VT | _T2]: ...
             # dict.__ior__ should be kept roughly in line with MutableMapping.update()
             @overload  # type: ignore[misc]
             def __ior__(self, __value: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ...
  2. changed the title [-]No error for [/-] [+]False negatives with `dict.__(r)or__`[/+] on Sep 7, 2023
  3. AlexWaygood commented on Sep 7, 2023

    @AlexWaygood
    Member

    Okay, making the type hints more accurate here is actually less disruptive than I thought it would be! See #10679 (comment)

  4. adamtheturtle commented on Sep 7, 2023

    @adamtheturtle
    ContributorAuthor

    Thank you for picking this up so quickly.

  5. sobolevn commented on Oct 11, 2023

    @sobolevn
  6. AlexWaygood commented on Oct 12, 2023

    @AlexWaygood
  7. AlexWaygood commented on Oct 12, 2023

    @AlexWaygood
  8. sobolevn commented on Oct 18, 2023

    @sobolevn
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    stubs: false negativeType checkers do not report an error, but should

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions