Skip to content
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

Add __call__ attribute to function #12579

Closed
wants to merge 1 commit into from
Closed

Conversation

LeeeeT
Copy link
Contributor

@LeeeeT LeeeeT commented Aug 22, 2024

I guess the lack of this attribute causes mypy and pyright to emit a false positive error for the following code.

def a(): pass
a.__call__

mypy:

file.py:2: error: "Callable[[], Any]" not callable  [operator]

pyright:

file.py
  file.py:2:3 - error: Cannot access attribute "__call__" for class "function"
    Attribute "__call__" is unknown (reportFunctionMemberAccess)

Copy link
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

discord.py (https://github.com/Rapptz/discord.py)
- discord/backoff.py:75: error: Incompatible types in assignment (expression has type "function", variable has type "Callable[..., int | float]")  [assignment]

schemathesis (https://github.com/schemathesis/schemathesis)
+ src/schemathesis/service/extensions.py:182: error: Unused "type: ignore" comment  [unused-ignore]
+ src/schemathesis/specs/openapi/_hypothesis.py: note: At top level:
+ src/schemathesis/specs/openapi/_hypothesis.py:422: error: Unused "type: ignore" comment  [unused-ignore]
+ src/schemathesis/cli/__init__.py: note: At top level:
+ src/schemathesis/cli/__init__.py:1472: error: Unused "type: ignore" comment  [unused-ignore]
+ src/schemathesis/cli/__init__.py:1477: error: Unused "type: ignore" comment  [unused-ignore]
+ src/schemathesis/cli/__init__.py:1478: error: Unused "type: ignore" comment  [unused-ignore]

pandas (https://github.com/pandas-dev/pandas)
+ pandas/core/computation/ops.py:523: error: Unused "type: ignore" comment  [unused-ignore]

ignite (https://github.com/pytorch/ignite)
+ ignite/metrics/fbeta.py:154: error: Unused "type: ignore" comment  [unused-ignore]
+ ignite/metrics/fbeta.py:163: error: Unused "type: ignore" comment  [unused-ignore]

sphinx (https://github.com/sphinx-doc/sphinx)
+ sphinx/directives/__init__.py:327: error: Unused "type: ignore" comment  [unused-ignore]
+ sphinx/util/rst.py: note: At top level:
+ sphinx/util/rst.py:69: error: Unused "type: ignore" comment  [unused-ignore]

pandera (https://github.com/pandera-dev/pandera)
+ tests/strategies/test_strategies.py:1010: error: Unused "type: ignore" comment  [unused-ignore]
+ tests/core/test_decorators.py:429: error: Unused "type: ignore" comment  [unused-ignore]
+ tests/core/test_decorators.py:442: error: Unused "type: ignore" comment  [unused-ignore]

trio (https://github.com/python-trio/trio)
+ src/trio/_core/_ki.py:199: error: Unused "type: ignore" comment  [unused-ignore]
+ src/trio/_core/_ki.py:202: error: Unused "type: ignore" comment  [unused-ignore]

dd-trace-py (https://github.com/DataDog/dd-trace-py)
+ ddtrace/_trace/utils_redis.py:34: error: Unused "type: ignore" comment  [unused-ignore]

scrapy (https://github.com/scrapy/scrapy)
+ scrapy/extensions/httpcache.py:324: error: Unused "type: ignore" comment  [unused-ignore]

werkzeug (https://github.com/pallets/werkzeug)
- tests/test_formparser.py:436: error: Argument "stream_factory" to "MultiPartParser" has incompatible type "Callable[[], Any]"; expected "TStreamFactory | None"  [arg-type]
- tests/test_formparser.py:438: error: Non-overlapping identity check (left operand type: "TStreamFactory", right operand type: "Callable[[], Any]")  [comparison-overlap]

jax (https://github.com/google/jax)
+ jax/_src/core.py:3227: error: Unused "type: ignore" comment  [unused-ignore]
+ jax/_src/pallas/mosaic/random.py:158: error: Unused "type: ignore" comment  [unused-ignore]
+ jax/_src/pallas/mosaic/random.py:159: error: Unused "type: ignore" comment  [unused-ignore]
+ jax/_src/pallas/mosaic/random.py:160: error: Unused "type: ignore" comment  [unused-ignore]
+ jax/experimental/jax2tf/tests/shape_poly_test.py:2219: error: Unused "type: ignore" comment  [unused-ignore]

graphql-core (https://github.com/graphql-python/graphql-core)
+ src/graphql/execution/execute.py:554: error: Unused "type: ignore" comment  [unused-ignore]
+ tests/execution/test_abstract.py:44: error: Unused "type: ignore" comment  [unused-ignore]

black (https://github.com/psf/black)
- src/blackd/__init__.py:91:22: error: List item 0 has incompatible type "Callable[[Request, Callable[[Request], Awaitable[StreamResponse]]], Awaitable[StreamResponse]]"; expected "Middleware"  [list-item]

starlette (https://github.com/encode/starlette)
+ tests/test_applications.py:128: error: Unused "type: ignore" comment  [unused-ignore]

dragonchain (https://github.com/dragonchain/dragonchain)
- dragonchain/job_processor/contract_job.py:307:24: error: Cannot call function of unknown type  [operator]
- dragonchain/job_processor/contract_job.py:344:20: error: Cannot call function of unknown type  [operator]

prefect (https://github.com/PrefectHQ/prefect)
- src/prefect/cli/profile.py:177: error: Cannot call function of unknown type  [operator]

antidote (https://github.com/Finistere/antidote)
+ tests/lib/interface/test_custom.py:434: error: Unused "type: ignore" comment  [unused-ignore]
+ tests/lib/interface/test_custom.py:437: error: Unused "type: ignore" comment  [unused-ignore]
+ tests/lib/interface/test_custom.py:440: error: Unused "type: ignore" comment  [unused-ignore]
+ tests/lib/interface/test_custom.py:443: error: Unused "type: ignore" comment  [unused-ignore]
+ tests/lib/interface/test_custom.py:446: error: Unused "type: ignore" comment  [unused-ignore]
+ tests/lib/interface/test_custom.py:449: error: Unused "type: ignore" comment  [unused-ignore]

@srittau
Copy link
Collaborator

srittau commented Aug 22, 2024

This looks completely reasonable to me, suspiciously so, in fact. And the primer hits seem to agree, although I haven't looked at the "red" hits in detail. I wonder whether there was any reason we didn't do this earlier? Maybe it's related to the fact that this is a "fake" type that seems to mainly exist for the benefit of mypy? Still, at first glance this looks like a sensible change.

@JelleZijlstra
Copy link
Member

I'm not sure this is right. I suspect it simply makes mypy accept a lot more code because functions now get signature (*Any, **Any) -> Any more easily.

@AlexWaygood
Copy link
Member

AlexWaygood commented Aug 22, 2024

Yeah... if you look at e.g. https://github.com/pandas-dev/pandas/blob/59bb3f44a9bdd6d79704caab4d5c4e229945aefd/pandas/core/computation/ops.py#L522-L523 (one of the errors going away here), it's clear that mypy already knows that it has an object of type function there, and it's clear that it knows that the function object is in fact callable. So ISTM that it's very much a deliberate choice on the part of mypy to emit an error there (because it doesn't know the signature of the function being called); but if we make this change, it will no longer be able to emit that error, because it'll find the (*Any, **Any) -> Any signature in the stub before it's able to fallback to the hook it has for builtins.function and emit the proper error message

@Akuli
Copy link
Collaborator

Akuli commented Aug 27, 2024

builtins.function is a legacy thing that mypy needs for historical reasons, see python/mypy#8240. Usually it uses Callable or some other class with a __call__. My guess is that the "Cannot call function of unknown type" made sense at the time it was implemented, but IMO it doesn't fit our "prefer false negatives" philosophy. If we can somehow make builtins.function less painful to users, why not?

The pyright error does not make sense to me. Perhaps "function" in the pyright error refers to something else than builtins.function.

@JelleZijlstra
Copy link
Member

Closing as I don't think this makes mypy more useful. In the long term we should get rid of builtins.function from the stub (and I would recommend all type checkers to move away from relying on it).

@srittau
Copy link
Collaborator

srittau commented Oct 31, 2024

Closing as I don't think this makes mypy more useful. In the long term we should get rid of builtins.function from the stub (and I would recommend all type checkers to move away from relying on it).

Maybe we should just remove it? As far as I understand, at least mypy patches typeshed anyway, and this could be another incentive to remove reliance on this type.

@JelleZijlstra
Copy link
Member

That feels a bit rude to do unilaterally, I'd rather see this fixed in mypy first. (And I'm not sure if other type checkers also rely on builtins.function.)

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

Successfully merging this pull request may close these issues.

5 participants