Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into dont-check-generate…
Browse files Browse the repository at this point in the history
…d-functions
  • Loading branch information
ikonst committed Dec 11, 2023
2 parents fcd3e0a + 0567da9 commit 993ac57
Show file tree
Hide file tree
Showing 82 changed files with 692 additions and 254 deletions.
4 changes: 2 additions & 2 deletions misc/dump-ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import sys

from mypy import defaults
from mypy.errors import CompileError
from mypy.errors import CompileError, Errors
from mypy.options import Options
from mypy.parse import parse

Expand All @@ -19,7 +19,7 @@ def dump(fname: str, python_version: tuple[int, int], quiet: bool = False) -> No
options.python_version = python_version
with open(fname, "rb") as f:
s = f.read()
tree = parse(s, fname, None, errors=None, options=options)
tree = parse(s, fname, None, errors=Errors(options), options=options)
if not quiet:
print(tree)

Expand Down
8 changes: 4 additions & 4 deletions misc/sync-typeshed.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,10 @@ def main() -> None:
print("Created typeshed sync commit.")

commits_to_cherry_pick = [
"9859fe7ba", # LiteralString reverts
"378a866e9", # sum reverts
"2816b97d5", # ctypes reverts
"7d987a105", # ParamSpec for functools.wraps
"588623ff2", # LiteralString reverts
"bdcc90e85", # sum reverts
"3e5d81337", # ctypes reverts
"344298e3a", # ParamSpec for functools.wraps
]
for commit in commits_to_cherry_pick:
try:
Expand Down
2 changes: 1 addition & 1 deletion misc/test-stubgenc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function stubgenc_test() {
# Compare generated stubs to expected ones
if ! git diff --exit-code "$STUBGEN_OUTPUT_FOLDER";
then
EXIT=$?
EXIT=1
fi
}

Expand Down
4 changes: 2 additions & 2 deletions mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -2176,8 +2176,8 @@ def parse_file(self, *, temporary: bool = False) -> None:
self.id,
self.xpath,
source,
self.ignore_all or self.options.ignore_errors,
self.options,
ignore_errors=self.ignore_all or self.options.ignore_errors,
options=self.options,
)

