Skip to content

Commit 0995a30

Browse files
committed
Refactor newtype code in semenal
1 parent c81477d commit 0995a30

File tree

1 file changed

+60
-52
lines changed

1 file changed

+60
-52
lines changed

mypy/semanal.py

Lines changed: 60 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,25 +1293,21 @@ def store_declared_types(self, lvalue: Node, typ: Type) -> None:
12931293

12941294
def process_newtype_declaration(self, s: AssignmentStmt) -> None:
12951295
"""Check if s declares a NewType; if yes, store it in symbol table."""
1296+
# Extract and check all information from newtype declaration
12961297
call = self.get_newtype_declaration(s)
1297-
if not call:
1298+
if call is None:
12981299
return
12991300
call.analyzed = NewTypeExpr(None).set_line(call.line)
13001301

1301-
lvalue = cast(NameExpr, s.lvalues[0])
1302-
name = lvalue.name
1303-
if not lvalue.is_def:
1304-
if s.type:
1305-
self.fail("Cannot declare the type of a NewType declaration", s)
1306-
else:
1307-
self.fail("Cannot redefine '%s' as a NewType" % name, s)
1302+
name = self.get_newtype_name(s)
1303+
if name is None:
13081304
return
13091305

13101306
old_type = self.check_newtype_args(call, name, s)
13111307
if old_type is None:
13121308
return
13131309

1314-
# Create the corresponding class def if it's subtypeable...
1310+
# Create the corresponding class definition if the aliased type is subtypeable
13151311
if isinstance(old_type, TupleType):
13161312
newtype_class_info = self.build_newtype_typeinfo(name, old_type, old_type.fallback)
13171313
newtype_class_info.tuple_type = old_type
@@ -1322,39 +1318,41 @@ def process_newtype_declaration(self, s: AssignmentStmt) -> None:
13221318
self.fail(message.format(old_type), s)
13231319
return
13241320

1325-
# ...and add it to the symbol table.
1321+
# If so, add it to the symbol table.
13261322
node = self.lookup(name, s)
1327-
node.kind = GDEF # TODO: locally defined newtype
1328-
call.analyzed = NewTypeExpr(newtype_class_info).set_line(call.line)
1323+
# TODO: why does NewType work in local scopes despite always being of kind GDEF?
1324+
node.kind = GDEF
13291325
node.node = newtype_class_info
1326+
call.analyzed = NewTypeExpr(newtype_class_info).set_line(call.line)
13301327

1331-
def build_newtype_typeinfo(self,name: str, old_type: Type, base_type: Instance) -> TypeInfo:
1332-
class_def = ClassDef(name, Block([]))
1333-
class_def.fullname = self.qualified_name(name)
1334-
1335-
symbols = SymbolTable()
1336-
info = TypeInfo(symbols, class_def)
1337-
info.mro = [info] + base_type.type.mro
1338-
info.bases = [base_type]
1339-
info.is_newtype = True
1340-
1341-
# Add __init__ method
1342-
args = [Argument(Var('cls'), NoneTyp(), None, ARG_POS),
1343-
self.make_argument('item', old_type)]
1344-
signature = CallableType(
1345-
arg_types = [cast(Type, None), old_type],
1346-
arg_kinds = [arg.kind for arg in args],
1347-
arg_names = ['self', 'item'],
1348-
ret_type = old_type,
1349-
fallback = self.named_type('__builtins__.function'),
1350-
name = name)
1351-
init_func = FuncDef('__init__', args, Block([]), typ=signature)
1352-
init_func.info = info
1353-
symbols['__init__'] = SymbolTableNode(MDEF, init_func)
1328+
def get_newtype_declaration(self, s: AssignmentStmt) -> Optional[CallExpr]:
1329+
"""Returns the Newtype() call statement if `s` is a newtype declaration
1330+
or None otherwise."""
1331+
# TODO: determine if this and get_typevar_declaration should be refactored
1332+
if len(s.lvalues) != 1 or not isinstance(s.lvalues[0], NameExpr):
1333+
return None
1334+
if not isinstance(s.rvalue, CallExpr):
1335+
return None
1336+
call = s.rvalue
1337+
if not isinstance(call.callee, RefExpr):
1338+
return None
1339+
callee = call.callee
1340+
if callee.fullname != 'typing.NewType':
1341+
return None
1342+
return call
13541343

1355-
return info
1344+
def get_newtype_name(self, s: AssignmentStmt) -> Optional[str]:
1345+
lvalue = cast(NameExpr, s.lvalues[0])
1346+
name = lvalue.name
1347+
if not lvalue.is_def:
1348+
if s.type:
1349+
self.fail("Cannot declare the type of a NewType declaration", s)
1350+
else:
1351+
self.fail("Cannot redefine '%s' as a NewType" % name, s)
1352+
return None
1353+
return name
13561354

1357-
def check_newtype_args(self, call: CallExpr, name: str, context: Context) -> Type:
1355+
def check_newtype_args(self, call: CallExpr, name: str, context: Context) -> Optional[Type]:
13581356
has_failed = False
13591357
args = call.args
13601358
if len(args) != 2:
@@ -1387,21 +1385,31 @@ def check_newtype_args(self, call: CallExpr, name: str, context: Context) -> Typ
13871385

13881386
return None if has_failed else value
13891387

1390-
def get_newtype_declaration(self, s: AssignmentStmt) -> Optional[CallExpr]:
1391-
"""Returns the Newtype() call statement if `s` is a newtype declaration
1392-
or None otherwise."""
1393-
# TODO: determine if this and get_typevar_declaration should be refactored
1394-
if len(s.lvalues) != 1 or not isinstance(s.lvalues[0], NameExpr):
1395-
return None
1396-
if not isinstance(s.rvalue, CallExpr):
1397-
return None
1398-
call = s.rvalue
1399-
if not isinstance(call.callee, RefExpr):
1400-
return None
1401-
callee = call.callee
1402-
if callee.fullname != 'typing.NewType':
1403-
return None
1404-
return call
1388+
def build_newtype_typeinfo(self, name: str, old_type: Type, base_type: Instance) -> TypeInfo:
1389+
class_def = ClassDef(name, Block([]))
1390+
class_def.fullname = self.qualified_name(name)
1391+
1392+
symbols = SymbolTable()
1393+
info = TypeInfo(symbols, class_def)
1394+
info.mro = [info] + base_type.type.mro
1395+
info.bases = [base_type]
1396+
info.is_newtype = True
1397+
1398+
# Add __init__ method
1399+
args = [Argument(Var('cls'), NoneTyp(), None, ARG_POS),
1400+
self.make_argument('item', old_type)]
1401+
signature = CallableType(
1402+
arg_types = [cast(Type, None), old_type],
1403+
arg_kinds = [arg.kind for arg in args],
1404+
arg_names = ['self', 'item'],
1405+
ret_type = old_type,
1406+
fallback = self.named_type('__builtins__.function'),
1407+
name = name)
1408+
init_func = FuncDef('__init__', args, Block([]), typ=signature)
1409+
init_func.info = info
1410+
symbols['__init__'] = SymbolTableNode(MDEF, init_func)
1411+
1412+
return info
14051413

14061414
def process_typevar_declaration(self, s: AssignmentStmt) -> None:
14071415
"""Check if s declares a TypeVar; it yes, store it in symbol table."""

0 commit comments

Comments
 (0)