Skip to content

Commit 9de2f07

Browse files
Merge branch 'master' into improve-inspection-is_classmethod
2 parents ea4bbf0 + 03cf35c commit 9de2f07

29 files changed

+332
-150
lines changed

.pre-commit-config.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ repos:
2121
- id: check-github-workflows
2222
- id: check-github-actions
2323
- id: check-readthedocs
24+
- repo: https://github.com/codespell-project/codespell
25+
rev: v2.4.1
26+
hooks:
27+
- id: codespell
28+
args:
29+
- --ignore-words-list=HAX,ccompiler,ot,statics,whet,zar
30+
exclude: ^(mypy/test/|mypy/typeshed/|mypyc/test-data/|test-data/).+$
2431
- repo: https://github.com/rhysd/actionlint
2532
rev: v1.7.7
2633
hooks:

CHANGELOG.md

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,48 @@
22

33
## Next Release
44

5-
...
5+
### Different Property Getter and Setter Types
6+
7+
Mypy now supports using different types for property getter and setter.
8+
```python
9+
class A:
10+
value: int
11+
12+
@property
13+
def f(self) -> int:
14+
return self.value
15+
@f.setter
16+
def f(self, x: str | int) -> None:
17+
try:
18+
self.value = int(x)
19+
except ValueError:
20+
raise Exception(f"'{x}' is not a valid value for 'f'")
21+
```
22+
23+
Contributed by Ivan Levkivskyi (PR [18510](https://github.com/python/mypy/pull/18510))
24+
25+
### Selectively Disable Deprecated Warnings
26+
27+
It's now possible to selectively disable warnings generated from
28+
[`warnings.deprecated`](https://docs.python.org/3/library/warnings.html#warnings.deprecated)
29+
using the [`--deprecated-calls-exclude`](https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-deprecated-calls-exclude)
30+
option.
31+
32+
```python
33+
# mypy --enable-error-code deprecated
34+
# --deprecated-calls-exclude=foo.A
35+
import foo
36+
37+
foo.A().func() # OK, the deprecated warning is ignored
38+
39+
# file foo.py
40+
from typing_extensions import deprecated
41+
class A:
42+
@deprecated("Use A.func2 instead")
43+
def func(self): pass
44+
```
45+
46+
Contributed by Marc Mueller (PR [18641](https://github.com/python/mypy/pull/18641))
647

748
## Mypy 1.15
849

mypy/checker.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4600,10 +4600,8 @@ def check_member_assignment(
46004600
bound_method = analyze_decorator_or_funcbase_access(
46014601
defn=dunder_set,
46024602
itype=attribute_type,
4603-
info=attribute_type.type,
4604-
self_type=attribute_type,
46054603
name="__set__",
4606-
mx=mx,
4604+
mx=mx.copy_modified(self_type=attribute_type),
46074605
)
46084606
typ = map_instance_to_supertype(attribute_type, dunder_set.info)
46094607
dunder_set_type = expand_type_by_instance(bound_method, typ)

mypy/checkmember.py

Lines changed: 20 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from collections.abc import Sequence
66
from typing import TYPE_CHECKING, Callable, cast
77

8-
from mypy import meet, message_registry, subtypes
8+
from mypy import message_registry, subtypes
99
from mypy.erasetype import erase_typevars
1010
from mypy.expandtype import (
1111
expand_self_type,
@@ -267,7 +267,9 @@ def may_be_awaitable_attribute(
267267
aw_type = mx.chk.get_precise_awaitable_type(typ, local_errors)
268268
if aw_type is None:
269269
return False
270-
_ = _analyze_member_access(name, aw_type, mx, override_info)
270+
_ = _analyze_member_access(
271+
name, aw_type, mx.copy_modified(self_type=aw_type), override_info
272+
)
271273
return not local_errors.has_new_errors()
272274

273275

@@ -323,7 +325,7 @@ def analyze_instance_member_access(
323325
assert isinstance(getter, Decorator)
324326
if mx.is_lvalue and (len(items := method.items) > 1):
325327
mx.chk.warn_deprecated(items[1], mx.context)
326-
return analyze_var(name, getter.var, typ, info, mx)
328+
return analyze_var(name, getter.var, typ, mx)
327329

328330
if mx.is_lvalue:
329331
mx.msg.cant_assign_to_method(mx.context)
@@ -340,11 +342,8 @@ def analyze_instance_member_access(
340342
signature = method.type
341343
signature = freshen_all_functions_type_vars(signature)
342344
if not method.is_static:
343-
# TODO: use proper treatment of special methods on unions instead
344-
# of this hack here and below (i.e. mx.self_type).
345-
dispatched_type = meet.meet_types(mx.original_type, typ)
346345
signature = check_self_arg(
347-
signature, dispatched_type, method.is_class, mx.context, name, mx.msg
346+
signature, mx.self_type, method.is_class, mx.context, name, mx.msg
348347
)
349348
signature = bind_self(signature, mx.self_type, is_classmethod=method.is_class)
350349
# TODO: should we skip these steps for static methods as well?
@@ -536,7 +535,7 @@ def analyze_member_var_access(
536535
if mx.is_lvalue and not mx.chk.get_final_context():
537536
check_final_member(name, info, mx.msg, mx.context)
538537

539-
return analyze_var(name, v, itype, info, mx, implicit=implicit)
538+
return analyze_var(name, v, itype, mx, implicit=implicit)
540539
elif isinstance(v, FuncDef):
541540
assert False, "Did not expect a function"
542541
elif isinstance(v, MypyFile):
@@ -560,12 +559,7 @@ def analyze_member_var_access(
560559
# that the attribute exists
561560
if method and method.info.fullname != "builtins.object":
562561
bound_method = analyze_decorator_or_funcbase_access(
563-
defn=method,
564-
itype=itype,
565-
info=info,
566-
self_type=mx.self_type,
567-
name=method_name,
568-
mx=mx,
562+
defn=method, itype=itype, name=method_name, mx=mx
569563
)
570564
typ = map_instance_to_supertype(itype, method.info)
571565
getattr_type = get_proper_type(expand_type_by_instance(bound_method, typ))
@@ -592,12 +586,7 @@ def analyze_member_var_access(
592586
setattr_meth = info.get_method("__setattr__")
593587
if setattr_meth and setattr_meth.info.fullname != "builtins.object":
594588
bound_type = analyze_decorator_or_funcbase_access(
595-
defn=setattr_meth,
596-
itype=itype,
597-
info=info,
598-
self_type=mx.self_type,
599-
name=name,
600-
mx=mx.copy_modified(is_lvalue=False),
589+
defn=setattr_meth, itype=itype, name=name, mx=mx.copy_modified(is_lvalue=False)
601590
)
602591
typ = map_instance_to_supertype(itype, setattr_meth.info)
603592
setattr_type = get_proper_type(expand_type_by_instance(bound_type, typ))
@@ -683,10 +672,8 @@ def analyze_descriptor_access(
683672
bound_method = analyze_decorator_or_funcbase_access(
684673
defn=dunder_get,
685674
itype=descriptor_type,
686-
info=descriptor_type.type,
687-
self_type=descriptor_type,
688675
name="__get__",
689-
mx=mx,
676+
mx=mx.copy_modified(self_type=descriptor_type),
690677
)
691678

692679
typ = map_instance_to_supertype(descriptor_type, dunder_get.info)
@@ -762,13 +749,7 @@ def is_instance_var(var: Var) -> bool:
762749

763750

764751
def analyze_var(
765-
name: str,
766-
var: Var,
767-
itype: Instance,
768-
info: TypeInfo,
769-
mx: MemberContext,
770-
*,
771-
implicit: bool = False,
752+
name: str, var: Var, itype: Instance, mx: MemberContext, *, implicit: bool = False
772753
) -> Type:
773754
"""Analyze access to an attribute via a Var node.
774755
@@ -807,7 +788,9 @@ def analyze_var(
807788
if isinstance(typ, FunctionLike) and not typ.is_type_obj():
808789
call_type = typ
809790
elif var.is_property:
810-
call_type = get_proper_type(_analyze_member_access("__call__", typ, mx))
791+
call_type = get_proper_type(
792+
_analyze_member_access("__call__", typ, mx.copy_modified(self_type=typ))
793+
)
811794
else:
812795
call_type = typ
813796

@@ -823,20 +806,12 @@ def analyze_var(
823806
# Class-level function objects and classmethods become bound methods:
824807
# the former to the instance, the latter to the class.
825808
functype: FunctionLike = call_type
826-
# Use meet to narrow original_type to the dispatched type.
827-
# For example, assume
828-
# * A.f: Callable[[A1], None] where A1 <: A (maybe A1 == A)
829-
# * B.f: Callable[[B1], None] where B1 <: B (maybe B1 == B)
830-
# * x: Union[A1, B1]
831-
# In `x.f`, when checking `x` against A1 we assume x is compatible with A
832-
# and similarly for B1 when checking against B
833-
dispatched_type = meet.meet_types(mx.original_type, itype)
834809
signature = freshen_all_functions_type_vars(functype)
835810
bound = get_proper_type(expand_self_type(var, signature, mx.original_type))
836811
assert isinstance(bound, FunctionLike)
837812
signature = bound
838813
signature = check_self_arg(
839-
signature, dispatched_type, var.is_classmethod, mx.context, name, mx.msg
814+
signature, mx.self_type, var.is_classmethod, mx.context, name, mx.msg
840815
)
841816
signature = bind_self(signature, mx.self_type, var.is_classmethod)
842817
expanded_signature = expand_type_by_instance(signature, itype)
@@ -946,13 +921,9 @@ def check_self_arg(
946921
For example if the method is defined as:
947922
class A:
948923
def f(self: S) -> T: ...
949-
then for 'x.f' we check that meet(type(x), A) <: S. If the method is overloaded, we
950-
select only overloads items that satisfy this requirement. If there are no matching
924+
then for 'x.f' we check that type(x) <: S. If the method is overloaded, we select
925+
only overloads items that satisfy this requirement. If there are no matching
951926
overloads, an error is generated.
952-
953-
Note: dispatched_arg_type uses a meet to select a relevant item in case if the
954-
original type of 'x' is a union. This is done because several special methods
955-
treat union types in ad-hoc manner, so we can't use MemberContext.self_type yet.
956927
"""
957928
items = functype.items
958929
if not items:
@@ -1436,22 +1407,17 @@ def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> P
14361407

14371408

14381409
def analyze_decorator_or_funcbase_access(
1439-
defn: Decorator | FuncBase,
1440-
itype: Instance,
1441-
info: TypeInfo,
1442-
self_type: Type | None,
1443-
name: str,
1444-
mx: MemberContext,
1410+
defn: Decorator | FuncBase, itype: Instance, name: str, mx: MemberContext
14451411
) -> Type:
14461412
"""Analyzes the type behind method access.
14471413
14481414
The function itself can possibly be decorated.
14491415
See: https://github.com/python/mypy/issues/10409
14501416
"""
14511417
if isinstance(defn, Decorator):
1452-
return analyze_var(name, defn.var, itype, info, mx)
1418+
return analyze_var(name, defn.var, itype, mx)
14531419
return bind_self(
1454-
function_type(defn, mx.chk.named_type("builtins.function")), original_type=self_type
1420+
function_type(defn, mx.chk.named_type("builtins.function")), original_type=mx.self_type
14551421
)
14561422

14571423

mypy/checkpattern.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
get_proper_type,
5555
split_with_prefix_and_suffix,
5656
)
57-
from mypy.typevars import fill_typevars
57+
from mypy.typevars import fill_typevars, fill_typevars_with_any
5858
from mypy.visitor import PatternVisitor
5959

6060
self_match_type_names: Final = [
@@ -544,16 +544,7 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType:
544544
self.msg.fail(message_registry.CLASS_PATTERN_GENERIC_TYPE_ALIAS, o)
545545
return self.early_non_match()
546546
if isinstance(type_info, TypeInfo):
547-
any_type = AnyType(TypeOfAny.implementation_artifact)
548-
args: list[Type] = []
549-
for tv in type_info.defn.type_vars:
550-
if isinstance(tv, TypeVarTupleType):
551-
args.append(
552-
UnpackType(self.chk.named_generic_type("builtins.tuple", [any_type]))
553-
)
554-
else:
555-
args.append(any_type)
556-
typ: Type = Instance(type_info, args)
547+
typ: Type = fill_typevars_with_any(type_info)
557548
elif isinstance(type_info, TypeAlias):
558549
typ = type_info.target
559550
elif (
@@ -703,6 +694,8 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType:
703694

704695
def should_self_match(self, typ: Type) -> bool:
705696
typ = get_proper_type(typ)
697+
if isinstance(typ, TupleType):
698+
typ = typ.partial_fallback
706699
if isinstance(typ, Instance) and typ.type.get("__match_args__") is not None:
707700
# Named tuples and other subtypes of builtins that define __match_args__
708701
# should not self match.

mypy/defaults.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717
CONFIG_NAMES: Final = ["mypy.ini", ".mypy.ini"]
1818
SHARED_CONFIG_NAMES: Final = ["pyproject.toml", "setup.cfg"]
1919

20-
USER_CONFIG_FILES: Final = ["~/.config/mypy/config", "~/.mypy.ini"]
20+
USER_CONFIG_FILES: list[str] = ["~/.config/mypy/config", "~/.mypy.ini"]
2121
if os.environ.get("XDG_CONFIG_HOME"):
2222
USER_CONFIG_FILES.insert(0, os.path.join(os.environ["XDG_CONFIG_HOME"], "mypy/config"))
23+
USER_CONFIG_FILES = [os.path.expanduser(f) for f in USER_CONFIG_FILES]
2324

2425
# This must include all reporters defined in mypy.report. This is defined here
2526
# to make reporter names available without importing mypy.report -- this speeds

mypy/erasetype.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,14 @@ def visit_tuple_type(self, t: TupleType) -> Type:
203203
return unpacked
204204
return result
205205

206+
def visit_callable_type(self, t: CallableType) -> Type:
207+
result = super().visit_callable_type(t)
208+
assert isinstance(result, ProperType) and isinstance(result, CallableType)
209+
# Usually this is done in semanal_typeargs.py, but erasure can create
210+
# a non-normal callable from normal one.
211+
result.normalize_trivial_unpack()
212+
return result
213+
206214
def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type:
207215
if self.erase_id(t.id):
208216
return t.tuple_fallback.copy_modified(args=[self.replacement])

mypy/expandtype.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ def visit_instance(self, t: Instance) -> Type:
226226
if isinstance(arg, UnpackType):
227227
unpacked = get_proper_type(arg.type)
228228
if isinstance(unpacked, Instance):
229+
# TODO: this and similar asserts below may be unsafe because get_proper_type()
230+
# may be called during semantic analysis before all invalid types are removed.
229231
assert unpacked.type.fullname == "builtins.tuple"
230232
args = list(unpacked.args)
231233
return t.copy_modified(args=args)
@@ -333,10 +335,7 @@ def interpolate_args_for_unpack(self, t: CallableType, var_arg: UnpackType) -> l
333335

334336
var_arg_type = get_proper_type(var_arg.type)
335337
new_unpack: Type
336-
if isinstance(var_arg_type, Instance):
337-
# we have something like Unpack[Tuple[Any, ...]]
338-
new_unpack = UnpackType(var_arg.type.accept(self))
339-
elif isinstance(var_arg_type, TupleType):
338+
if isinstance(var_arg_type, TupleType):
340339
# We have something like Unpack[Tuple[Unpack[Ts], X1, X2]]
341340
expanded_tuple = var_arg_type.accept(self)
342341
assert isinstance(expanded_tuple, ProperType) and isinstance(expanded_tuple, TupleType)
@@ -348,6 +347,11 @@ def interpolate_args_for_unpack(self, t: CallableType, var_arg: UnpackType) -> l
348347
fallback = var_arg_type.tuple_fallback
349348
expanded_items = self.expand_unpack(var_arg)
350349
new_unpack = UnpackType(TupleType(expanded_items, fallback))
350+
# Since get_proper_type() may be called in semanal.py before callable
351+
# normalization happens, we need to also handle non-normal cases here.
352+
elif isinstance(var_arg_type, Instance):
353+
# we have something like Unpack[Tuple[Any, ...]]
354+
new_unpack = UnpackType(var_arg.type.accept(self))
351355
else:
352356
# We have invalid type in Unpack. This can happen when expanding aliases
353357
# to Callable[[*Invalid], Ret]

mypy/fastparse.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ def from_operator(self, op: ast3.operator) -> str:
557557
ast3.Is: "is",
558558
ast3.IsNot: "is not",
559559
ast3.In: "in",
560-
ast3.NotIn: "not in",
560+
ast3.NotIn: "not in", # codespell:ignore notin
561561
}
562562

563563
def from_comp_operator(self, op: ast3.cmpop) -> str:
@@ -2169,7 +2169,7 @@ def visit_member_expr(self, e: MemberExpr) -> None:
21692169

21702170

21712171
class FindYield(TraverserVisitor):
2172-
"""Check if an AST contains yields or yield froms."""
2172+
"""Check if an AST contains yields or yield froms.""" # codespell:ignore froms
21732173

21742174
def __init__(self) -> None:
21752175
self.found = False

mypy/ipc.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,10 @@ def cleanup(self) -> None:
303303
def connection_name(self) -> str:
304304
if sys.platform == "win32":
305305
return self.name
306+
elif sys.platform == "gnu0":
307+
# GNU/Hurd returns empty string from getsockname()
308+
# for AF_UNIX sockets
309+
return os.path.join(self.sock_directory, self.name)
306310
else:
307311
name = self.sock.getsockname()
308312
assert isinstance(name, str)

0 commit comments

Comments
 (0)