@@ -1837,6 +1837,172 @@ def _verify_node_has_expected_attr(self, node: nodes.NodeNG) -> None:
18371837 with self .assertRaises (AttributeInferenceError ):
18381838 inferred .getattr ("bad_attr" )
18391839
1840+ def test_typing_newtype_forward_reference_imported (self ) -> None :
1841+ all_ast_nodes = builder .extract_node (
1842+ """
1843+ from typing import NewType
1844+
1845+ A = NewType("A", "decimal.Decimal")
1846+ B = NewType("B", "decimal_mod_alias.Decimal")
1847+ C = NewType("C", "Decimal")
1848+ D = NewType("D", "DecimalAlias")
1849+
1850+ import decimal
1851+ import decimal as decimal_mod_alias
1852+ from decimal import Decimal
1853+ from decimal import Decimal as DecimalAlias
1854+
1855+ Decimal #@
1856+
1857+ a = A(decimal.Decimal(2))
1858+ a #@
1859+ b = B(decimal_mod_alias.Decimal(2))
1860+ b #@
1861+ c = C(Decimal(2))
1862+ c #@
1863+ d = D(DecimalAlias(2))
1864+ d #@
1865+ """
1866+ )
1867+ assert isinstance (all_ast_nodes , list )
1868+
1869+ real_dec , * ast_nodes = all_ast_nodes
1870+
1871+ real_quantize = next (real_dec .infer ()).getattr ("quantize" )
1872+
1873+ for node in ast_nodes :
1874+ all_inferred = list (node .infer ())
1875+ assert len (all_inferred ) == 1
1876+ inferred = all_inferred [0 ]
1877+ assert isinstance (inferred , astroid .Instance )
1878+
1879+ assert inferred .getattr ("quantize" ) == real_quantize
1880+
1881+ def test_typing_newtype_forward_ref_bad_base (self ) -> None :
1882+ ast_nodes = builder .extract_node (
1883+ """
1884+ from typing import NewType
1885+
1886+ A = NewType("A", "DoesntExist")
1887+
1888+ a = A()
1889+ a #@
1890+
1891+ # Valid name, but not actually imported
1892+ B = NewType("B", "decimal.Decimal")
1893+
1894+ b = B()
1895+ b #@
1896+
1897+ # AST works out, but can't import the module
1898+ import not_a_real_module
1899+
1900+ C = NewType("C", "not_a_real_module.SomeClass")
1901+ c = C()
1902+ c #@
1903+
1904+ # Real module, fake base class name
1905+ import email.charset
1906+
1907+ D = NewType("D", "email.charset.BadClassRef")
1908+ d = D()
1909+ d #@
1910+
1911+ # Real module, but aliased differently than used
1912+ import email.header as header_mod
1913+
1914+ E = NewType("E", "email.header.Header")
1915+ e = E(header_mod.Header())
1916+ e #@
1917+ """
1918+ )
1919+ assert isinstance (ast_nodes , list )
1920+
1921+ for ast_node in ast_nodes :
1922+ inferred = next (ast_node .infer ())
1923+
1924+ with self .assertRaises (astroid .AttributeInferenceError ):
1925+ inferred .getattr ("value" )
1926+
1927+ def test_typing_newtype_forward_ref_nested_module (self ) -> None :
1928+ ast_nodes = builder .extract_node (
1929+ """
1930+ from typing import NewType
1931+
1932+ A = NewType("A", "email.charset.Charset")
1933+ B = NewType("B", "charset.Charset")
1934+
1935+ # header is unused in both cases, but verifies that module name is properly checked
1936+ import email.header, email.charset
1937+ from email import header, charset
1938+
1939+ real = charset.Charset()
1940+ real #@
1941+
1942+ a = A(email.charset.Charset())
1943+ a #@
1944+
1945+ b = B(charset.Charset())
1946+ """
1947+ )
1948+ assert isinstance (ast_nodes , list )
1949+
1950+ real , * newtypes = ast_nodes
1951+
1952+ real_inferred_all = list (real .infer ())
1953+ assert len (real_inferred_all ) == 1
1954+ real_inferred = real_inferred_all [0 ]
1955+
1956+ real_method = real_inferred .getattr ("get_body_encoding" )
1957+
1958+ for newtype_node in newtypes :
1959+ newtype_inferred_all = list (newtype_node .infer ())
1960+ assert len (newtype_inferred_all ) == 1
1961+ newtype_inferred = newtype_inferred_all [0 ]
1962+
1963+ newtype_method = newtype_inferred .getattr ("get_body_encoding" )
1964+
1965+ assert real_method == newtype_method
1966+
1967+ def test_typing_newtype_forward_ref_nested_class (self ) -> None :
1968+ ast_nodes = builder .extract_node (
1969+ """
1970+ from typing import NewType
1971+
1972+ A = NewType("A", "SomeClass.Nested")
1973+
1974+ class SomeClass:
1975+ class Nested:
1976+ def method(self) -> None:
1977+ pass
1978+
1979+ real = SomeClass.Nested()
1980+ real #@
1981+
1982+ a = A(SomeClass.Nested())
1983+ a #@
1984+ """
1985+ )
1986+ assert isinstance (ast_nodes , list )
1987+
1988+ real , newtype = ast_nodes
1989+
1990+ real_all_inferred = list (real .infer ())
1991+ assert len (real_all_inferred ) == 1
1992+ real_inferred = real_all_inferred [0 ]
1993+ real_method = real_inferred .getattr ("method" )
1994+
1995+ newtype_all_inferred = list (newtype .infer ())
1996+ assert len (newtype_all_inferred ) == 1
1997+ newtype_inferred = newtype_all_inferred [0 ]
1998+
1999+ # This could theoretically work, but for now just here to check that
2000+ # the "forward-declared module" inference doesn't totally break things
2001+ with self .assertRaises (astroid .AttributeInferenceError ):
2002+ newtype_method = newtype_inferred .getattr ("method" )
2003+
2004+ assert real_method == newtype_method
2005+
18402006 def test_namedtuple_nested_class (self ):
18412007 result = builder .extract_node (
18422008 """
0 commit comments