-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Expand file tree
/
Copy pathparse.py
More file actions
135 lines (112 loc) · 4.78 KB
/
Copy pathparse.py
File metadata and controls
135 lines (112 loc) · 4.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
from __future__ import annotations
import re
from librt.internal import ReadBuffer
from mypy import errorcodes as codes
from mypy.cache import read_int
from mypy.errors import Errors
from mypy.nodes import FileRawData, MypyFile, ParseError, StrExpr
from mypy.options import Options
from mypy.types import ProperType
def parse(
source: str | bytes | None,
fnam: str,
module: str | None,
errors: Errors,
options: Options,
eager: bool = False,
) -> MypyFile:
"""Parse a source file, without doing any semantic analysis.
Return the parse tree, use the errors object to report parse errors.
The python_version (major, minor) option determines the Python syntax variant.
New parser returns empty tree with serialized data. To get the full tree and
the parse errors, use eager=True.
`source` must not be `None` if the old parser is used. The new parser will read and
parse contents from path `fnam` if `source` is `None`.
"""
if options.native_parser:
import mypy.nativeparse
ignore_errors = options.ignore_errors or 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 not options.preserve_asts
tree, _, _ = mypy.nativeparse.native_parse(
fnam, options, source, skip_function_bodies=strip_function_bodies
)
# Set is_stub based on file extension
tree.is_stub = fnam.endswith(".pyi")
# Note: tree.imports is populated directly by load_from_raw() with deserialized
# import metadata, so we don't need to collect imports via AST traversal
if eager and tree.raw_data is not None:
tree = load_from_raw(fnam, module, tree.raw_data, errors, options)
return tree
if source is None:
raise ValueError("Source cannot be `None` when using the old parser")
if options.transform_source is not None:
source = options.transform_source(source)
import mypy.fastparse
return mypy.fastparse.parse(source, fnam=fnam, module=module, errors=errors, options=options)
def load_from_raw(
fnam: str,
module: str | None,
raw_data: FileRawData,
errors: Errors,
options: Options,
imports_only: bool = False,
) -> MypyFile:
"""Load AST from parsed binary data and report stored errors.
If imports_only is true, only deserialize imports and return a mostly
empty AST.
"""
from mypy.nativeparse import State, deserialize_imports, read_statements
state = State(options, is_stub=fnam.endswith(".pyi"))
if imports_only:
defs = []
else:
data = ReadBuffer(raw_data.defs)
n = read_int(data)
defs = read_statements(state, data, n)
imports = deserialize_imports(raw_data.imports)
tree = MypyFile(defs, imports)
tree.path = fnam
tree.ignored_lines = raw_data.ignored_lines
tree.is_partial_stub_package = raw_data.is_partial_stub_package
tree.uses_template_strings = raw_data.uses_template_strings
tree.is_stub = fnam.endswith(".pyi")
if module is not None:
tree._fullname = module
# Report parse errors, this replicates the logic in parse().
all_errors = raw_data.raw_errors + state.errors
errors.set_file(fnam, module, options=options)
for error in all_errors:
# Note we never raise in this function, so it should not be called in coordinator.
report_parse_error(error, errors)
if imports_only:
# Preserve raw data when only de-serializing imports, it will be sent to
# the parallel workers.
tree.raw_data = raw_data
return tree
def report_parse_error(error: ParseError, errors: Errors) -> None:
message = error["message"]
# Standardize error message by capitalizing the first word
message = re.sub(r"^(\s*\w)", lambda m: m.group(1).upper(), message)
# Respect blocker status from error, default to True for syntax errors
is_blocker = error.get("blocker", True)
error_code = error.get("code")
if error_code is None:
error_code = codes.SYNTAX
else:
# Fallback to [syntax] for backwards compatibility.
error_code = codes.error_codes.get(error_code) or codes.SYNTAX
errors.report(error["line"], error["column"], message, blocker=is_blocker, code=error_code)
def parse_type_string(expr: StrExpr, options: Options) -> ProperType:
if options.native_parser:
import mypy.nativeparse
return mypy.nativeparse.native_parse_type_string(
expr.value,
expr.line,
expr.column,
expr.end_line or expr.line,
expr.end_column or expr.column,
options,
)
import mypy.fastparse
return mypy.fastparse.parse_type_string(expr.value, "builtins.str", expr.line, expr.column)