Skip to content

Commit 443f6c7

Browse files
Merge branch 'master' into special_case_typeddict_get
2 parents 628a0be + 16cd4c5 commit 443f6c7

22 files changed

+697
-22
lines changed

.github/workflows/mypy_primer.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ jobs:
6767
--debug \
6868
--additional-flags="--debug-serialize" \
6969
--output concise \
70+
--mypy-install-librt \
7071
| tee diff_${{ matrix.shard-index }}.txt
7172
) || [ $? -eq 1 ]
7273
- if: ${{ matrix.shard-index == 0 }}

docs/source/command_line.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ definitions or calls.
372372

373373
.. option:: --untyped-calls-exclude
374374

375-
This flag allows to selectively disable :option:`--disallow-untyped-calls`
375+
This flag allows one to selectively disable :option:`--disallow-untyped-calls`
376376
for functions and methods defined in specific packages, modules, or classes.
377377
Note that each exclude entry acts as a prefix. For example (assuming there
378378
are no type annotations for ``third_party_lib`` available):
@@ -562,7 +562,7 @@ potentially problematic or redundant in some way.
562562

563563
.. option:: --deprecated-calls-exclude
564564

565-
This flag allows to selectively disable :ref:`deprecated<code-deprecated>` warnings
565+
This flag allows one to selectively disable :ref:`deprecated<code-deprecated>` warnings
566566
for functions and methods defined in specific packages, modules, or classes.
567567
Note that each exclude entry acts as a prefix. For example (assuming ``foo.A.func`` is deprecated):
568568

docs/source/error_code_list.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,8 +1032,8 @@ Warn about top level await expressions [top-level-await]
10321032
This error code is separate from the general ``[syntax]`` errors, because in
10331033
some environments (e.g. IPython) a top level ``await`` is allowed. In such
10341034
environments a user may want to use ``--disable-error-code=top-level-await``,
1035-
that allows to still have errors for other improper uses of ``await``, for
1036-
example:
1035+
which allows one to still have errors for other improper uses of ``await``,
1036+
for example:
10371037

10381038
.. code-block:: python
10391039

docs/source/mypy_daemon.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,16 +252,16 @@ command.
252252
Statically inspect expressions
253253
******************************
254254

255-
The daemon allows to get declared or inferred type of an expression (or other
255+
The daemon allows one to get the declared or inferred type of an expression (or other
256256
information about an expression, such as known attributes or definition location)
257-
using ``dmypy inspect LOCATION`` command. The location of the expression should be
257+
using the ``dmypy inspect LOCATION`` command. The location of the expression should be
258258
specified in the format ``path/to/file.py:line:column[:end_line:end_column]``.
259259
Both line and column are 1-based. Both start and end position are inclusive.
260260
These rules match how mypy prints the error location in error messages.
261261

262262
If a span is given (i.e. all 4 numbers), then only an exactly matching expression
263263
is inspected. If only a position is given (i.e. 2 numbers, line and column), mypy
264-
will inspect all *expressions*, that include this position, starting from the
264+
will inspect all expressions that include this position, starting from the
265265
innermost one.
266266

267267
Consider this Python code snippet:

mypy/semanal.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6292,22 +6292,37 @@ def record_imported_symbol(self, sym: SymbolTableNode) -> None:
62926292
if sym.kind == LDEF or sym.node is None:
62936293
return
62946294
node = sym.node
6295-
if not node.fullname:
6295+
if isinstance(node, PlaceholderNode) or not node.fullname:
6296+
# This node is not ready yet.
62966297
return
6298+
if node.fullname.startswith(("builtins.", "typing.")):
6299+
# Skip dependencies on builtins/typing.
6300+
return
6301+
# Modules, classes, and type aliases store defining module directly.
62976302
if isinstance(node, MypyFile):
62986303
fullname = node.fullname
62996304
elif isinstance(node, TypeInfo):
63006305
fullname = node.module_name
63016306
elif isinstance(node, TypeAlias):
63026307
fullname = node.module
6303-
elif isinstance(node, (Var, FuncDef, OverloadedFuncDef)) and node.info:
6304-
fullname = node.info.module_name
6308+
elif isinstance(node, (Var, FuncDef, OverloadedFuncDef, Decorator)):
6309+
# For functions/variables infer defining module from enclosing class.
6310+
info = node.var.info if isinstance(node, Decorator) else node.info
6311+
if info:
6312+
fullname = info.module_name
6313+
else:
6314+
# global function/variable
6315+
fullname = node.fullname.rsplit(".", maxsplit=1)[0]
63056316
else:
6306-
fullname = node.fullname.rsplit(".")[0]
6317+
# Some nodes (currently only TypeVarLikeExpr subclasses) don't store
6318+
# module fullname explicitly, infer it from the node fullname iteratively.
6319+
# TODO: this is not 100% robust for type variables nested within a class
6320+
# with a name that matches name of a submodule.
6321+
fullname = node.fullname.rsplit(".", maxsplit=1)[0]
63076322
if fullname == self.cur_mod_id:
63086323
return
63096324
while "." in fullname and fullname not in self.modules:
6310-
fullname = fullname.rsplit(".")[0]
6325+
fullname = fullname.rsplit(".", maxsplit=1)[0]
63116326
if fullname != self.cur_mod_id:
63126327
self.cur_mod_node.module_refs.add(fullname)
63136328

mypy/strconv.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,9 @@ def visit_name_expr(self, o: mypy.nodes.NameExpr) -> str:
388388
o.name, o.kind, o.fullname, o.is_inferred_def or o.is_special_form, o.node
389389
)
390390
if isinstance(o.node, mypy.nodes.Var) and o.node.is_final:
391-
pretty += f" = {o.node.final_value}"
391+
final_value = o.node.final_value
392+
if final_value is not None:
393+
pretty += f" = {o.node.final_value}"
392394
return short_type(o) + "(" + pretty + ")"
393395

