Skip to content

Mypy false positive for converter class attribute in logging.Formatter class. #19241

Open

Description

@Yazan-Sharaya

Bug Report

Mypy false positive: Mypy thinks that setting the converter class attribute for a subclass of logging.Formatter to something like time.gmtime is wrong.

To Reproduce

class UTCFormatter(logging.Formatter):
    """An exact match for the `logging.Formatter` class, except that it logs the time in UTC."""
    converter = time.gmtime

Expected Behavior
No error, since time.gmtime is annotated as def gmtime(seconds: float | None = None, /) -> struct_time: ... and the converter attribute is annotated as converter: Callable[[float | None], struct_time].

Actual Behavior

logger.py:45: error: Incompatible types in assignment (expression has type "Callable[[], struct_time]", base class "Formatter" defined the type as "Callable[[float | None], struct_time]") [assignment]

Your Environment

  • Mypy version used: 1.16.0 (compiled: yes)
  • Mypy command-line flags: None
  • Mypy configuration options from mypy.ini (and other config files): None
  • Python version used: 3.13.4 (on Linux)

By the way, this issue didn't exist in my Python3.12 environment with an older version of mypy installed, but I don't have access to that environment right now so I can't provide which mypy version it had.

Activity

  1. A5rocks commented on Jun 6, 2025

    @A5rocks
    Collaborator

    Potentially this is fixed by #19233?

  2. sterliakov commented on Jun 6, 2025

    @sterliakov
    Collaborator

    @A5rocks nope, this is way more tricky: this only works because gmtime is a "special" C function. If you do the same with

    import time
    
    def my_gmtime(t: float | None = None, /) -> time.struct_time:
        return time.gmtime(t)
    
    class Demo:
        will_bind = my_gmtime
        wont_bind = time.gmtime
    
    Demo().wont_bind()
    Demo().wont_bind(0)
    Demo().will_bind()  # TypeError: 'Demo' object cannot be interpreted as an integer
    Demo().will_bind(0)  # TypeError: my_gmtime() takes from 0 to 1 positional arguments but 2 were given

    That happens when the RHS of the assignment is a builtin_function_or_method. We don't have typeshed information to distinguish those from plain python functions. Perhaps a short allowlist would do the trick? But gathering that take a lot of work... Can we script it?

  3. A5rocks commented on Jun 7, 2025

    @A5rocks
    Collaborator

    Yeah we could have a list for typeshed and go with "if it's from a .pyi file then it's native" as a heuristic for everything else.

    I'm not sure it's a good idea to allow this code if alternate implementations or compilations of Python would fail in this case, though. I'm in favor of saying this is intended...

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

    bugmypy got something wrongtopic-callsFunction calls, *args, **kwargs, defaults

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions