Skip to content

plugins: delete open plugin #9275

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

Merged
merged 8 commits into from
Feb 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
61 changes: 2 additions & 59 deletions mypy/plugins/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
from typing import Callable, Optional, List

from mypy import message_registry
from mypy.nodes import Expression, StrExpr, IntExpr, DictExpr, UnaryExpr
from mypy.nodes import StrExpr, IntExpr, DictExpr, UnaryExpr
from mypy.plugin import (
Plugin, FunctionContext, MethodContext, MethodSigContext, AttributeContext, ClassDefContext,
CheckerPluginInterface,
Plugin, FunctionContext, MethodContext, MethodSigContext, AttributeContext, ClassDefContext
)
from mypy.plugins.common import try_getting_str_literals
from mypy.types import (
Expand All @@ -26,8 +25,6 @@ def get_function_hook(self, fullname: str

if fullname in ('contextlib.contextmanager', 'contextlib.asynccontextmanager'):
return contextmanager_callback
elif fullname == 'builtins.open' and self.python_version[0] == 3:
return open_callback
elif fullname == 'ctypes.Array':
return ctypes.array_constructor_callback
elif fullname == 'functools.singledispatch':
Expand Down Expand Up @@ -74,8 +71,6 @@ def get_method_hook(self, fullname: str
return ctypes.array_getitem_callback
elif fullname == 'ctypes.Array.__iter__':
return ctypes.array_iter_callback
elif fullname == 'pathlib.Path.open':
return path_open_callback
elif fullname == singledispatch.SINGLEDISPATCH_REGISTER_METHOD:
return singledispatch.singledispatch_register_callback
elif fullname == singledispatch.REGISTER_CALLABLE_CALL_METHOD:
Expand Down Expand Up @@ -129,58 +124,6 @@ def get_class_decorator_hook(self, fullname: str
return None


def open_callback(ctx: FunctionContext) -> Type:
"""Infer a better return type for 'open'."""
return _analyze_open_signature(
arg_types=ctx.arg_types,
args=ctx.args,
mode_arg_index=1,
default_return_type=ctx.default_return_type,
api=ctx.api,
)


def path_open_callback(ctx: MethodContext) -> Type:
"""Infer a better return type for 'pathlib.Path.open'."""
return _analyze_open_signature(
arg_types=ctx.arg_types,
args=ctx.args,
mode_arg_index=0,
default_return_type=ctx.default_return_type,
api=ctx.api,
)


def _analyze_open_signature(arg_types: List[List[Type]],
args: List[List[Expression]],
mode_arg_index: int,
default_return_type: Type,
api: CheckerPluginInterface,
) -> Type:
"""A helper for analyzing any function that has approximately
the same signature as the builtin 'open(...)' function.

Currently, the only thing the caller can customize is the index
of the 'mode' argument. If the mode argument is omitted or is a
string literal, we refine the return type to either 'TextIO' or
'BinaryIO' as appropriate.
"""
mode = None
if not arg_types or len(arg_types[mode_arg_index]) != 1:
mode = 'r'
else:
mode_expr = args[mode_arg_index][0]
if isinstance(mode_expr, StrExpr):
mode = mode_expr.value
if mode is not None:
assert isinstance(default_return_type, Instance) # type: ignore
if 'b' in mode:
return api.named_generic_type('typing.BinaryIO', [])
else:
return api.named_generic_type('typing.TextIO', [])
return default_return_type


def contextmanager_callback(ctx: FunctionContext) -> Type:
"""Infer a better return type for 'contextlib.contextmanager'."""
# Be defensive, just in case.
Expand Down
4 changes: 2 additions & 2 deletions mypy/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,10 @@ def _write_out_report(self,
f.write(separator + '\n')
for row_values in rows:
r = ("{:>{}}" * len(widths)).format(*itertools.chain(*zip(row_values, widths)))
f.writelines(r + '\n')
f.write(r + '\n')
Copy link
Member

Choose a reason for hiding this comment

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

What made this change necessary?

Copy link
Member

Choose a reason for hiding this comment

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

That looks like it fixes a bug! writelines takes a sequence of strings, not a single string (though because a string is a sequence of strings and writelines just writes the concatenation of the items it worked :-).

Though I'm not sure how the change found this bug -- does the TextIoWriter class (or whatever is used) not have writelines?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We now infer f as TextIOWrapper. TextIOWrapper and TextIOBase take List[str] for writelines in typeshed. Maybe that signature should be loosened though.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I think that should be Iterable[str]. I just tried in 3.6 and TextIOWrapper.writelines accepts at least tuples and generator expressions.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

And it looks like the perennial "str is a iterable of str" bug is benign in this case, so there's not really any downside to the looser signature.

f.write(separator + '\n')
footer_str = ("{:>{}}" * len(widths)).format(*itertools.chain(*zip(footer, widths)))
f.writelines(footer_str + '\n')
f.write(footer_str + '\n')

def _report_any_exprs(self) -> None:
total_any = sum(num_any for num_any, _ in self.counts.values())
Expand Down
4 changes: 2 additions & 2 deletions mypyc/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,8 @@ def write_file(path: str, contents: str) -> None:
old_contents = None
if old_contents != encoded_contents:
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, 'wb') as f:
f.write(encoded_contents)
with open(path, 'wb') as g:
g.write(encoded_contents)

# Fudge the mtime forward because otherwise when two builds happen close
# together (like in a test) setuptools might not realize the source is newer
Expand Down
30 changes: 15 additions & 15 deletions test-data/unit/pythoneval.test
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,8 @@ f.write('x')
f.write(b'x')
f.foobar()
[out]
_program.py:3: error: Argument 1 to "write" of "IO" has incompatible type "bytes"; expected "str"
_program.py:4: error: "TextIO" has no attribute "foobar"
_program.py:3: error: Argument 1 to "write" of "TextIOBase" has incompatible type "bytes"; expected "str"
_program.py:4: error: "TextIOWrapper" has no attribute "foobar"

[case testOpenReturnTypeInference]
reveal_type(open('x'))
Expand All @@ -281,9 +281,9 @@ reveal_type(open('x', 'rb'))
mode = 'rb'
reveal_type(open('x', mode))
[out]
_program.py:1: note: Revealed type is "typing.TextIO"
_program.py:2: note: Revealed type is "typing.TextIO"
_program.py:3: note: Revealed type is "typing.BinaryIO"
_program.py:1: note: Revealed type is "io.TextIOWrapper"
_program.py:2: note: Revealed type is "io.TextIOWrapper"
_program.py:3: note: Revealed type is "io.BufferedReader"
_program.py:5: note: Revealed type is "typing.IO[Any]"

[case testOpenReturnTypeInferenceSpecialCases]
Expand All @@ -292,8 +292,8 @@ reveal_type(open(file='x', mode='rb'))
mode = 'rb'
reveal_type(open(mode=mode, file='r'))
[out]
_testOpenReturnTypeInferenceSpecialCases.py:1: note: Revealed type is "typing.BinaryIO"
_testOpenReturnTypeInferenceSpecialCases.py:2: note: Revealed type is "typing.BinaryIO"
_testOpenReturnTypeInferenceSpecialCases.py:1: note: Revealed type is "io.BufferedReader"
_testOpenReturnTypeInferenceSpecialCases.py:2: note: Revealed type is "io.BufferedReader"
_testOpenReturnTypeInferenceSpecialCases.py:4: note: Revealed type is "typing.IO[Any]"

[case testPathOpenReturnTypeInference]
Expand All @@ -305,21 +305,21 @@ reveal_type(p.open('rb'))
mode = 'rb'
reveal_type(p.open(mode))
[out]
_program.py:3: note: Revealed type is "typing.TextIO"
_program.py:4: note: Revealed type is "typing.TextIO"
_program.py:5: note: Revealed type is "typing.BinaryIO"
_program.py:3: note: Revealed type is "io.TextIOWrapper"
_program.py:4: note: Revealed type is "io.TextIOWrapper"
_program.py:5: note: Revealed type is "io.BufferedReader"
_program.py:7: note: Revealed type is "typing.IO[Any]"

[case testPathOpenReturnTypeInferenceSpecialCases]
from pathlib import Path
p = Path("x")
reveal_type(p.open(mode='rb', errors='replace'))
reveal_type(p.open(errors='replace', mode='rb'))
mode = 'rb'
reveal_type(p.open(mode='r', errors='replace'))
reveal_type(p.open(errors='replace', mode='r'))
mode = 'r'
reveal_type(p.open(mode=mode, errors='replace'))
[out]
_program.py:3: note: Revealed type is "typing.BinaryIO"
_program.py:4: note: Revealed type is "typing.BinaryIO"
_program.py:3: note: Revealed type is "io.TextIOWrapper"
_program.py:4: note: Revealed type is "io.TextIOWrapper"
_program.py:6: note: Revealed type is "typing.IO[Any]"

[case testGenericPatterns]
Expand Down