Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into bugfix/st-paramsp…
Browse files Browse the repository at this point in the history
…ec-with-functools-partial
  • Loading branch information
sterliakov committed Jun 24, 2024
2 parents acb3548 + 6c1d867 commit fd79c7a
Show file tree
Hide file tree
Showing 171 changed files with 5,920 additions and 1,554 deletions.
20 changes: 10 additions & 10 deletions docs/source/error_code_list2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ Error codes for optional checks

This section documents various errors codes that mypy generates only
if you enable certain options. See :ref:`error-codes` for general
documentation about error codes. :ref:`error-code-list` documents
error codes that are enabled by default.
documentation about error codes and their configuration.
:ref:`error-code-list` documents error codes that are enabled by default.

.. note::

Expand Down Expand Up @@ -241,7 +241,7 @@ mypy generates an error if it thinks that an expression is redundant.

.. code-block:: python
# Use "mypy --enable-error-code redundant-expr ..."
# mypy: enable-error-code="redundant-expr"
def example(x: int) -> None:
# Error: Left operand of "and" is always true [redundant-expr]
Expand All @@ -268,7 +268,7 @@ example:

.. code-block:: python
# Use "mypy --enable-error-code possibly-undefined ..."
# mypy: enable-error-code="possibly-undefined"
from typing import Iterable
Expand Down Expand Up @@ -297,7 +297,7 @@ Using an iterable value in a boolean context has a separate error code

.. code-block:: python
# Use "mypy --enable-error-code truthy-bool ..."
# mypy: enable-error-code="truthy-bool"
class Foo:
pass
Expand Down Expand Up @@ -347,7 +347,7 @@ Example:

.. code-block:: python
# Use "mypy --enable-error-code ignore-without-code ..."
# mypy: enable-error-code="ignore-without-code"
class Foo:
def __init__(self, name: str) -> None:
Expand Down Expand Up @@ -378,7 +378,7 @@ Example:

.. code-block:: python
# Use "mypy --enable-error-code unused-awaitable ..."
# mypy: enable-error-code="unused-awaitable"
import asyncio
Expand Down Expand Up @@ -462,7 +462,7 @@ Example:

.. code-block:: python
# Use "mypy --enable-error-code explicit-override ..."
# mypy: enable-error-code="explicit-override"
from typing import override
Expand Down Expand Up @@ -536,7 +536,7 @@ Now users can actually import ``reveal_type`` to make the runtime code safe.

.. code-block:: python
# Use "mypy --enable-error-code unimported-reveal"
# mypy: enable-error-code="unimported-reveal"
x = 1
reveal_type(x) # Note: Revealed type is "builtins.int" \
Expand All @@ -546,7 +546,7 @@ Correct usage:

.. code-block:: python
# Use "mypy --enable-error-code unimported-reveal"
# mypy: enable-error-code="unimported-reveal"
from typing import reveal_type # or `typing_extensions`
x = 1
Expand Down
10 changes: 7 additions & 3 deletions docs/source/error_codes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,13 @@ still keep the other two error codes enabled. The overall logic is following:

* Individual config sections *adjust* them per glob/module

* Inline ``# mypy: disable-error-code="..."`` comments can further
*adjust* them for a specific module.
For example: ``# mypy: disable-error-code="truthy-bool, ignore-without-code"``
* Inline ``# mypy: disable-error-code="..."`` and ``# mypy: enable-error-code="..."``
comments can further *adjust* them for a specific file.
For example:

