Skip to content

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

Closed
@hynek

Description

@hynek
Member

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

hauntsaninja

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

hauntsaninja

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...

tyralla

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)

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

      Participants

      @hynek@hauntsaninja@tyralla@sterliakov

      Issue actions

        [1.16 regression] a cast is both needed and redundant at the same time · Issue #19170 · python/mypy