def parse_typeddict_args( self, call: CallExpr ) -> Optional[Tuple[str, List[str], List[Type], bool, bool]]: """Parse typed dict call expression. Return names, types, totality, was there an error during parsing. If some type is not ready, return None. """ # TODO: Share code with check_argument_count in checkexpr.py? args = call.args if len(args) < 2: return self.fail_typeddict_arg("Too few arguments for TypedDict()", call) if len(args) > 3: return self.fail_typeddict_arg( "Too many arguments for TypedDict()", call) # TODO: Support keyword arguments if call.arg_kinds not in ([ARG_POS, ARG_POS], [ARG_POS, ARG_POS, ARG_NAMED]): return self.fail_typeddict_arg( "Unexpected arguments to TypedDict()", call) if len(args) == 3 and call.arg_names[2] != 'total': return self.fail_typeddict_arg( 'Unexpected keyword argument "{}" for "TypedDict"'.format( call.arg_names[2]), call) if not isinstance(args[0], (StrExpr, BytesExpr, UnicodeExpr)): return self.fail_typeddict_arg( "TypedDict() expects a string literal as the first argument", call) if not isinstance(args[1], DictExpr): return self.fail_typeddict_arg( "TypedDict() expects a dictionary literal as the second argument", call) total = True # type: Optional[bool] if len(args) == 3: total = self.api.parse_bool(call.args[2]) if total is None: return self.fail_typeddict_arg( 'TypedDict() "total" argument must be True or False', call) dictexpr = args[1] res = self.parse_typeddict_fields_with_types(dictexpr.items, call) if res is None: # One of the types is not ready, defer. return None items, types, ok = res for t in types: check_for_explicit_any(t, self.options, self.api.is_typeshed_stub_file, self.msg, context=call) if self.options.disallow_any_unimported: for t in types: if has_any_from_unimported_type(t): self.msg.unimported_type_becomes_any( "Type of a TypedDict key", t, dictexpr) assert total is not None return args[0].value, items, types, total, ok
def process_newtype_declaration(self, s: AssignmentStmt) -> None: """Check if s declares a NewType; if yes, store it in symbol table.""" # Extract and check all information from newtype declaration name, call = self.analyze_newtype_declaration(s) if name is None or call is None: return old_type = self.check_newtype_args(name, call, s) call.analyzed = NewTypeExpr(name, old_type, line=call.line, column=call.column) if old_type is None: return # Create the corresponding class definition if the aliased type is subtypeable if isinstance(old_type, TupleType): newtype_class_info = self.build_newtype_typeinfo( name, old_type, old_type.partial_fallback) newtype_class_info.tuple_type = old_type elif isinstance(old_type, Instance): if old_type.type.is_protocol: self.fail("NewType cannot be used with protocol classes", s) newtype_class_info = self.build_newtype_typeinfo( name, old_type, old_type) else: message = "Argument 2 to NewType(...) must be subclassable (got {})" self.fail(message.format(self.msg.format(old_type)), s) return check_for_explicit_any(old_type, self.options, self.api.is_typeshed_stub_file, self.msg, context=s) if self.options.disallow_any_unimported and has_any_from_unimported_type( old_type): self.msg.unimported_type_becomes_any("Argument 2 to NewType(...)", old_type, s) # If so, add it to the symbol table. node = self.api.lookup(name, s) if node is None: self.fail("Could not find {} in current namespace".format(name), s) return # TODO: why does NewType work in local scopes despite always being of kind GDEF? node.kind = GDEF call.analyzed.info = node.node = newtype_class_info
def process_newtype_declaration(self, s: AssignmentStmt) -> None: """Check if s declares a NewType; if yes, store it in symbol table.""" # Extract and check all information from newtype declaration name, call = self.analyze_newtype_declaration(s) if name is None or call is None: return old_type = self.check_newtype_args(name, call, s) call.analyzed = NewTypeExpr(name, old_type, line=call.line) if old_type is None: return # Create the corresponding class definition if the aliased type is subtypeable if isinstance(old_type, TupleType): newtype_class_info = self.build_newtype_typeinfo(name, old_type, old_type.partial_fallback) newtype_class_info.tuple_type = old_type elif isinstance(old_type, Instance): if old_type.type.is_protocol: self.fail("NewType cannot be used with protocol classes", s) newtype_class_info = self.build_newtype_typeinfo(name, old_type, old_type) else: message = "Argument 2 to NewType(...) must be subclassable (got {})" self.fail(message.format(self.msg.format(old_type)), s) return check_for_explicit_any(old_type, self.options, self.api.is_typeshed_stub_file, self.msg, context=s) if self.options.disallow_any_unimported and has_any_from_unimported_type(old_type): self.msg.unimported_type_becomes_any("Argument 2 to NewType(...)", old_type, s) # If so, add it to the symbol table. node = self.api.lookup(name, s) if node is None: self.fail("Could not find {} in current namespace".format(name), s) return # TODO: why does NewType work in local scopes despite always being of kind GDEF? node.kind = GDEF call.analyzed.info = node.node = newtype_class_info
def parse_typeddict_args(self, call: CallExpr) -> Tuple[List[str], List[Type], bool, bool]: # TODO: Share code with check_argument_count in checkexpr.py? args = call.args if len(args) < 2: return self.fail_typeddict_arg("Too few arguments for TypedDict()", call) if len(args) > 3: return self.fail_typeddict_arg("Too many arguments for TypedDict()", call) # TODO: Support keyword arguments if call.arg_kinds not in ([ARG_POS, ARG_POS], [ARG_POS, ARG_POS, ARG_NAMED]): return self.fail_typeddict_arg("Unexpected arguments to TypedDict()", call) if len(args) == 3 and call.arg_names[2] != 'total': return self.fail_typeddict_arg( 'Unexpected keyword argument "{}" for "TypedDict"'.format(call.arg_names[2]), call) if not isinstance(args[0], (StrExpr, BytesExpr, UnicodeExpr)): return self.fail_typeddict_arg( "TypedDict() expects a string literal as the first argument", call) if not isinstance(args[1], DictExpr): return self.fail_typeddict_arg( "TypedDict() expects a dictionary literal as the second argument", call) total = True # type: Optional[bool] if len(args) == 3: total = self.api.parse_bool(call.args[2]) if total is None: return self.fail_typeddict_arg( 'TypedDict() "total" argument must be True or False', call) dictexpr = args[1] items, types, ok = self.parse_typeddict_fields_with_types(dictexpr.items, call) for t in types: check_for_explicit_any(t, self.options, self.api.is_typeshed_stub_file, self.msg, context=call) if self.options.disallow_any_unimported: for t in types: if has_any_from_unimported_type(t): self.msg.unimported_type_becomes_any("Type of a TypedDict key", t, dictexpr) assert total is not None return items, types, total, ok
def process_newtype_declaration(self, s: AssignmentStmt) -> bool: """Check if s declares a NewType; if yes, store it in symbol table. Return True if it's a NewType declaration. The current target may be deferred as a side effect if the base type is not ready, even if the return value is True. The logic in this function mostly copies the logic for visit_class_def() with a single (non-Generic) base. """ name, call = self.analyze_newtype_declaration(s) if name is None or call is None: return False # OK, now we know this is a NewType. But the base type may be not ready yet, # add placeholder as we do for ClassDef. fullname = self.api.qualified_name(name) if (not call.analyzed or isinstance(call.analyzed, NewTypeExpr) and not call.analyzed.info): # Start from labeling this as a future class, as we do for normal ClassDefs. placeholder = PlaceholderNode(fullname, s, s.line, becomes_typeinfo=True) self.api.add_symbol(name, placeholder, s, can_defer=False) old_type, should_defer = self.check_newtype_args(name, call, s) old_type = get_proper_type(old_type) if not call.analyzed: call.analyzed = NewTypeExpr(name, old_type, line=call.line, column=call.column) if old_type is None: if should_defer: # Base type is not ready. self.api.defer() return True # Create the corresponding class definition if the aliased type is subtypeable if isinstance(old_type, TupleType): newtype_class_info = self.build_newtype_typeinfo( name, old_type, old_type.partial_fallback) newtype_class_info.tuple_type = old_type elif isinstance(old_type, Instance): if old_type.type.is_protocol: self.fail("NewType cannot be used with protocol classes", s) newtype_class_info = self.build_newtype_typeinfo( name, old_type, old_type) else: if old_type is not None: message = "Argument 2 to NewType(...) must be subclassable (got {})" self.fail(message.format(format_type(old_type)), s, code=codes.VALID_NEWTYPE) # Otherwise the error was already reported. old_type = AnyType(TypeOfAny.from_error) object_type = self.api.named_type('__builtins__.object') newtype_class_info = self.build_newtype_typeinfo( name, old_type, object_type) newtype_class_info.fallback_to_any = True check_for_explicit_any(old_type, self.options, self.api.is_typeshed_stub_file, self.msg, context=s) if self.options.disallow_any_unimported and has_any_from_unimported_type( old_type): self.msg.unimported_type_becomes_any("Argument 2 to NewType(...)", old_type, s) # If so, add it to the symbol table. assert isinstance(call.analyzed, NewTypeExpr) # As we do for normal classes, create the TypeInfo only once, then just # update base classes on next iterations (to get rid of placeholders there). if not call.analyzed.info: call.analyzed.info = newtype_class_info else: call.analyzed.info.bases = newtype_class_info.bases self.api.add_symbol(name, call.analyzed.info, s) newtype_class_info.line = s.line return True