else:
Expand Down
13 changes: 11 additions & 2 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -5257,6 +5257,15 @@ def _make_fake_typeinfo_and_full_name(
pretty_names_list = pretty_seq(
format_type_distinctly(*base_classes, options=self.options, bare=True), "and"
)

new_errors = []
for base in base_classes:
if base.type.is_final:
new_errors.append((pretty_names_list, f'"{base.type.name}" is final'))
if new_errors:
errors.extend(new_errors)
return None

try:
info, full_name = _make_fake_typeinfo_and_full_name(base_classes, curr_module)
with self.msg.filter_errors() as local_errors:
Expand All @@ -5269,10 +5278,10 @@ def _make_fake_typeinfo_and_full_name(
self.check_multiple_inheritance(info)
info.is_intersection = True
except MroError:
errors.append((pretty_names_list, "inconsistent method resolution order"))
errors.append((pretty_names_list, "would have inconsistent method resolution order"))
return None
if local_errors.has_new_errors():
errors.append((pretty_names_list, "incompatible method signatures"))
errors.append((pretty_names_list, "would have incompatible method signatures"))
return None

curr_module.names[full_name] = SymbolTableNode(GDEF, info)
Expand Down
31 changes: 16 additions & 15 deletions mypy/fastparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ def parse(
source: str | bytes,
fnam: str,
module: str | None,
errors: Errors | None = None,
errors: Errors,
options: Options | None = None,
) -> MypyFile:
"""Parse a source file, without doing any semantic analysis.
Expand All @@ -199,16 +199,13 @@ def parse(
on failure. Otherwise, use the errors object to report parse errors.
"""
ignore_errors = (options is not None and options.ignore_errors) or (
errors is not None and fnam in errors.ignored_files
fnam in errors.ignored_files
)
# If errors are ignored, we can drop many function bodies to speed up type checking.
strip_function_bodies = ignore_errors and (options is None or not options.preserve_asts)
raise_on_error = False

if options is None:
options = Options()
if errors is None:
errors = Errors(options)
raise_on_error = True
errors.set_file(fnam, module, options=options)
is_stub_file = fnam.endswith(".pyi")
if is_stub_file:
Expand All @@ -228,11 +225,9 @@ def parse(
options=options,
is_stub=is_stub_file,
errors=errors,
ignore_errors=ignore_errors,
strip_function_bodies=strip_function_bodies,
path=fnam,
).visit(ast)
tree.path = fnam
tree.is_stub = is_stub_file
except SyntaxError as e:
# alias to please mypyc
is_py38_or_earlier = sys.version_info < (3, 9)
Expand All @@ -254,9 +249,6 @@ def parse(
)
tree = MypyFile([], [], False, {})

if raise_on_error and errors.is_errors():
errors.raise_error()

assert isinstance(tree, MypyFile)
return tree

Expand Down Expand Up @@ -357,8 +349,8 @@ def __init__(
is_stub: bool,
errors: Errors,
*,
ignore_errors: bool,
strip_function_bodies: bool,
path: str,
) -> None:
# 'C' for class, 'D' for function signature, 'F' for function, 'L' for lambda
self.class_and_function_stack: list[Literal["C", "D", "F", "L"]] = []
Expand All @@ -367,8 +359,8 @@ def __init__(
self.options = options
self.is_stub = is_stub
self.errors = errors
self.ignore_errors = ignore_errors
self.strip_function_bodies = strip_function_bodies
self.path = path

self.type_ignores: dict[int, list[str]] = {}

Expand All @@ -380,6 +372,10 @@ def note(self, msg: str, line: int, column: int) -> None:

def fail(self, msg: ErrorMessage, line: int, column: int, blocker: bool = True) -> None:
if blocker or not self.options.ignore_errors:
# Make sure self.errors reflects any type ignores that we have parsed
self.errors.set_file_ignored_lines(
self.path, self.type_ignores, self.options.ignore_errors
)
self.errors.report(line, column, msg.value, blocker=blocker, code=msg.code)

def fail_merge_overload(self, node: IfStmt) -> None:
Expand Down Expand Up @@ -858,8 +854,13 @@ def visit_Module(self, mod: ast3.Module) -> MypyFile:
self.type_ignores[ti.lineno] = parsed
else:
self.fail(message_registry.INVALID_TYPE_IGNORE, ti.lineno, -1, blocker=False)

body = self.fix_function_overloads(self.translate_stmt_list(mod.body, ismodule=True))
return MypyFile(body, self.imports, False, self.type_ignores)

ret = MypyFile(body, self.imports, False, ignored_lines=self.type_ignores)
ret.is_stub = self.is_stub
ret.path = self.path
return ret

# --- stmt ---
# FunctionDef(identifier name, arguments args,
Expand Down
2 changes: 1 addition & 1 deletion mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -2051,7 +2051,7 @@ def redundant_expr(self, description: str, truthiness: bool, context: Context) -
def impossible_intersection(
self, formatted_base_class_list: str, reason: str, context: Context
) -> None:
template = "Subclass of {} cannot exist: would have {}"
template = "Subclass of {} cannot exist: {}"
self.fail(
template.format(formatted_base_class_list, reason), context, code=codes.UNREACHABLE
)
Expand Down
12 changes: 10 additions & 2 deletions mypy/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@


def parse(
source: str | bytes, fnam: str, module: str | None, errors: Errors | None, options: Options
source: str | bytes,
fnam: str,
module: str | None,
errors: Errors,
options: Options,
raise_on_error: bool = False,
) -> MypyFile:
"""Parse a source file, without doing any semantic analysis.
Expand All @@ -19,4 +24,7 @@ def parse(
source = options.transform_source(source)
import mypy.fastparse

return mypy.fastparse.parse(source, fnam=fnam, module=module, errors=errors, options=options)
tree = mypy.fastparse.parse(source, fnam=fnam, module=module, errors=errors, options=options)
if raise_on_error and errors.is_errors():
errors.raise_error()
return tree
24 changes: 21 additions & 3 deletions mypy/plugins/attrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class Attribute:
def __init__(
self,
name: str,
alias: str | None,
info: TypeInfo,
has_default: bool,
init: bool,
Expand All @@ -114,6 +115,7 @@ def __init__(
init_type: Type | None,
) -> None:
self.name = name
self.alias = alias
self.info = info
self.has_default = has_default
self.init = init
Expand Down Expand Up @@ -171,12 +173,14 @@ def argument(self, ctx: mypy.plugin.ClassDefContext) -> Argument:
arg_kind = ARG_OPT if self.has_default else ARG_POS

# Attrs removes leading underscores when creating the __init__ arguments.
return Argument(Var(self.name.lstrip("_"), init_type), init_type, None, arg_kind)
name = self.alias or self.name.lstrip("_")
return Argument(Var(name, init_type), init_type, None, arg_kind)

def serialize(self) -> JsonDict:
"""Serialize this object so it can be saved and restored."""
return {
"name": self.name,
"alias": self.alias,
"has_default": self.has_default,
"init": self.init,
"kw_only": self.kw_only,
Expand Down Expand Up @@ -205,6 +209,7 @@ def deserialize(

return Attribute(
data["name"],
data["alias"],
info,
data["has_default"],
data["init"],
Expand Down Expand Up @@ -498,6 +503,7 @@ def _attributes_from_assignment(
or if auto_attribs is enabled also like this:
x: type
x: type = default_value
x: type = attr.ib(...)
"""
for lvalue in stmt.lvalues:
lvalues, rvalues = _parse_assignments(lvalue, stmt)
Expand Down Expand Up @@ -564,7 +570,7 @@ def _attribute_from_auto_attrib(
has_rhs = not isinstance(rvalue, TempNode)
sym = ctx.cls.info.names.get(name)
init_type = sym.type if sym else None
return Attribute(name, ctx.cls.info, has_rhs, True, kw_only, None, stmt, init_type)
return Attribute(name, None, ctx.cls.info, has_rhs, True, kw_only, None, stmt, init_type)


def _attribute_from_attrib_maker(
Expand Down Expand Up @@ -628,13 +634,25 @@ def _attribute_from_attrib_maker(
converter = convert
converter_info = _parse_converter(ctx, converter)

# Custom alias might be defined:
alias = None
alias_expr = _get_argument(rvalue, "alias")
if alias_expr:
alias = ctx.api.parse_str_literal(alias_expr)
if alias is None:
ctx.api.fail(
'"alias" argument to attrs field must be a string literal',
rvalue,
code=LITERAL_REQ,
)

if init_type is None and ctx.api.options.disallow_untyped_defs:
assert lhs.node is not None
ctx.api.msg.need_annotation_for_var(lhs.node, stmt)

name = unmangle(lhs.name)
return Attribute(
name, ctx.cls.info, attr_has_default, init, kw_only, converter_info, stmt, init_type
name, alias, ctx.cls.info, attr_has_default, init, kw_only, converter_info, stmt, init_type
)


Expand Down
6 changes: 3 additions & 3 deletions mypy/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class AbstractReporter(metaclass=ABCMeta):
def __init__(self, reports: Reports, output_dir: str) -> None:
self.output_dir = output_dir
if output_dir != "<memory>":
stats.ensure_dir_exists(output_dir)
os.makedirs(output_dir, exist_ok=True)

@abstractmethod
def on_file(
Expand Down Expand Up @@ -737,7 +737,7 @@ def on_file(
if path.startswith(".."):
return
out_path = os.path.join(self.output_dir, "xml", path + ".xml")
stats.ensure_dir_exists(os.path.dirname(out_path))
os.makedirs(os.path.dirname(out_path), exist_ok=True)
last_xml.write(out_path, encoding="utf-8")

def on_finish(self) -> None:
Expand Down Expand Up @@ -782,7 +782,7 @@ def on_file(
if path.startswith(".."):
return
out_path = os.path.join(self.output_dir, "html", path + ".html")
stats.ensure_dir_exists(os.path.dirname(out_path))
os.makedirs(os.path.dirname(out_path), exist_ok=True)
transformed_html = bytes(self.xslt_html(last_xml, ext=self.param_html))
with open(out_path, "wb") as out_file:
out_file.write(transformed_html)
Expand Down
5 changes: 0 additions & 5 deletions mypy/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,11 +477,6 @@ def is_complex(t: Type) -> bool:
return is_generic(t) or isinstance(t, (FunctionLike, TupleType, TypeVarType))


def ensure_dir_exists(dir: str) -> None:
if not os.path.exists(dir):
os.makedirs(dir)


def is_special_form_any(t: AnyType) -> bool:
return get_original_any(t).type_of_any == TypeOfAny.special_form

Expand Down
3 changes: 2 additions & 1 deletion mypy/stubdoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,8 @@ def infer_ret_type_sig_from_docstring(docstr: str, name: str) -> str | None:

def infer_ret_type_sig_from_anon_docstring(docstr: str) -> str | None:
"""Convert signature in form of "(self: TestClass, arg0) -> int" to their return type."""
return infer_ret_type_sig_from_docstring("stub" + docstr.strip(), "stub")
lines = ["stub" + line.strip() for line in docstr.splitlines() if line.strip().startswith("(")]
return infer_ret_type_sig_from_docstring("".join(lines), "stub")


def parse_signature(sig: str) -> tuple[str, list[str], list[str]] | None:
Expand Down
4 changes: 2 additions & 2 deletions mypy/stubgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -1700,6 +1700,7 @@ def generate_stubs(options: Options) -> None:
doc_dir=options.doc_dir,
include_private=options.include_private,
export_less=options.export_less,
include_docstrings=options.include_docstrings,
)
num_modules = len(all_modules)
if not options.quiet and num_modules > 0:
Expand Down Expand Up @@ -1832,8 +1833,7 @@ def parse_options(args: list[str]) -> Options:
parser.error("Cannot specify both --parse-only/--no-analysis and --inspect-mode")

# Create the output folder if it doesn't already exist.
if not os.path.exists(ns.output_dir):
os.makedirs(ns.output_dir)
os.makedirs(ns.output_dir, exist_ok=True)

return Options(
pyversion=pyversion,
Expand Down
Loading

0 comments on commit 993ac57

Please sign in to comment.