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

Adjusting the signature of a function on the basis of caller types in plugin #10216

Closed
Paul-Ilyin opened this issue Mar 15, 2021 · 3 comments
Closed
Labels
question topic-plugins The plugin API and ideas for new plugins

Comments

@Paul-Ilyin
Copy link

What is the proper way to adjust the signature of a function basing on actual caller types?
I have two ideas:

  1. using get_function_signature_hook, manually inferring all types from FunctionSigContext.args list
  2. using get_function_hook, calling FunctionContext.api.fail every time when arguments are invalid

Are there any better ways?

@AlexWaygood AlexWaygood added the topic-plugins The plugin API and ideas for new plugins label Apr 2, 2022
@ikonst
Copy link
Contributor

ikonst commented Jan 27, 2023

I've stumbled into the same issue and can't see any better way.

  • get_function_signature_hook is called too early so it doesn't get arg types.
  • get_function_hook is called late enough for expressions to be mapped to their types, but cannot change the signature anymore, so it would have to do its own validation (which is ironic since it's called from within ExpressionChecker.check_call)

Also, I found FunctionSigContext.args to be incomplete, w.r.t. kwargs. At the point where it's called we also know arg_names (and arg_kinds, though arguably you should know the function you're hooking), so why not pass that in?

@ikonst
Copy link
Contributor

ikonst commented Jan 27, 2023

If we address this, we should update the docs here w.r.t "Note that argument types aren't available yet":

mypy/mypy/plugin.py

Lines 423 to 426 in a9bc366

# A context for a function signature hook that infers a better signature for a
# function. Note that argument types aren't available yet. If you need them,
# you have to use a method hook instead.
class FunctionSigContext(NamedTuple):

("use a method hook instead" should've said "use a function hook instead" in this case -- copy-pasta)

Notably this comment is in @sobolevn's #9102 which added this hook, so this looks at least somewhat intentional, but I can't tell if there's a really good reason.

JelleZijlstra pushed a commit that referenced this issue Mar 6, 2023
Validate `attr.evolve` calls to specify correct arguments and types.

The implementation makes it so that at every point where `attr.evolve`
is called, the signature is modified to expect the attrs class'
initializer's arguments (but so that they're all kw-only and optional).

Notes:
- Added `class dict: pass` to some fixtures files since our attrs type
stubs now have **kwargs and that triggers a `builtin.dict` lookup in
dozens of attrs tests.
- Looking up the type of the 1st argument with
`ctx.api.expr_checker.accept(inst_arg)` which is a hack since it's not
part of the plugin API. This is a compromise for due to #10216.

Fixes #14525.
@ikonst
Copy link
Contributor

ikonst commented Jun 11, 2023

@ilevkivskyi let's have #15369 resolve this one too?

ilevkivskyi pushed a commit that referenced this issue Jun 17, 2023
Validate `dataclassses.replace` actual arguments to match the fields:

- Unlike `__init__`, the arguments are always named.
- All arguments are optional except for `InitVar`s without a default
value.

The tricks:
- We're looking up type of the first positional argument ("obj") through
private API. See #10216, #14845.
- We're preparing the signature of "replace" (for that specific
dataclass) during the dataclass transformation and storing it in a
"private" class attribute `__mypy-replace` (obviously not part of
PEP-557 but contains a hyphen so should not conflict with any future
valid identifier). Stashing the signature into the symbol table allows
it to be passed across phases and cached across invocations. The stashed
signature lacks the first argument, which we prepend at function
signature hook time, since it depends on the type that `replace` is
called on.

Based on #14526 but actually simpler.
Partially addresses #5152.

# Remaining tasks

- [x] handle generic dataclasses
- [x] avoid data class transforms
- [x] fine-grained mode tests

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question topic-plugins The plugin API and ideas for new plugins
Projects
None yet
Development

No branches or pull requests

4 participants