Repository navigation
Expand file tree
/
Copy pathparse.py
More file actions
140 lines (117 loc) · 5.19 KB
/
Copy pathparse.py
File metadata and controls
140 lines (117 loc) · 5.19 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
136
137
138
139
140
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:
# Use import from as workaround for cpython#149728 - accessing mypy.nativeparse
# after importing it may fail when this function is called concurrently.
from mypy import 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, _, _ = 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)
errors.set_file_ignored_lines(fnam, tree.ignored_lines, options.ignore_errors)
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:
# Use a import from as workaround for cpython#149728 - accessing mypy.nativeparse
# after importing it may fail when this function is called concurrently.
from mypy import nativeparse
return 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)