@@ -1640,6 +1640,26 @@ def test_typing_types(self) -> None:
1640
1640
inferred = next (node .infer ())
1641
1641
self .assertIsInstance (inferred , nodes .ClassDef , node .as_string ())
1642
1642
1643
+ def test_typing_typevar_bad_args (self ) -> None :
1644
+ ast_nodes = builder .extract_node (
1645
+ """
1646
+ from typing import TypeVar
1647
+
1648
+ T = TypeVar()
1649
+ T #@
1650
+
1651
+ U = TypeVar(f"U")
1652
+ U #@
1653
+ """
1654
+ )
1655
+ assert isinstance (ast_nodes , list )
1656
+
1657
+ no_args_node = ast_nodes [0 ]
1658
+ assert list (no_args_node .infer ()) == [util .Uninferable ]
1659
+
1660
+ fstr_node = ast_nodes [1 ]
1661
+ assert list (fstr_node .infer ()) == [util .Uninferable ]
1662
+
1643
1663
def test_typing_type_without_tip (self ):
1644
1664
"""Regression test for https://github.com/PyCQA/pylint/issues/5770"""
1645
1665
node = builder .extract_node (
@@ -1651,7 +1671,171 @@ def make_new_type(t):
1651
1671
"""
1652
1672
)
1653
1673
with self .assertRaises (UseInferenceDefault ):
1654
- astroid .brain .brain_typing .infer_typing_typevar_or_newtype (node .value )
1674
+ astroid .brain .brain_typing .infer_typing_newtype (node .value )
1675
+
1676
+ def test_typing_newtype_attrs (self ) -> None :
1677
+ ast_nodes = builder .extract_node (
1678
+ """
1679
+ from typing import NewType
1680
+ import decimal
1681
+ from decimal import Decimal
1682
+
1683
+ NewType("Foo", str) #@
1684
+ NewType("Bar", "int") #@
1685
+ NewType("Baz", Decimal) #@
1686
+ NewType("Qux", decimal.Decimal) #@
1687
+ """
1688
+ )
1689
+ assert isinstance (ast_nodes , list )
1690
+
1691
+ # Base type given by reference
1692
+ foo_node = ast_nodes [0 ]
1693
+
1694
+ # Should be unambiguous
1695
+ foo_inferred_all = list (foo_node .infer ())
1696
+ assert len (foo_inferred_all ) == 1
1697
+
1698
+ foo_inferred = foo_inferred_all [0 ]
1699
+ assert isinstance (foo_inferred , astroid .ClassDef )
1700
+
1701
+ # Check base type method is inferred by accessing one of its methods
1702
+ foo_base_class_method = foo_inferred .getattr ("endswith" )[0 ]
1703
+ assert isinstance (foo_base_class_method , astroid .FunctionDef )
1704
+ assert foo_base_class_method .qname () == "builtins.str.endswith"
1705
+
1706
+ # Base type given by string (i.e. "int")
1707
+ bar_node = ast_nodes [1 ]
1708
+ bar_inferred_all = list (bar_node .infer ())
1709
+ assert len (bar_inferred_all ) == 1
1710
+ bar_inferred = bar_inferred_all [0 ]
1711
+ assert isinstance (bar_inferred , astroid .ClassDef )
1712
+
1713
+ bar_base_class_method = bar_inferred .getattr ("bit_length" )[0 ]
1714
+ assert isinstance (bar_base_class_method , astroid .FunctionDef )
1715
+ assert bar_base_class_method .qname () == "builtins.int.bit_length"
1716
+
1717
+ # Decimal may be reexported from an implementation-defined module. For
1718
+ # example, in CPython 3.10 this is _decimal, but in PyPy 7.3 it's
1719
+ # _pydecimal. So the expected qname needs to be grabbed dynamically.
1720
+ decimal_quant_node = builder .extract_node (
1721
+ """
1722
+ from decimal import Decimal
1723
+ Decimal.quantize #@
1724
+ """
1725
+ )
1726
+ assert isinstance (decimal_quant_node , nodes .NodeNG )
1727
+
1728
+ # Just grab the first result, since infer() may return values for both
1729
+ # _decimal and _pydecimal
1730
+ decimal_quant_qname = next (decimal_quant_node .infer ()).qname ()
1731
+
1732
+ # Base type is from an "import from"
1733
+ baz_node = ast_nodes [2 ]
1734
+ baz_inferred_all = list (baz_node .infer ())
1735
+ assert len (baz_inferred_all ) == 1
1736
+ baz_inferred = baz_inferred_all [0 ]
1737
+ assert isinstance (baz_inferred , astroid .ClassDef )
1738
+
1739
+ baz_base_class_method = baz_inferred .getattr ("quantize" )[0 ]
1740
+ assert isinstance (baz_base_class_method , astroid .FunctionDef )
1741
+ assert decimal_quant_qname == baz_base_class_method .qname ()
1742
+
1743
+ # Base type is from an import
1744
+ qux_node = ast_nodes [3 ]
1745
+ qux_inferred_all = list (qux_node .infer ())
1746
+ qux_inferred = qux_inferred_all [0 ]
1747
+ assert isinstance (qux_inferred , astroid .ClassDef )
1748
+
1749
+ qux_base_class_method = qux_inferred .getattr ("quantize" )[0 ]
1750
+ assert isinstance (qux_base_class_method , astroid .FunctionDef )
1751
+ assert decimal_quant_qname == qux_base_class_method .qname ()
1752
+
1753
+ def test_typing_newtype_bad_args (self ) -> None :
1754
+ ast_nodes = builder .extract_node (
1755
+ """
1756
+ from typing import NewType
1757
+
1758
+ NoArgs = NewType()
1759
+ NoArgs #@
1760
+
1761
+ OneArg = NewType("OneArg")
1762
+ OneArg #@
1763
+
1764
+ ThreeArgs = NewType("ThreeArgs", int, str)
1765
+ ThreeArgs #@
1766
+
1767
+ DynamicArg = NewType(f"DynamicArg", int)
1768
+ DynamicArg #@
1769
+
1770
+ DynamicBase = NewType("DynamicBase", f"int")
1771
+ DynamicBase #@
1772
+ """
1773
+ )
1774
+ assert isinstance (ast_nodes , list )
1775
+
1776
+ node : nodes .NodeNG
1777
+ for node in ast_nodes :
1778
+ assert list (node .infer ()) == [util .Uninferable ]
1779
+
1780
+ def test_typing_newtype_user_defined (self ) -> None :
1781
+ ast_nodes = builder .extract_node (
1782
+ """
1783
+ from typing import NewType
1784
+
1785
+ class A:
1786
+ def __init__(self, value: int):
1787
+ self.value = value
1788
+
1789
+ a = A(5)
1790
+ a #@
1791
+
1792
+ B = NewType("B", A)
1793
+ b = B(5)
1794
+ b #@
1795
+ """
1796
+ )
1797
+ assert isinstance (ast_nodes , list )
1798
+
1799
+ for node in ast_nodes :
1800
+ self ._verify_node_has_expected_attr (node )
1801
+
1802
+ def test_typing_newtype_forward_reference (self ) -> None :
1803
+ # Similar to the test above, but using a forward reference for "A"
1804
+ ast_nodes = builder .extract_node (
1805
+ """
1806
+ from typing import NewType
1807
+
1808
+ B = NewType("B", "A")
1809
+
1810
+ class A:
1811
+ def __init__(self, value: int):
1812
+ self.value = value
1813
+
1814
+ a = A(5)
1815
+ a #@
1816
+
1817
+ b = B(5)
1818
+ b #@
1819
+ """
1820
+ )
1821
+ assert isinstance (ast_nodes , list )
1822
+
1823
+ for node in ast_nodes :
1824
+ self ._verify_node_has_expected_attr (node )
1825
+
1826
+ def _verify_node_has_expected_attr (self , node : nodes .NodeNG ) -> None :
1827
+ inferred_all = list (node .infer ())
1828
+ assert len (inferred_all ) == 1
1829
+ inferred = inferred_all [0 ]
1830
+ assert isinstance (inferred , astroid .Instance )
1831
+
1832
+ # Should be able to infer that the "value" attr is present on both types
1833
+ val = inferred .getattr ("value" )[0 ]
1834
+ assert isinstance (val , astroid .AssignAttr )
1835
+
1836
+ # Sanity check: nonexistent attr is not inferred
1837
+ with self .assertRaises (AttributeInferenceError ):
1838
+ inferred .getattr ("bad_attr" )
1655
1839
1656
1840
def test_namedtuple_nested_class (self ):
1657
1841
result = builder .extract_node (
0 commit comments