Skip to content

Commit 73c25c2

Browse files
Reject calling NotImplemented() (fix #2918)
NotImplemented is a singleton; stubs model NotImplementedType with an Any base, which made has_base_any treat it as implicitly callable. Skip that path for types.NotImplementedType and builtins._NotImplementedType. Adds regression test in pyrefly/lib/test/callable.rs. Made-with: Cursor
1 parent ec4e852 commit 73c25c2

2 files changed

Lines changed: 17 additions & 1 deletion

File tree

pyrefly/lib/alt/call.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,9 +366,15 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
366366
// If the class has an unknown base (e.g. inherits from an
367367
// unresolved name), it might have inherited `__call__` from
368368
// that base, so treat it as callable with implicit Any.
369+
//
370+
// `NotImplemented` is a singleton instance of `NotImplementedType`; it must
371+
// never be treated as callable even when stubs use `NotImplementedType(Any)`,
372+
// which would otherwise set `has_base_any` and hit this branch.
369373
None if self
370374
.get_metadata_for_class(cls.class_object())
371-
.has_base_any() =>
375+
.has_base_any()
376+
&& !cls.has_qname("types", "NotImplementedType")
377+
&& !cls.has_qname("builtins", "_NotImplementedType") =>
372378
{
373379
CallTargetLookup::Ok(Box::new(CallTarget::Any(AnyStyle::Implicit)))
374380
}

pyrefly/lib/test/callable.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1449,3 +1449,13 @@ def after_func() -> None: ...
14491449
schedule(1000, after_func)
14501450
"#,
14511451
);
1452+
1453+
// Regression test for https://github.com/facebook/pyrefly/issues/2918
1454+
testcase!(
1455+
test_notimplemented_not_callable,
1456+
r#"
1457+
NotImplemented() # E: Expected a callable
1458+
NotImplemented("not yet done") # E: Expected a callable
1459+
raise NotImplementedError()
1460+
"#,
1461+
);

0 commit comments

Comments
 (0)