Skip to content

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

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

Open
Yazan-Sharaya opened this issue Jun 6, 2025 · 3 comments
Labels
bug mypy got something wrong topic-calls Function calls, *args, **kwargs, defaults

Comments

@Yazan-Sharaya
Copy link

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.

@Yazan-Sharaya Yazan-Sharaya added the bug mypy got something wrong label Jun 6, 2025
@A5rocks
Copy link
Collaborator

A5rocks commented Jun 6, 2025

Potentially this is fixed by #19233?

@sterliakov
Copy link
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?

@sterliakov sterliakov added the topic-calls Function calls, *args, **kwargs, defaults label Jun 6, 2025
@A5rocks
Copy link
Collaborator

A5rocks commented Jun 7, 2025

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
Labels
bug mypy got something wrong topic-calls Function calls, *args, **kwargs, defaults
Projects
None yet
Development

No branches or pull requests

3 participants