@@ -1776,6 +1776,172 @@ def _verify_node_has_expected_attr(self, node: nodes.NodeNG) -> None:
1776
1776
with self .assertRaises (AttributeInferenceError ):
1777
1777
inferred .getattr ("bad_attr" )
1778
1778
1779
+ def test_typing_newtype_forward_reference_imported (self ) -> None :
1780
+ all_ast_nodes = builder .extract_node (
1781
+ """
1782
+ from typing import NewType
1783
+
1784
+ A = NewType("A", "decimal.Decimal")
1785
+ B = NewType("B", "decimal_mod_alias.Decimal")
1786
+ C = NewType("C", "Decimal")
1787
+ D = NewType("D", "DecimalAlias")
1788
+
1789
+ import decimal
1790
+ import decimal as decimal_mod_alias
1791
+ from decimal import Decimal
1792
+ from decimal import Decimal as DecimalAlias
1793
+
1794
+ Decimal #@
1795
+
1796
+ a = A(decimal.Decimal(2))
1797
+ a #@
1798
+ b = B(decimal_mod_alias.Decimal(2))
1799
+ b #@
1800
+ c = C(Decimal(2))
1801
+ c #@
1802
+ d = D(DecimalAlias(2))
1803
+ d #@
1804
+ """
1805
+ )
1806
+ assert isinstance (all_ast_nodes , list )
1807
+
1808
+ real_dec , * ast_nodes = all_ast_nodes
1809
+
1810
+ real_quantize = next (real_dec .infer ()).getattr ("quantize" )
1811
+
1812
+ for node in ast_nodes :
1813
+ all_inferred = list (node .infer ())
1814
+ assert len (all_inferred ) == 1
1815
+ inferred = all_inferred [0 ]
1816
+ assert isinstance (inferred , astroid .Instance )
1817
+
1818
+ assert inferred .getattr ("quantize" ) == real_quantize
1819
+
1820
+ def test_typing_newtype_forward_ref_bad_base (self ) -> None :
1821
+ ast_nodes = builder .extract_node (
1822
+ """
1823
+ from typing import NewType
1824
+
1825
+ A = NewType("A", "DoesntExist")
1826
+
1827
+ a = A()
1828
+ a #@
1829
+
1830
+ # Valid name, but not actually imported
1831
+ B = NewType("B", "decimal.Decimal")
1832
+
1833
+ b = B()
1834
+ b #@
1835
+
1836
+ # AST works out, but can't import the module
1837
+ import not_a_real_module
1838
+
1839
+ C = NewType("C", "not_a_real_module.SomeClass")
1840
+ c = C()
1841
+ c #@
1842
+
1843
+ # Real module, fake base class name
1844
+ import email.charset
1845
+
1846
+ D = NewType("D", "email.charset.BadClassRef")
1847
+ d = D()
1848
+ d #@
1849
+
1850
+ # Real module, but aliased differently than used
1851
+ import email.header as header_mod
1852
+
1853
+ E = NewType("E", "email.header.Header")
1854
+ e = E(header_mod.Header())
1855
+ e #@
1856
+ """
1857
+ )
1858
+ assert isinstance (ast_nodes , list )
1859
+
1860
+ for ast_node in ast_nodes :
1861
+ inferred = next (ast_node .infer ())
1862
+
1863
+ with self .assertRaises (astroid .AttributeInferenceError ):
1864
+ inferred .getattr ("value" )
1865
+
1866
+ def test_typing_newtype_forward_ref_nested_module (self ) -> None :
1867
+ ast_nodes = builder .extract_node (
1868
+ """
1869
+ from typing import NewType
1870
+
1871
+ A = NewType("A", "email.charset.Charset")
1872
+ B = NewType("B", "charset.Charset")
1873
+
1874
+ # header is unused in both cases, but verifies that module name is properly checked
1875
+ import email.header, email.charset
1876
+ from email import header, charset
1877
+
1878
+ real = charset.Charset()
1879
+ real #@
1880
+
1881
+ a = A(email.charset.Charset())
1882
+ a #@
1883
+
1884
+ b = B(charset.Charset())
1885
+ """
1886
+ )
1887
+ assert isinstance (ast_nodes , list )
1888
+
1889
+ real , * newtypes = ast_nodes
1890
+
1891
+ real_inferred_all = list (real .infer ())
1892
+ assert len (real_inferred_all ) == 1
1893
+ real_inferred = real_inferred_all [0 ]
1894
+
1895
+ real_method = real_inferred .getattr ("get_body_encoding" )
1896
+
1897
+ for newtype_node in newtypes :
1898
+ newtype_inferred_all = list (newtype_node .infer ())
1899
+ assert len (newtype_inferred_all ) == 1
1900
+ newtype_inferred = newtype_inferred_all [0 ]
1901
+
1902
+ newtype_method = newtype_inferred .getattr ("get_body_encoding" )
1903
+
1904
+ assert real_method == newtype_method
1905
+
1906
+ def test_typing_newtype_forward_ref_nested_class (self ) -> None :
1907
+ ast_nodes = builder .extract_node (
1908
+ """
1909
+ from typing import NewType
1910
+
1911
+ A = NewType("A", "SomeClass.Nested")
1912
+
1913
+ class SomeClass:
1914
+ class Nested:
1915
+ def method(self) -> None:
1916
+ pass
1917
+
1918
+ real = SomeClass.Nested()
1919
+ real #@
1920
+
1921
+ a = A(SomeClass.Nested())
1922
+ a #@
1923
+ """
1924
+ )
1925
+ assert isinstance (ast_nodes , list )
1926
+
1927
+ real , newtype = ast_nodes
1928
+
1929
+ real_all_inferred = list (real .infer ())
1930
+ assert len (real_all_inferred ) == 1
1931
+ real_inferred = real_all_inferred [0 ]
1932
+ real_method = real_inferred .getattr ("method" )
1933
+
1934
+ newtype_all_inferred = list (newtype .infer ())
1935
+ assert len (newtype_all_inferred ) == 1
1936
+ newtype_inferred = newtype_all_inferred [0 ]
1937
+
1938
+ # This could theoretically work, but for now just here to check that
1939
+ # the "forward-declared module" inference doesn't totally break things
1940
+ with self .assertRaises (astroid .AttributeInferenceError ):
1941
+ newtype_method = newtype_inferred .getattr ("method" )
1942
+
1943
+ assert real_method == newtype_method
1944
+
1779
1945
def test_namedtuple_nested_class (self ):
1780
1946
result = builder .extract_node (
1781
1947
"""
0 commit comments