diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeChecker.java b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeChecker.java index e2e1ecb1a1bf2..3be1b36a5f32e 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeChecker.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeChecker.java @@ -113,6 +113,26 @@ public static boolean match(@Nullable PyType expected, } } + // PY-85543: Treat typing.NewType instances as their underlying runtime type for assignability checks. + // NewType is only a static marker; at runtime it's the supertype, so unwrap it early. + if (actual instanceof PyTypingNewType typingNewType) { + if (expected instanceof PyTypingNewType && typingNewType.getAncestorTypes(context.context).contains(expected)) { + return Optional.of(true); + } + + List superTypes = typingNewType.getSuperClassTypes(context.context); + if (!superTypes.isEmpty()) { + actual = superTypes.get(0); + } + else { + actual = new PyClassTypeImpl(typingNewType.getPyClass(), typingNewType.isDefinition()); + } + + if (Objects.equals(expected, actual)) { + return Optional.of(true); + } + } + if (expected instanceof PyClassType) { Optional match = matchObject((PyClassType)expected, actual); if (match.isPresent()) { diff --git a/python/testData/inspections/PyTypeCheckerInspection/NewTypeWithGenerics.py b/python/testData/inspections/PyTypeCheckerInspection/NewTypeWithGenerics.py new file mode 100644 index 0000000000000..aa4e341fcfe98 --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/NewTypeWithGenerics.py @@ -0,0 +1,13 @@ +from typing import NewType, Dict, List + +KeyValue = NewType('KeyValue', Dict[str, int]) + +def f(x: KeyValue): + pass + +kv = KeyValue({'a': 1}) + +def g(x: Dict[str, str]): + pass + +g(kv) diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java index 4499932bf7fc3..9279233b34e61 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java @@ -1240,6 +1240,24 @@ def a_int_b_str(a: int, b: str) -> int: res4 = twice(a_int_b_str, b=1, a="A")"""); } + // PY-85543 + public void testNewTypeAcceptedByIntCallInsideFString() { + doTestByText(""" + from typing import NewType + + MyInt = NewType("MyInt", int) + + def test_(): + number = MyInt(10) + q = f"{int(number)}" + """); + } + + // PY-85543 + public void testNewTypeWithGenerics() { + doTest(); + } + // PY-50403 public void testFunctionNotEnoughArgumentsToMatchWithParamSpec() { doTestByText("""