Skip to content

[1.16 regression] a cast is both needed and redundant at the same time #19170

Closed

Description

@hynek

Bug Report

Since 1.16, structlog's package type check fails with:

src/structlog/stdlib.py:1160: error: Redundant cast to "MutableMapping[str, Any]"  [redundant-cast]
                ed = p(logger, meth_name, cast(EventDict, ed))
                                          ^
Found 1 error in 1 file (checked 1 source file)

If I remove the cast, I get:

src/structlog/stdlib.py:1160: error: Argument 3 has incompatible type "Mapping[str, Any] | str | bytes | bytearray | tuple[Any, ...]"; expected
"MutableMapping[str, Any]"  [arg-type]
                ed = p(logger, meth_name, ed)
                                          ^~
Found 1 error in 1 file (checked 1 source file)

If I add a reveal_type, I get on both 1.15 & 1.16:

src/structlog/stdlib.py:1160: note: Revealed type is "typing.MutableMapping[builtins.str, Any]"
src/structlog/stdlib.py:1160: note: Revealed type is "Union[typing.Mapping[builtins.str, Any], builtins.str, builtins.bytes, builtins.bytearray, builtins.tuple[Any, ...]]"

So I guess Mypy got somehow smarter/confused about that?

To Reproduce

  • git clone git@github.com:hynek/structlog.git
  • git checkout d948027
  • run tox -e mypy-pkg

I really tried to create a minimal reproducer, but given that I don't quite understand what's going on, I've failed.

The relevant code is here: https://github.com/hynek/structlog/blob/e941e337d312eaf256595d9d09da4542c5e94d0b/src/structlog/stdlib.py#L1160

The types are defined here: https://github.com/hynek/structlog/blob/e941e337d312eaf256595d9d09da4542c5e94d0b/src/structlog/typing.py#L61-L76

Expected Behavior

I would expect it to keep working, or at least decide whether or not I need the cast. 🤓

Actual Behavior

I don't seem to find a way to make the code pass except using Any all over the place I guess.

Your Environment

edit I managed to work around it with the help of @Tinche but it still looks like I managed to break poor little Mypy.

Activity

  1. hauntsaninja commented on May 30, 2025

    @hauntsaninja
    Collaborator

    Thanks! Probably proximal cause is #18588

    If you'd like to upgrade to 1.16, p(logger, meth_name, cast(Any, ed)) is probably a foolproof workaround

  2. hauntsaninja commented on May 30, 2025

    @hauntsaninja
    Collaborator

    Minimised repro:

    from typing import Callable, cast
    
    class X: ...
    
    ProcessorReturnValue = X | str | bytes
    Processor = Callable[[X], ProcessorReturnValue]
    
    def main_cast(p: Processor) -> None:
        ed: ProcessorReturnValue
        ed = cast(X, ...)
    
        for _ in range(5):
            ed = p(cast(X, ed))
    
    def main_no_cast(p: Processor) -> None:
        ed: ProcessorReturnValue
        ed = cast(X, ...)
    
        for _ in range(5):
            ed = p(ed)
    

    I guess the cast is redundant the second time through the loop...

  3. tyralla commented on May 30, 2025

    @tyralla
    Collaborator

    Yes, I hope we just have to handle codes.REDUNDANT_CAST the same way as codes.REDUNDANT_EXPR in #19118. Maybe there are more such candidates? (UNUSED_IGNORE or so)

  4. added a commit that references this issue on Jun 6, 2025
    55c4067
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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions