Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consistently use literal-required error code for TypedDicts #14621

Merged
merged 2 commits into from
Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions docs/source/error_code_list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,35 @@ consistently when using the call-based syntax. Example:
# Error: First argument to namedtuple() should be "Point2D", not "Point"
Point2D = NamedTuple("Point", [("x", int), ("y", int)])

Check that literal is used where expected [literal-required]
------------------------------------------------------------

There are some places where only a (string) literal value is expected for
the purposes of statis type checking, for example a ``TypedDict`` key, or
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: statis

a ``__match_args__`` item. Providing a ``str``-valued variable in such contexts
will result in an error. Note however, in many cases you can use ``Final``,
or ``Literal`` variables, for example:

.. code-block:: python

from typing import Final, Literal, TypedDict

class Point(TypedDict):
x: int
y: int

def test(p: Point) -> None:
X: Final = "x"
p[X] # OK

Y: Literal["y"] = "y"
p[Y] # OK

key = "x" # Inferred type of key is `str`
# Error: TypedDict key must be a string literal;
# expected one of ("x", "y") [literal-required]
p[key]

Check that overloaded functions have an implementation [no-overload-impl]
-------------------------------------------------------------------------

Expand Down
6 changes: 5 additions & 1 deletion mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,11 @@ def validate_typeddict_kwargs(self, kwargs: DictExpr) -> dict[str, Expression] |
literal_value = values[0]
if literal_value is None:
key_context = item_name_expr or item_arg
self.chk.fail(message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL, key_context)
self.chk.fail(
message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL,
key_context,
code=codes.LITERAL_REQ,
)
return None
else:
item_names.append(literal_value)
Expand Down
19 changes: 16 additions & 3 deletions mypy/plugins/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from functools import partial
from typing import Callable

import mypy.errorcodes as codes
from mypy import message_registry
from mypy.nodes import DictExpr, IntExpr, StrExpr, UnaryExpr
from mypy.plugin import (
Expand Down Expand Up @@ -264,7 +265,11 @@ def typed_dict_pop_callback(ctx: MethodContext) -> Type:
):
keys = try_getting_str_literals(ctx.args[0][0], ctx.arg_types[0][0])
if keys is None:
ctx.api.fail(message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL, ctx.context)
ctx.api.fail(
message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL,
ctx.context,
code=codes.LITERAL_REQ,
)
return AnyType(TypeOfAny.from_error)

value_types = []
Expand Down Expand Up @@ -319,7 +324,11 @@ def typed_dict_setdefault_callback(ctx: MethodContext) -> Type:
):
keys = try_getting_str_literals(ctx.args[0][0], ctx.arg_types[0][0])
if keys is None:
ctx.api.fail(message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL, ctx.context)
ctx.api.fail(
message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL,
ctx.context,
code=codes.LITERAL_REQ,
)
return AnyType(TypeOfAny.from_error)

default_type = ctx.arg_types[1][0]
Expand Down Expand Up @@ -357,7 +366,11 @@ def typed_dict_delitem_callback(ctx: MethodContext) -> Type:
):
keys = try_getting_str_literals(ctx.args[0][0], ctx.arg_types[0][0])
if keys is None:
ctx.api.fail(message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL, ctx.context)
ctx.api.fail(
message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL,
ctx.context,
code=codes.LITERAL_REQ,
)
return AnyType(TypeOfAny.from_error)

for key in keys:
Expand Down