.. code-block:: python
# mypy: enable-error-code="truthy-bool, ignore-without-code"
So one can e.g. enable some code globally, disable it for all tests in
the corresponding config section, and then re-enable it with an inline
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
From 5c00e362d40aa26e0a22a740f05a52d05edf0f91 Mon Sep 17 00:00:00 2001
From 3ec9b878d6bbe3fae64a508a62372f10a886406f Mon Sep 17 00:00:00 2001
From: Shantanu <[email protected]>
Date: Mon, 26 Sep 2022 12:55:07 -0700
Subject: [PATCH] Remove use of LiteralString in builtins (#13743)

---
mypy/typeshed/stdlib/builtins.pyi | 88 -------------------------------
1 file changed, 88 deletions(-)
mypy/typeshed/stdlib/builtins.pyi | 95 -------------------------------
1 file changed, 95 deletions(-)

diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi
index b4765b26c..99919c64c 100644
index 53e00ec6a..bad3250ef 100644
--- a/mypy/typeshed/stdlib/builtins.pyi
+++ b/mypy/typeshed/stdlib/builtins.pyi
@@ -61,7 +61,6 @@ from typing import ( # noqa: Y022
Expand All @@ -19,7 +19,7 @@ index b4765b26c..99919c64c 100644
ParamSpec,
Self,
TypeAlias,
@@ -434,31 +433,16 @@ class str(Sequence[str]):
@@ -435,31 +434,16 @@ class str(Sequence[str]):
def __new__(cls, object: object = ...) -> Self: ...
@overload
def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ...
Expand Down Expand Up @@ -49,9 +49,9 @@ index b4765b26c..99919c64c 100644
- def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ...
- @overload
def format(self, *args: object, **kwargs: object) -> str: ...
def format_map(self, map: _FormatMapMapping) -> str: ...
def format_map(self, mapping: _FormatMapMapping, /) -> str: ...
def index(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ...
@@ -474,89 +458,32 @@ class str(Sequence[str]):
@@ -475,99 +459,35 @@ class str(Sequence[str]):
def isspace(self) -> bool: ...
def istitle(self) -> bool: ...
def isupper(self) -> bool: ...
Expand All @@ -75,10 +75,20 @@ index b4765b26c..99919c64c 100644
- def partition(self: LiteralString, sep: LiteralString, /) -> tuple[LiteralString, LiteralString, LiteralString]: ...
- @overload
def partition(self, sep: str, /) -> tuple[str, str, str]: ... # type: ignore[misc]
- @overload
- def replace(self: LiteralString, old: LiteralString, new: LiteralString, count: SupportsIndex = -1, /) -> LiteralString: ...
- @overload
def replace(self, old: str, new: str, count: SupportsIndex = -1, /) -> str: ... # type: ignore[misc]
if sys.version_info >= (3, 13):
- @overload
- def replace(
- self: LiteralString, old: LiteralString, new: LiteralString, /, count: SupportsIndex = -1
- ) -> LiteralString: ...
- @overload
def replace(self, old: str, new: str, /, count: SupportsIndex = -1) -> str: ... # type: ignore[misc]
else:
- @overload
- def replace(
- self: LiteralString, old: LiteralString, new: LiteralString, count: SupportsIndex = -1, /
- ) -> LiteralString: ...
- @overload
def replace(self, old: str, new: str, count: SupportsIndex = -1, /) -> str: ... # type: ignore[misc]
if sys.version_info >= (3, 9):
- @overload
- def removeprefix(self: LiteralString, prefix: LiteralString, /) -> LiteralString: ...
Expand Down Expand Up @@ -141,7 +151,7 @@ index b4765b26c..99919c64c 100644
def zfill(self, width: SupportsIndex, /) -> str: ... # type: ignore[misc]
@staticmethod
@overload
@@ -567,9 +494,6 @@ class str(Sequence[str]):
@@ -578,9 +498,6 @@ class str(Sequence[str]):
@staticmethod
@overload
def maketrans(x: str, y: str, z: str, /) -> dict[int, int | None]: ...
Expand All @@ -151,7 +161,7 @@ index b4765b26c..99919c64c 100644
def __add__(self, value: str, /) -> str: ... # type: ignore[misc]
# Incompatible with Sequence.__contains__
def __contains__(self, key: str, /) -> bool: ... # type: ignore[override]
@@ -578,25 +502,13 @@ class str(Sequence[str]):
@@ -589,25 +506,13 @@ class str(Sequence[str]):
def __getitem__(self, key: SupportsIndex | slice, /) -> str: ...
def __gt__(self, value: str, /) -> bool: ...
def __hash__(self) -> int: ...
Expand All @@ -178,5 +188,5 @@ index b4765b26c..99919c64c 100644
def __getnewargs__(self) -> tuple[str]: ...

--
2.39.3 (Apple Git-146)
2.45.2

140 changes: 135 additions & 5 deletions mypy/applytype.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
from __future__ import annotations

from typing import Callable, Sequence
from typing import Callable, Iterable, Sequence

import mypy.subtypes
from mypy.erasetype import erase_typevars
from mypy.expandtype import expand_type
from mypy.nodes import Context
from mypy.nodes import Context, TypeInfo
from mypy.type_visitor import TypeTranslator
from mypy.typeops import get_all_type_vars
from mypy.types import (
AnyType,
CallableType,
Instance,
Parameters,
ParamSpecFlavor,
ParamSpecType,
PartialType,
ProperType,
Type,
TypeAliasType,
TypeVarId,
TypeVarLikeType,
TypeVarTupleType,
TypeVarType,
UninhabitedType,
UnpackType,
get_proper_type,
remove_dups,
)


Expand Down Expand Up @@ -93,8 +101,7 @@ def apply_generic_arguments(
bound or constraints, instead of giving an error.
"""
tvars = callable.variables
min_arg_count = sum(not tv.has_default() for tv in tvars)
assert min_arg_count <= len(orig_types) <= len(tvars)
assert len(orig_types) <= len(tvars)
# Check that inferred type variable values are compatible with allowed
# values and bounds. Also, promote subtype values to allowed values.
# Create a map from type variable id to target type.
Expand Down Expand Up @@ -148,7 +155,7 @@ def apply_generic_arguments(
type_is = None

# The callable may retain some type vars if only some were applied.
# TODO: move apply_poly() logic from checkexpr.py here when new inference
# TODO: move apply_poly() logic here when new inference
# becomes universally used (i.e. in all passes + in unification).
# With this new logic we can actually *add* some new free variables.
remaining_tvars: list[TypeVarLikeType] = []
Expand All @@ -170,3 +177,126 @@ def apply_generic_arguments(
type_guard=type_guard,
type_is=type_is,
)


def apply_poly(tp: CallableType, poly_tvars: Sequence[TypeVarLikeType]) -> CallableType | None:
"""Make free type variables generic in the type if possible.
This will translate the type `tp` while trying to create valid bindings for
type variables `poly_tvars` while traversing the type. This follows the same rules
as we do during semantic analysis phase, examples:
* Callable[Callable[[T], T], T] -> def [T] (def (T) -> T) -> T
* Callable[[], Callable[[T], T]] -> def () -> def [T] (T -> T)
* List[T] -> None (not possible)
"""
try:
return tp.copy_modified(
arg_types=[t.accept(PolyTranslator(poly_tvars)) for t in tp.arg_types],
ret_type=tp.ret_type.accept(PolyTranslator(poly_tvars)),
variables=[],
)
except PolyTranslationError:
return None


class PolyTranslationError(Exception):
pass


class PolyTranslator(TypeTranslator):
"""Make free type variables generic in the type if possible.
See docstring for apply_poly() for details.
"""

def __init__(
self,
poly_tvars: Iterable[TypeVarLikeType],
bound_tvars: frozenset[TypeVarLikeType] = frozenset(),
seen_aliases: frozenset[TypeInfo] = frozenset(),
) -> None:
self.poly_tvars = set(poly_tvars)
# This is a simplified version of TypeVarScope used during semantic analysis.
self.bound_tvars = bound_tvars
self.seen_aliases = seen_aliases

def collect_vars(self, t: CallableType | Parameters) -> list[TypeVarLikeType]:
found_vars = []
for arg in t.arg_types:
for tv in get_all_type_vars(arg):
if isinstance(tv, ParamSpecType):
normalized: TypeVarLikeType = tv.copy_modified(
flavor=ParamSpecFlavor.BARE, prefix=Parameters([], [], [])
)
else:
normalized = tv
if normalized in self.poly_tvars and normalized not in self.bound_tvars:
found_vars.append(normalized)
return remove_dups(found_vars)

def visit_callable_type(self, t: CallableType) -> Type:
found_vars = self.collect_vars(t)
self.bound_tvars |= set(found_vars)
result = super().visit_callable_type(t)
self.bound_tvars -= set(found_vars)

assert isinstance(result, ProperType) and isinstance(result, CallableType)
result.variables = list(result.variables) + found_vars
return result

def visit_type_var(self, t: TypeVarType) -> Type:
if t in self.poly_tvars and t not in self.bound_tvars:
raise PolyTranslationError()
return super().visit_type_var(t)

def visit_param_spec(self, t: ParamSpecType) -> Type:
if t in self.poly_tvars and t not in self.bound_tvars:
raise PolyTranslationError()
return super().visit_param_spec(t)

def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type:
if t in self.poly_tvars and t not in self.bound_tvars:
raise PolyTranslationError()
return super().visit_type_var_tuple(t)

def visit_type_alias_type(self, t: TypeAliasType) -> Type:
if not t.args:
return t.copy_modified()
if not t.is_recursive:
return get_proper_type(t).accept(self)
# We can't handle polymorphic application for recursive generic aliases
# without risking an infinite recursion, just give up for now.
raise PolyTranslationError()

def visit_instance(self, t: Instance) -> Type:
if t.type.has_param_spec_type:
# We need this special-casing to preserve the possibility to store a
# generic function in an instance type. Things like
# forall T . Foo[[x: T], T]
# are not really expressible in current type system, but this looks like
# a useful feature, so let's keep it.
param_spec_index = next(
i for (i, tv) in enumerate(t.type.defn.type_vars) if isinstance(tv, ParamSpecType)
)
p = get_proper_type(t.args[param_spec_index])
if isinstance(p, Parameters):
found_vars = self.collect_vars(p)
self.bound_tvars |= set(found_vars)
new_args = [a.accept(self) for a in t.args]
self.bound_tvars -= set(found_vars)

repl = new_args[param_spec_index]
assert isinstance(repl, ProperType) and isinstance(repl, Parameters)
repl.variables = list(repl.variables) + list(found_vars)
return t.copy_modified(args=new_args)
# There is the same problem with callback protocols as with aliases
# (callback protocols are essentially more flexible aliases to callables).
if t.args and t.type.is_protocol and t.type.protocol_members == ["__call__"]:
if t.type in self.seen_aliases:
raise PolyTranslationError()
call = mypy.subtypes.find_member("__call__", t, t, is_operator=True)
assert call is not None
return call.accept(
PolyTranslator(self.poly_tvars, self.bound_tvars, self.seen_aliases | {t.type})
)
return super().visit_instance(t)
7 changes: 5 additions & 2 deletions mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -3467,8 +3467,11 @@ def process_stale_scc(graph: Graph, scc: list[str], manager: BuildManager) -> No
for id in stale:
graph[id].transitive_error = True
for id in stale:
errors = manager.errors.file_messages(graph[id].xpath, formatter=manager.error_formatter)
manager.flush_errors(manager.errors.simplify_path(graph[id].xpath), errors, False)
if graph[id].xpath not in manager.errors.ignored_files:
errors = manager.errors.file_messages(
graph[id].xpath, formatter=manager.error_formatter
)
manager.flush_errors(manager.errors.simplify_path(graph[id].xpath), errors, False)
graph[id].write_cache()
graph[id].mark_as_rechecked()

Expand Down
Loading

0 comments on commit fd79c7a

Please sign in to comment.