Skip to content

Commit ab588fb

Browse files
committed
Refactor newtype code in semenal
1 parent ae03faf commit ab588fb

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
@@ -1283,25 +1283,21 @@ def store_declared_types(self, lvalue: Node, typ: Type) -> None:
12831283

12841284
def process_newtype_declaration(self, s: AssignmentStmt) -> None:
12851285
"""Check if s declares a NewType; if yes, store it in symbol table."""
1286+
# Extract and check all information from newtype declaration
12861287
call = self.get_newtype_declaration(s)
1287-
if not call:
1288+
if call is None:
12881289
return
12891290
call.analyzed = NewTypeExpr(None).set_line(call.line)
12901291

1291-
lvalue = cast(NameExpr, s.lvalues[0])
1292-
name = lvalue.name
1293-
if not lvalue.is_def:
1294-
if s.type:
1295-
self.fail("Cannot declare the type of a NewType declaration", s)
1296-
else:
1297-
self.fail("Cannot redefine '%s' as a NewType" % name, s)
1292+
name = self.get_newtype_name(s)
1293+
if name is None:
12981294
return
12991295

13001296
old_type = self.check_newtype_args(call, name, s)
13011297
if old_type is None:
13021298
return
13031299

1304-
# Create the corresponding class def if it's subtypeable...
1300+
# Create the corresponding class definition if the aliased type is subtypeable
13051301
if isinstance(old_type, TupleType):
13061302
newtype_class_info = self.build_newtype_typeinfo(name, old_type, old_type.fallback)
13071303
newtype_class_info.tuple_type = old_type
@@ -1312,39 +1308,41 @@ def process_newtype_declaration(self, s: AssignmentStmt) -> None:
13121308
self.fail(message.format(old_type), s)
13131309
return
13141310

1315-
# ...and add it to the symbol table.
1311+
# If so, add it to the symbol table.
13161312
node = self.lookup(name, s)
1317-
node.kind = GDEF # TODO: locally defined newtype
1318-
call.analyzed = NewTypeExpr(newtype_class_info).set_line(call.line)
1313+
# TODO: why does NewType work in local scopes despite always being of kind GDEF?
1314+
node.kind = GDEF
13191315
node.node = newtype_class_info
1316+
call.analyzed = NewTypeExpr(newtype_class_info).set_line(call.line)
13201317

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

1345-
return info
1334+
def get_newtype_name(self, s: AssignmentStmt) -> Optional[str]:
1335+
lvalue = cast(NameExpr, s.lvalues[0])
1336+
name = lvalue.name
1337+
if not lvalue.is_def:
1338+
if s.type:
1339+
self.fail("Cannot declare the type of a NewType declaration", s)
1340+
else:
1341+
self.fail("Cannot redefine '%s' as a NewType" % name, s)
1342+
return None
1343+
return name
13461344

1347-
def check_newtype_args(self, call: CallExpr, name: str, context: Context) -> Type:
1345+
def check_newtype_args(self, call: CallExpr, name: str, context: Context) -> Optional[Type]:
13481346
has_failed = False
13491347
args = call.args
13501348
if len(args) != 2:
@@ -1377,21 +1375,31 @@ def check_newtype_args(self, call: CallExpr, name: str, context: Context) -> Typ
13771375

13781376
return None if has_failed else value
13791377

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

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

0 commit comments

Comments
 (0)