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

[red-knot] Use try_call_dunder infrastructure consistently #16371

Open
sharkdp opened this issue Feb 25, 2025 · 4 comments
Open

[red-knot] Use try_call_dunder infrastructure consistently #16371

sharkdp opened this issue Feb 25, 2025 · 4 comments
Labels
bug Something isn't working red-knot Multi-file analysis & type inference

Comments

@sharkdp
Copy link
Contributor

sharkdp commented Feb 25, 2025

Summary

__enter__ and __exit__ should be treated like all other dunder methods. However, while working on #16368, I noticed that we look them up manually (and potentially incorrectly) here …

let enter = context_manager_ty.member(self.db(), "__enter__");
let exit = context_manager_ty.member(self.db(), "__exit__");

… and call them manually (and potentially incorrectly) here:

let target_ty = enter_ty
.try_call(self.db(), &CallArguments::positional([context_expression_ty]))

This only leads to incorrect behavior in very exotic situations like the following, where we incorrectly raise a "Object of type Manager cannot be used with with because it does not correctly implement __enter__" diagnostic:

from __future__ import annotations

class Target: ...

class SomeCallable:
    def __call__(self) -> Target:
        return Target()

class Descriptor:
    def __get__(self, instance, owner) -> SomeCallable:
        return SomeCallable()

class Manager:
    __enter__: Descriptor = Descriptor()

    def __exit__(self, exc_type, exc_value, traceback): ...

with Manager() as f:
    reveal_type(f)  # revealed: Target

Fixing this would involve using try_call_dunder after #16368 is merged, or using static_member + a manual descriptor call, if that is not possible.

@sharkdp sharkdp added bug Something isn't working red-knot Multi-file analysis & type inference labels Feb 25, 2025
@sharkdp
Copy link
Contributor Author

sharkdp commented Feb 25, 2025

The same is probably true for __class_getitem__:

let dunder_class_getitem_method =
value_ty.member(self.db(), "__class_getitem__");

.try_call(self.db(), &CallArguments::positional([value_ty, slice_ty]))

@sharkdp
Copy link
Contributor Author

sharkdp commented Feb 25, 2025

And augmented assignments:

Type::Instance(instance) => {
if let Symbol::Type(class_member, boundness) = instance
.class()
.class_member(self.db(), op.in_place_dunder())
{
let call = class_member.try_call(
self.db(),
&CallArguments::positional([target_type, value_type]),
);

@sharkdp
Copy link
Contributor Author

sharkdp commented Feb 25, 2025

And in/not in tests:

let contains_dunder = right.class().class_member(db, "__contains__");
let compare_result_opt = match contains_dunder {
Symbol::Type(contains_dunder, Boundness::Bound) => {
// If `__contains__` is available, it is used directly for the membership test.
contains_dunder
.try_call(
db,
&CallArguments::positional([Type::Instance(right), Type::Instance(left)]),
)

@sharkdp
Copy link
Contributor Author

sharkdp commented Feb 25, 2025

… and subscript expressions (this is the last one, I promise):

let dunder_class_getitem_method =
value_ty.member(self.db(), "__class_getitem__");
match dunder_class_getitem_method {
Symbol::Unbound => {}
Symbol::Type(ty, boundness) => {
if boundness == Boundness::PossiblyUnbound {
self.context.report_lint(
&CALL_POSSIBLY_UNBOUND_METHOD,
value_node,
format_args!(
"Method `__class_getitem__` of type `{}` is possibly unbound",
value_ty.display(self.db()),
),
);
}
return ty
.try_call(self.db(), &CallArguments::positional([value_ty, slice_ty]))

@sharkdp sharkdp changed the title [red-knot] Context managers should use try_call_dunder infrastructure [red-knot] Use try_call_dunder infrastructure consistently Feb 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working red-knot Multi-file analysis & type inference
Projects
None yet
Development

No branches or pull requests

1 participant