Skip to content

Commit df1fc7a

Browse files
Fix NotImplemented incorrectly treated as callable (#2918)
NotImplementedType stubs inherit Any, so has_base_any made the singleton callable via the implicit-Any call path. Exclude types.NotImplementedType and builtins._NotImplementedType from that branch. Tests: callable/calls regressions; return NotImplemented unchanged. Made-with: Cursor
1 parent 92ec1be commit df1fc7a

File tree

3 files changed

+19
-4
lines changed

3 files changed

+19
-4
lines changed

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+
);

pyrefly/lib/test/calls.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -408,16 +408,15 @@ def get_flow_version(run_id: str | None) -> str | None:
408408

409409
// https://github.com/facebook/pyrefly/issues/2918
410410
testcase!(
411-
bug = "Should error when calling NotImplemented (a constant, not a class)",
412411
test_call_not_implemented_constant,
413412
r#"
414413
# NotImplemented is a singleton constant, not a callable class.
415414
# Using NotImplemented() is always a mistake; they mean NotImplementedError().
416415
def broken():
417-
raise NotImplemented()
416+
raise NotImplemented() # E: Expected a callable
418417
419418
def also_broken():
420-
raise NotImplemented("not yet done")
419+
raise NotImplemented("not yet done") # E: Expected a callable
421420
"#,
422421
);
423422

0 commit comments

Comments
 (0)