394396
def pretty_name(

mypy/traverser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ class ExtendedTraverserVisitor(TraverserVisitor):
504504
In addition to the base traverser it:
505505
* has visit_ methods for leaf nodes
506506
* has common method that is called for all nodes
507-
* allows to skip recursing into a node
507+
* allows skipping recursing into a node
508508
509509
Note that this traverser still doesn't visit some internal
510510
mypy constructs like _promote expression and Var.

mypy/type_visitor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
get_proper_type,
5252
)
5353

54-
T = TypeVar("T")
54+
T = TypeVar("T", covariant=True)
5555

5656

5757
@trait

mypy/types.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3938,8 +3938,12 @@ def visit_type_alias_type(self, t: TypeAliasType, /) -> list[mypy.nodes.TypeAlia
39383938
assert t.alias is not None
39393939
if t.alias not in self.seen_alias_nodes:
39403940
self.seen_alias_nodes.add(t.alias)
3941-
return [t.alias] + t.alias.target.accept(self)
3942-
return []
3941+
res = [t.alias] + t.alias.target.accept(self)
3942+
else:
3943+
res = []
3944+
for arg in t.args:
3945+
res.extend(arg.accept(self))
3946+
return res
39433947

39443948

39453949
def is_named_instance(t: Type, fullnames: str | tuple[str, ...]) -> TypeGuard[Instance]:

mypyc/codegen/emitclass.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ def native_slot(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
3939
return f"{NATIVE_PREFIX}{fn.cname(emitter.names)}"
4040

4141

42+
def dunder_attr_slot(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
43+
wrapper_fn = cl.get_method(fn.name + "__wrapper")
44+
assert wrapper_fn
45+
return f"{NATIVE_PREFIX}{wrapper_fn.cname(emitter.names)}"
46+
47+
4248
# We maintain a table from dunder function names to struct slots they
4349
# correspond to and functions that generate a wrapper (if necessary)
4450
# and return the function name to stick in the slot.
@@ -55,6 +61,7 @@ def native_slot(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
5561
"__iter__": ("tp_iter", native_slot),
5662
"__hash__": ("tp_hash", generate_hash_wrapper),
5763
"__get__": ("tp_descr_get", generate_get_wrapper),
64+
"__getattr__": ("tp_getattro", dunder_attr_slot),
5865
}
5966

6067
AS_MAPPING_SLOT_DEFS: SlotTable = {

0 commit comments

Comments
 (0)