Skip to content

Incorrect hints for TestCase.assertRaises #8372

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

Closed
ShaneHarvey opened this issue Jul 23, 2022 · 12 comments · Fixed by #8373 or #8399
Closed

Incorrect hints for TestCase.assertRaises #8372

ShaneHarvey opened this issue Jul 23, 2022 · 12 comments · Fixed by #8373 or #8399

Comments

@ShaneHarvey
Copy link
Contributor

ShaneHarvey commented Jul 23, 2022

Pymongo's mypy tests pass with mypy==0.942 but fail with mypy==0.971 with this error:

test/test_bson.py: note: In member "assertInvalid" of class "TestBSON":
test/test_bson.py:120: error: Argument 2 to "assertRaises" of "TestCase" has incompatible type
"Callable[[Union[bytes, memoryview, mmap, array[Any]], Optional[CodecOptions[_DocumentType]]], _DocumentType]"; expected "Callable[..., object]" 
[arg-type]
            self.assertRaises(InvalidBSON, decode, data)
                                           ^

I believe this was a regression caused by the changes in #7012

@JelleZijlstra
Copy link
Member

What is _DocumentType here? The error message may indicate a mypy bug, since Callable is covariant in its return type and every type should be compatible with object.

@ShaneHarvey
Copy link
Contributor Author

decode() is defined here:

def decode(
    data: _ReadableBuffer, codec_options: "Optional[CodecOptions[_DocumentType]]" = None
) -> _DocumentType:
    ...

and _DocumentType is defined here:

class CodecOptions(Tuple, Generic[_DocumentType]):
    document_class: Type[_DocumentType]
    tz_aware: bool
    uuid_representation: int
    unicode_decode_error_handler: Optional[str]
    tzinfo: Optional[datetime.tzinfo]
    type_registry: TypeRegistry

    def __new__(
        cls: Type[CodecOptions],
        document_class: Optional[Type[_DocumentType]] = ...,
        tz_aware: bool = ...,
        uuid_representation: Optional[int] = ...,
        unicode_decode_error_handler: Optional[str] = ...,
        tzinfo: Optional[datetime.tzinfo] = ...,
        type_registry: Optional[TypeRegistry] = ...,
    ) -> CodecOptions[_DocumentType]: ...

@JelleZijlstra
Copy link
Member

Thanks! Maybe it has something to do with the TypeVar, but I'm not having much luck trying to reproduce this in a smaller example. https://mypy-play.net/?mypy=latest&python=3.10&gist=1f95669029e61cff3091eb3b75c4e2bd

@ShaneHarvey
Copy link
Contributor Author

ShaneHarvey commented Jul 23, 2022

Here's the smallest repro I could get:

import unittest
from typing import TypeVar, Any, Mapping, Optional

T = TypeVar("T", bound=Mapping[str, Any])

def raises(opts: Optional[T] = None) -> T:
    if opts is None:
        raise TypeError()
    return opts

class TestAssertRaises(unittest.TestCase):
    def test_assertRaises(self) -> None:
        self.assertRaises(TypeError, raises, None)

output:

mypy --strict repro-mypy-bug.py
repro-mypy-bug.py: note: In member "test_assertRaises" of class "TestAssertRaises":
repro-mypy-bug.py:13: error: Argument 2 to "assertRaises" of "TestCase" has incompatible type "Callable[[Optional[T]], T]"; expected
"Callable[..., object]"  [arg-type]
            self.assertRaises(TypeError, raises, None)
                                         ^
Found 1 error in 1 file (checked 1 source file)

@JelleZijlstra
Copy link
Member

JelleZijlstra commented Jul 23, 2022

Thanks! Here's a repro for the mypy bug that doesn't rely on the stubs:

from typing import Callable, TypeVar, Any, Mapping, Optional
T = TypeVar("T", bound=Mapping[str, Any])
def raises(opts: Optional[T]) -> T: pass
def assertRaises(cb: Callable[..., object]) -> None: pass
assertRaises(raises)
main.py:11: error: Argument 1 to "assertRaises" has incompatible type "Callable[[Optional[T]], T]"; expected "Callable[..., object]"

@AlexWaygood given this mypy bug, what do you think of returning to Callable[..., Any] for now?

@hauntsaninja
Copy link
Collaborator

Opened python/mypy#13220 to track the mypy issue

@AlexWaygood
Copy link
Member

@AlexWaygood given this mypy bug, what do you think of returning to Callable[..., Any] for now?

Sounds good to me.

@AlexWaygood
Copy link
Member

I merged #8373. Is there a risk that this mypy bug could hit users for any of the other functions that were changed in #7012? Or are we good to leave this closed now?

@JelleZijlstra
Copy link
Member

It could hit in other cases, I think the main ingredient is the constrained TypeVar. Not sure how likely it is to actually come up though.

@srittau
Copy link
Collaborator

srittau commented Jul 23, 2022

Reopening. We should at least document why we use Any in the PR, instead of object. That said, I'd prefer if mypy would fix their bug, or alternatively that mypy patches their version of typeshed when shipping the next release.

@srittau srittau reopened this Jul 23, 2022
@hauntsaninja
Copy link
Collaborator

I can confirm that the issue is the bound TypeVar (and possible constrained TypeVars have a similar problem). I tried several ways of fixing, but they all broke a bunch of stuff -- curious if anyone has ideas on alternate mypy fixes.

@ShaneHarvey
Copy link
Contributor Author

Is there a risk that this mypy bug could hit users for any of the other functions that were changed in #7012? Or are we good to leave this closed now?

My mistake, the other functions also need to be changed to use Any, for example addCleanup is also broken:

test/test_change_stream.py: note: In member "setFailPoint" of class "TestAllLegacyScenarios":
test/test_change_stream.py:1088: error: Argument 1 to "addCleanup" of
"TestCase" has incompatible type
"Callable[[Union[str, MutableMapping[str, Any]], Any, bool, Optional[Sequence[Union[str, int]]], Optional[_ServerMode], Optional[CodecOptions[_CodecDocumentType]], Optional[ClientSession], Optional[Any], KwArg(Any)], _CodecDocumentType]";
expected
"Callable[[Union[str, MutableMapping[str, Any]], Any, bool, Optional[Sequence[Union[str, int]]], Optional[_ServerMode], Optional[CodecOptions[_CodecDocumentType]], Optional[ClientSession], Optional[Any], KwArg(Any)], object]"
 [arg-type]
                client_context.client.admin.command,
                ^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants