Skip to content

Commit f8f77b8

Browse files
authored
Merge branch 'master' into typecheck_setup.py
2 parents a201e66 + e9a813c commit f8f77b8

File tree

7 files changed

+94
-19
lines changed

7 files changed

+94
-19
lines changed

misc/gen_blog_post_html.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def convert(src: str) -> str:
9595
h = re.sub(r"`\*\*`", "<tt>**</tt>", h)
9696

9797
# Paragraphs
98-
h = re.sub(r"\n([A-Z])", r"\n<p>\1", h)
98+
h = re.sub(r"\n\n([A-Z])", r"\n\n<p>\1", h)
9999

100100
# Bullet lists
101101
h = format_lists(h)
@@ -104,6 +104,7 @@ def convert(src: str) -> str:
104104
h = format_code(h)
105105

106106
# Code fragments
107+
h = re.sub(r"``([^`]+)``", r"<tt>\1</tt>", h)
107108
h = re.sub(r"`([^`]+)`", r"<tt>\1</tt>", h)
108109

109110
# Remove **** noise
@@ -125,7 +126,9 @@ def convert(src: str) -> str:
125126
r'fixes issue <a href="https://github.com/python/mypy/issues/\1">\1</a>',
126127
h,
127128
)
128-
h = re.sub(r"#([0-9]+)", r'PR <a href="https://github.com/python/mypy/pull/\1">\1</a>', h)
129+
# Note the leading space to avoid stomping on strings that contain #\d in the middle (such as
130+
# links to PRs in other repos)
131+
h = re.sub(r" #([0-9]+)", r' PR <a href="https://github.com/python/mypy/pull/\1">\1</a>', h)
129132
h = re.sub(r"\) \(PR", ", PR", h)
130133

131134
# Markdown links

misc/generate_changelog.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ def format_changelog_entry(c: CommitInfo) -> str:
145145
s += f" (#{c.pr_number})"
146146
s += f" ({c.author})"
147147
"""
148-
s = f" * {c.title} ({c.author}"
148+
title = c.title.removesuffix(".")
149+
s = f" * {title} ({c.author}"
149150
if c.pr_number:
150151
s += f", PR [{c.pr_number}](https://github.com/python/mypy/pull/{c.pr_number})"
151152
s += ")"

mypy/checkexpr.py

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@
1515
import mypy.errorcodes as codes
1616
from mypy import applytype, erasetype, join, message_registry, nodes, operators, types
1717
from mypy.argmap import ArgTypeExpander, map_actuals_to_formals, map_formals_to_actuals
18-
from mypy.checkmember import analyze_member_access, freeze_all_type_vars, type_object_type
18+
from mypy.checkmember import (
19+
analyze_member_access,
20+
freeze_all_type_vars,
21+
type_object_type,
22+
typeddict_callable,
23+
)
1924
from mypy.checkstrformat import StringFormatterChecker
2025
from mypy.erasetype import erase_type, remove_instance_last_known_values, replace_meta_vars
2126
from mypy.errors import ErrorWatcher, report_internal_error
@@ -955,20 +960,7 @@ def typeddict_callable(self, info: TypeInfo) -> CallableType:
955960
Note it is not safe to move this to type_object_type() since it will crash
956961
on plugin-generated TypedDicts, that may not have the special_alias.
957962
"""
958-
assert info.special_alias is not None
959-
target = info.special_alias.target
960-
assert isinstance(target, ProperType) and isinstance(target, TypedDictType)
961-
expected_types = list(target.items.values())
962-
kinds = [ArgKind.ARG_NAMED] * len(expected_types)
963-
names = list(target.items.keys())
964-
return CallableType(
965-
expected_types,
966-
kinds,
967-
names,
968-
target,
969-
self.named_type("builtins.type"),
970-
variables=info.defn.type_vars,
971-
)
963+
return typeddict_callable(info, self.named_type)
972964

973965
def typeddict_callable_from_context(self, callee: TypedDictType) -> CallableType:
974966
return CallableType(

mypy/checkmember.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
ARG_STAR2,
2121
EXCLUDED_ENUM_ATTRIBUTES,
2222
SYMBOL_FUNCBASE_TYPES,
23+
ArgKind,
2324
Context,
2425
Decorator,
2526
FuncBase,
@@ -1148,8 +1149,16 @@ def analyze_class_attribute_access(
11481149
)
11491150
return AnyType(TypeOfAny.from_error)
11501151

1152+
# TODO: some logic below duplicates analyze_ref_expr in checkexpr.py
11511153
if isinstance(node.node, TypeInfo):
1152-
return type_object_type(node.node, mx.named_type)
1154+
if node.node.typeddict_type:
1155+
# We special-case TypedDict, because they don't define any constructor.
1156+
return typeddict_callable(node.node, mx.named_type)
1157+
elif node.node.fullname == "types.NoneType":
1158+
# We special case NoneType, because its stub definition is not related to None.
1159+
return TypeType(NoneType())
1160+
else:
1161+
return type_object_type(node.node, mx.named_type)
11531162

11541163
if isinstance(node.node, MypyFile):
11551164
# Reference to a module object.
@@ -1330,6 +1339,31 @@ class B(A[str]): pass
13301339
return t
13311340

13321341

1342+
def typeddict_callable(info: TypeInfo, named_type: Callable[[str], Instance]) -> CallableType:
1343+
"""Construct a reasonable type for a TypedDict type in runtime context.
1344+
1345+
If it appears as a callee, it will be special-cased anyway, e.g. it is
1346+
also allowed to accept a single positional argument if it is a dict literal.
1347+
1348+
Note it is not safe to move this to type_object_type() since it will crash
1349+
on plugin-generated TypedDicts, that may not have the special_alias.
1350+
"""
1351+
assert info.special_alias is not None
1352+
target = info.special_alias.target
1353+
assert isinstance(target, ProperType) and isinstance(target, TypedDictType)
1354+
expected_types = list(target.items.values())
1355+
kinds = [ArgKind.ARG_NAMED] * len(expected_types)
1356+
names = list(target.items.keys())
1357+
return CallableType(
1358+
expected_types,
1359+
kinds,
1360+
names,
1361+
target,
1362+
named_type("builtins.type"),
1363+
variables=info.defn.type_vars,
1364+
)
1365+
1366+
13331367
def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> ProperType:
13341368
"""Return the type of a type object.
13351369

mypy/plugins/dataclasses.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,8 @@ def _freeze(self, attributes: list[DataclassAttribute]) -> None:
768768
if sym_node is not None:
769769
var = sym_node.node
770770
if isinstance(var, Var):
771+
if var.is_final:
772+
continue # do not turn `Final` attrs to `@property`
771773
var.is_property = True
772774
else:
773775
var = attr.to_var(info)

test-data/unit/check-dataclasses.test

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2553,3 +2553,26 @@ class X(metaclass=DCMeta):
25532553
class Y(X):
25542554
a: int # E: Covariant override of a mutable attribute (base class "X" defined the type as "Optional[int]", expression has type "int")
25552555
[builtins fixtures/tuple.pyi]
2556+
2557+
2558+
[case testFrozenWithFinal]
2559+
from dataclasses import dataclass
2560+
from typing import Final
2561+
2562+
@dataclass(frozen=True)
2563+
class My:
2564+
a: Final = 1
2565+
b: Final[int] = 2
2566+
2567+
reveal_type(My.a) # N: Revealed type is "Literal[1]?"
2568+
reveal_type(My.b) # N: Revealed type is "builtins.int"
2569+
My.a = 1 # E: Cannot assign to final attribute "a"
2570+
My.b = 2 # E: Cannot assign to final attribute "b"
2571+
2572+
m = My()
2573+
reveal_type(m.a) # N: Revealed type is "Literal[1]?"
2574+
reveal_type(m.b) # N: Revealed type is "builtins.int"
2575+
2576+
m.a = 1 # E: Cannot assign to final attribute "a"
2577+
m.b = 2 # E: Cannot assign to final attribute "b"
2578+
[builtins fixtures/tuple.pyi]

test-data/unit/check-typeddict.test

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4118,3 +4118,23 @@ Func = TypedDict('Func', {
41184118
})
41194119
[builtins fixtures/dict.pyi]
41204120
[typing fixtures/typing-typeddict.pyi]
4121+
4122+
[case testTypedDictNestedInClassAndInherited]
4123+
from typing_extensions import TypedDict
4124+
4125+
class Base:
4126+
class Params(TypedDict):
4127+
name: str
4128+
4129+
class Derived(Base):
4130+
pass
4131+
4132+
class DerivedOverride(Base):
4133+
class Params(Base.Params):
4134+
pass
4135+
4136+
Base.Params(name="Robert")
4137+
Derived.Params(name="Robert")
4138+
DerivedOverride.Params(name="Robert")
4139+
[builtins fixtures/dict.pyi]
4140+
[typing fixtures/typing-typeddict.pyi]

0 commit comments

Comments
 (0)