Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit929377a

Browse files
authored
Refactor/unify access to static attributes (#19254)
Fixes#3832Fixes#5723Fixes#17174Improves#7217This is a sixth "major" PR toward#7724. Previously access to"static" attributes (like type aliases, class objects) was duplicated infour places:* In `analyze_ref_expr()`* In `determine_type_of_member()` (for modules as subtypes of protocols)* In instance attribute access logic* In class attribute logicMost of these were somewhat incomplete and/or inconsistent, this PRunifies all four (there is still tiny duplication because I decided tolimit the number of deferrals, i.e. preserve the existing logic in thisrespect). Some notable things that are not pure refactoring:* Previously we disabled access to type variables as class attributes.This was inconsistent with plain references and instance attributes thatjust return `Instance("typing.TypeVar")`.* Instance access plugins were only applied on `TypeInfo`s and`TypeAlias`es, now they are applied always.* Previously arguments kinds were sometimes not correct for TypedDictclass objects with non-required keys.* I tweaked `TypeOfAny` in couple places to be more logical.
1 parentac511d6 commit929377a

15 files changed

+141
-138
lines changed

‎mypy/checker.py‎

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@
117117
TypeAlias,
118118
TypeAliasStmt,
119119
TypeInfo,
120-
TypeVarExpr,
121120
UnaryExpr,
122121
Var,
123122
WhileStmt,
@@ -2858,29 +2857,6 @@ def check_multiple_inheritance(self, typ: TypeInfo) -> None:
28582857
ifnameinbase2.namesandbase2notinbase.mro:
28592858
self.check_compatibility(name,base,base2,typ)
28602859

2861-
defdetermine_type_of_member(self,sym:SymbolTableNode)->Type|None:
2862-
# TODO: this duplicates both checkmember.py and analyze_ref_expr(), delete.
2863-
ifsym.typeisnotNone:
2864-
returnsym.type
2865-
ifisinstance(sym.node,SYMBOL_FUNCBASE_TYPES):
2866-
returnself.function_type(sym.node)
2867-
ifisinstance(sym.node,TypeInfo):
2868-
ifsym.node.typeddict_type:
2869-
# We special-case TypedDict, because they don't define any constructor.
2870-
returnself.expr_checker.typeddict_callable(sym.node)
2871-
else:
2872-
returntype_object_type(sym.node,self.named_type)
2873-
ifisinstance(sym.node,TypeVarExpr):
2874-
# Use of TypeVars is rejected in an expression/runtime context, so
2875-
# we don't need to check supertype compatibility for them.
2876-
returnAnyType(TypeOfAny.special_form)
2877-
ifisinstance(sym.node,TypeAlias):
2878-
withself.msg.filter_errors():
2879-
# Suppress any errors, they will be given when analyzing the corresponding node.
2880-
# Here we may have incorrect options and location context.
2881-
returnself.expr_checker.alias_type_in_runtime_context(sym.node,ctx=sym.node)
2882-
returnNone
2883-
28842860
defcheck_compatibility(
28852861
self,name:str,base1:TypeInfo,base2:TypeInfo,ctx:TypeInfo
28862862
)->None:

‎mypy/checker_shared.py‎

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
MypyFile,
2222
Node,
2323
RefExpr,
24-
TypeAlias,
24+
SymbolNode,
2525
TypeInfo,
2626
Var,
2727
)
@@ -64,10 +64,6 @@ def accept(
6464
defanalyze_ref_expr(self,e:RefExpr,lvalue:bool=False)->Type:
6565
raiseNotImplementedError
6666

67-
@abstractmethod
68-
defmodule_type(self,node:MypyFile)->Instance:
69-
raiseNotImplementedError
70-
7167
@abstractmethod
7268
defcheck_call(
7369
self,
@@ -112,24 +108,26 @@ def check_method_call_by_name(
112108
)->tuple[Type,Type]:
113109
raiseNotImplementedError
114110

115-
@abstractmethod
116-
defalias_type_in_runtime_context(
117-
self,alias:TypeAlias,*,ctx:Context,alias_definition:bool=False
118-
)->Type:
119-
raiseNotImplementedError
120-
121111
@abstractmethod
122112
defvisit_typeddict_index_expr(
123113
self,td_type:TypedDictType,index:Expression,setitem:bool=False
124114
)->tuple[Type,set[str]]:
125115
raiseNotImplementedError
126116

127117
@abstractmethod
128-
deftypeddict_callable(self,info:TypeInfo)->CallableType:
118+
definfer_literal_expr_type(self,value:LiteralValue,fallback_name:str)->Type:
129119
raiseNotImplementedError
130120

131121
@abstractmethod
132-
definfer_literal_expr_type(self,value:LiteralValue,fallback_name:str)->Type:
122+
defanalyze_static_reference(
123+
self,
124+
node:SymbolNode,
125+
ctx:Context,
126+
is_lvalue:bool,
127+
*,
128+
include_modules:bool=True,
129+
suppress_errors:bool=False,
130+
)->Type:
133131
raiseNotImplementedError
134132

135133

‎mypy/checkexpr.py‎

Lines changed: 69 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
importtime
88
fromcollectionsimportdefaultdict
99
fromcollections.abcimportIterable,Iterator,Sequence
10-
fromcontextlibimportcontextmanager
10+
fromcontextlibimportcontextmanager,nullcontext
1111
fromtypingimportCallable,ClassVar,Final,Optional,cast,overload
1212
fromtyping_extensionsimportTypeAliasas_TypeAlias,assert_never
1313

@@ -94,6 +94,7 @@
9494
TypedDictExpr,
9595
TypeInfo,
9696
TypeVarExpr,
97+
TypeVarLikeExpr,
9798
TypeVarTupleExpr,
9899
UnaryExpr,
99100
Var,
@@ -173,6 +174,7 @@
173174
TypeOfAny,
174175
TypeType,
175176
TypeVarId,
177+
TypeVarLikeType,
176178
TypeVarTupleType,
177179
TypeVarType,
178180
UnboundType,
@@ -377,26 +379,24 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type:
377379
result=self.analyze_var_ref(node,e)
378380
ifisinstance(result,PartialType):
379381
result=self.chk.handle_partial_var_type(result,lvalue,node,e)
380-
elifisinstance(node,FuncDef):
381-
# Reference to a global function.
382-
result=function_type(node,self.named_type("builtins.function"))
382+
elifisinstance(node,Decorator):
383+
result=self.analyze_var_ref(node.var,e)
383384
elifisinstance(node,OverloadedFuncDef):
384385
ifnode.typeisNone:
385386
ifself.chk.in_checked_function()andnode.items:
386387
self.chk.handle_cannot_determine_type(node.name,e)
387388
result=AnyType(TypeOfAny.from_error)
388389
else:
389390
result=node.type
390-
elifisinstance(node,TypeInfo):
391-
# Reference to a type object.
392-
ifnode.typeddict_type:
393-
# We special-case TypedDict, because they don't define any constructor.
394-
result=self.typeddict_callable(node)
395-
elifnode.fullname=="types.NoneType":
396-
# We special case NoneType, because its stub definition is not related to None.
397-
result=TypeType(NoneType())
398-
else:
399-
result=type_object_type(node,self.named_type)
391+
elifisinstance(node, (FuncDef,TypeInfo,TypeAlias,MypyFile,TypeVarLikeExpr)):
392+
result=self.analyze_static_reference(node,e,e.is_alias_rvalueorlvalue)
393+
else:
394+
ifisinstance(node,PlaceholderNode):
395+
assertFalse,f"PlaceholderNode{node.fullname!r} leaked to checker"
396+
# Unknown reference; use any type implicitly to avoid
397+
# generating extra type errors.
398+
result=AnyType(TypeOfAny.from_error)
399+
ifisinstance(node,TypeInfo):
400400
ifisinstance(result,CallableType)andisinstance(# type: ignore[misc]
401401
result.ret_type,Instance
402402
):
@@ -408,30 +408,56 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type:
408408
# This is the type in a type[] expression, so substitute type
409409
# variables with Any.
410410
result=erasetype.erase_typevars(result)
411-
elifisinstance(node,MypyFile):
412-
# Reference to a module object.
413-
result=self.module_type(node)
414-
elifisinstance(node,Decorator):
415-
result=self.analyze_var_ref(node.var,e)
411+
assertresultisnotNone
412+
returnresult
413+
414+
defanalyze_static_reference(
415+
self,
416+
node:SymbolNode,
417+
ctx:Context,
418+
is_lvalue:bool,
419+
*,
420+
include_modules:bool=True,
421+
suppress_errors:bool=False,
422+
)->Type:
423+
"""
424+
This is the version of analyze_ref_expr() that doesn't do any deferrals.
425+
426+
This function can be used by member access to "static" attributes. For example,
427+
when accessing module attributes in protocol checks, or accessing attributes of
428+
special kinds (like TypeAlias, TypeInfo, etc.) on an instance or class object.
429+
# TODO: merge with analyze_ref_expr() when we are confident about performance.
430+
"""
431+
ifisinstance(node, (Var,Decorator,OverloadedFuncDef)):
432+
returnnode.typeorAnyType(TypeOfAny.special_form)
433+
elifisinstance(node,FuncDef):
434+
returnfunction_type(node,self.named_type("builtins.function"))
435+
elifisinstance(node,TypeInfo):
436+
# Reference to a type object.
437+
ifnode.typeddict_type:
438+
# We special-case TypedDict, because they don't define any constructor.
439+
returnself.typeddict_callable(node)
440+
elifnode.fullname=="types.NoneType":
441+
# We special case NoneType, because its stub definition is not related to None.
442+
returnTypeType(NoneType())
443+
else:
444+
returntype_object_type(node,self.named_type)
416445
elifisinstance(node,TypeAlias):
417446
# Something that refers to a type alias appears in runtime context.
418447
# Note that we suppress bogus errors for alias redefinitions,
419448
# they are already reported in semanal.py.
420-
result=self.alias_type_in_runtime_context(
421-
node,ctx=e,alias_definition=e.is_alias_rvalueorlvalue
422-
)
449+
withself.msg.filter_errors()ifsuppress_errorselsenullcontext():
450+
returnself.alias_type_in_runtime_context(
451+
node,ctx=ctx,alias_definition=is_lvalue
452+
)
423453
elifisinstance(node,TypeVarExpr):
424454
returnself.named_type("typing.TypeVar")
425455
elifisinstance(node, (ParamSpecExpr,TypeVarTupleExpr)):
426-
result=self.object_type()
427-
else:
428-
ifisinstance(node,PlaceholderNode):
429-
assertFalse,f"PlaceholderNode{node.fullname!r} leaked to checker"
430-
# Unknown reference; use any type implicitly to avoid
431-
# generating extra type errors.
432-
result=AnyType(TypeOfAny.from_error)
433-
assertresultisnotNone
434-
returnresult
456+
returnself.object_type()
457+
elifisinstance(node,MypyFile):
458+
# Reference to a module object.
459+
returnself.module_type(node)ifinclude_moduleselseAnyType(TypeOfAny.special_form)
460+
returnAnyType(TypeOfAny.from_error)
435461

436462
defanalyze_var_ref(self,var:Var,context:Context)->Type:
437463
ifvar.type:
@@ -459,20 +485,21 @@ def module_type(self, node: MypyFile) -> Instance:
459485
# Fall back to a dummy 'object' type instead to
460486
# avoid a crash.
461487
result=self.named_type("builtins.object")
462-
module_attrs= {}
488+
module_attrs:dict[str,Type]= {}
463489
immutable=set()
464490
forname,ninnode.names.items():
465491
ifnotn.module_public:
466492
continue
467493
ifisinstance(n.node,Var)andn.node.is_final:
468494
immutable.add(name)
469-
typ=self.chk.determine_type_of_member(n)
470-
iftyp:
471-
module_attrs[name]=typ
495+
ifn.nodeisNone:
496+
module_attrs[name]=AnyType(TypeOfAny.from_error)
472497
else:
473498
# TODO: what to do about nested module references?
474499
# They are non-trivial because there may be import cycles.
475-
module_attrs[name]=AnyType(TypeOfAny.special_form)
500+
module_attrs[name]=self.analyze_static_reference(
501+
n.node,n.node,False,include_modules=False,suppress_errors=True
502+
)
476503
result.extra_attrs=ExtraAttrs(module_attrs,immutable,node.fullname)
477504
returnresult
478505

@@ -961,19 +988,11 @@ def typeddict_callable(self, info: TypeInfo) -> CallableType:
961988
assertinfo.special_aliasisnotNone
962989
target=info.special_alias.target
963990
assertisinstance(target,ProperType)andisinstance(target,TypedDictType)
964-
expected_types=list(target.items.values())
965-
kinds= [ArgKind.ARG_NAMED]*len(expected_types)
966-
names=list(target.items.keys())
967-
returnCallableType(
968-
expected_types,
969-
kinds,
970-
names,
971-
target,
972-
self.named_type("builtins.type"),
973-
variables=info.defn.type_vars,
974-
)
991+
returnself.typeddict_callable_from_context(target,info.defn.type_vars)
975992

976-
deftypeddict_callable_from_context(self,callee:TypedDictType)->CallableType:
993+
deftypeddict_callable_from_context(
994+
self,callee:TypedDictType,variables:Sequence[TypeVarLikeType]|None=None
995+
)->CallableType:
977996
returnCallableType(
978997
list(callee.items.values()),
979998
[
@@ -983,6 +1002,8 @@ def typeddict_callable_from_context(self, callee: TypedDictType) -> CallableType
9831002
list(callee.items.keys()),
9841003
callee,
9851004
self.named_type("builtins.type"),
1005+
variables=variables,
1006+
is_bound=True,
9861007
)
9871008

9881009
defcheck_typeddict_call_with_kwargs(

‎mypy/checkmember.py‎

Lines changed: 12 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
TempNode,
3535
TypeAlias,
3636
TypeInfo,
37-
TypeVarExpr,
37+
TypeVarLikeExpr,
3838
Var,
3939
is_final_node,
4040
)
@@ -49,7 +49,6 @@
4949
make_simplified_union,
5050
supported_self_type,
5151
tuple_fallback,
52-
type_object_type,
5352
)
5453
frommypy.typesimport (
5554
AnyType,
@@ -537,24 +536,20 @@ def analyze_member_var_access(
537536
is_trivial_self=vv.func.is_trivial_selfandnotvv.decorators
538537
ifmx.is_superandnotmx.suppress_errors:
539538
validate_super_call(vv.func,mx)
539+
ifisinstance(v,FuncDef):
540+
assertFalse,"Did not expect a function"
541+
ifisinstance(v,MypyFile):
542+
mx.chk.module_refs.add(v.fullname)
540543

541-
ifisinstance(vv,TypeInfo):
544+
ifisinstance(vv,(TypeInfo,TypeAlias,MypyFile,TypeVarLikeExpr)):
542545
# If the associated variable is a TypeInfo synthesize a Var node for
543546
# the purposes of type checking. This enables us to type check things
544-
# like accessing class attributes on an inner class.
545-
v=Var(name,type=type_object_type(vv,mx.named_type))
546-
v.info=info
547-
548-
ifisinstance(vv,TypeAlias):
549-
# Similar to the above TypeInfo case, we allow using
550-
# qualified type aliases in runtime context if it refers to an
551-
# instance type. For example:
547+
# like accessing class attributes on an inner class. Similar we allow
548+
# using qualified type aliases in runtime context. For example:
552549
# class C:
553550
# A = List[int]
554551
# x = C.A() <- this is OK
555-
typ=mx.chk.expr_checker.alias_type_in_runtime_context(
556-
vv,ctx=mx.context,alias_definition=mx.is_lvalue
557-
)
552+
typ=mx.chk.expr_checker.analyze_static_reference(vv,mx.context,mx.is_lvalue)
558553
v=Var(name,type=typ)
559554
v.info=info
560555

@@ -567,13 +562,6 @@ def analyze_member_var_access(
567562
check_final_member(name,info,mx.msg,mx.context)
568563

569564
returnanalyze_var(name,v,itype,mx,implicit=implicit,is_trivial_self=is_trivial_self)
570-
elifisinstance(v,FuncDef):
571-
assertFalse,"Did not expect a function"
572-
elifisinstance(v,MypyFile):
573-
mx.chk.module_refs.add(v.fullname)
574-
returnmx.chk.expr_checker.module_type(v)
575-
elifisinstance(v,TypeVarExpr):
576-
returnmx.chk.named_type("typing.TypeVar")
577565
elif (
578566
notv
579567
andnamenotin ["__getattr__","__setattr__","__getattribute__"]
@@ -1259,29 +1247,9 @@ def analyze_class_attribute_access(
12591247
mx.not_ready_callback(name,mx.context)
12601248
returnAnyType(TypeOfAny.special_form)
12611249

1262-
ifisinstance(node.node,TypeVarExpr):
1263-
mx.fail(message_registry.CANNOT_USE_TYPEVAR_AS_EXPRESSION.format(info.name,name))
1264-
returnAnyType(TypeOfAny.from_error)
1265-
1266-
# TODO: some logic below duplicates analyze_ref_expr in checkexpr.py
1267-
ifisinstance(node.node,TypeInfo):
1268-
ifnode.node.typeddict_type:
1269-
# We special-case TypedDict, because they don't define any constructor.
1270-
returnmx.chk.expr_checker.typeddict_callable(node.node)
1271-
elifnode.node.fullname=="types.NoneType":
1272-
# We special case NoneType, because its stub definition is not related to None.
1273-
returnTypeType(NoneType())
1274-
else:
1275-
returntype_object_type(node.node,mx.named_type)
1276-
1277-
ifisinstance(node.node,MypyFile):
1278-
# Reference to a module object.
1279-
returnmx.named_type("types.ModuleType")
1280-
1281-
ifisinstance(node.node,TypeAlias):
1282-
returnmx.chk.expr_checker.alias_type_in_runtime_context(
1283-
node.node,ctx=mx.context,alias_definition=mx.is_lvalue
1284-
)
1250+
ifisinstance(node.node, (TypeInfo,TypeAlias,MypyFile,TypeVarLikeExpr)):
1251+
# TODO: should we apply class plugin here (similar to instance access)?
1252+
returnmx.chk.expr_checker.analyze_static_reference(node.node,mx.context,mx.is_lvalue)
12851253

12861254
ifis_decorated:
12871255
assertisinstance(node.node,Decorator)

‎mypy/message_registry.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,6 @@ def with_additional_msg(self, info: str) -> ErrorMessage:
188188

189189
# TypeVar
190190
INCOMPATIBLE_TYPEVAR_VALUE:Final='Value of type variable "{}" of {} cannot be {}'
191-
CANNOT_USE_TYPEVAR_AS_EXPRESSION:Final='Type variable "{}.{}" cannot be used as an expression'
192191
INVALID_TYPEVAR_AS_TYPEARG:Final='Type variable "{}" not valid as type argument value for "{}"'
193192
INVALID_TYPEVAR_ARG_BOUND:Final='Type argument {} of "{}" must be a subtype of {}'
194193
INVALID_TYPEVAR_ARG_VALUE:Final='Invalid type argument value for "{}"'

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp