Skip to content

Commit 00ae328

Browse files
committed
Add test cases
1 parent 4f1d0cd commit 00ae328

File tree

3 files changed

+217
-13
lines changed

3 files changed

+217
-13
lines changed

mypy/exportjson.py

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,24 +67,29 @@
6767
JsonDict: _TypeAlias = dict[str, Any]
6868

6969

70-
def convert_binary_cache_to_json(data: bytes) -> JsonDict:
70+
class Config:
71+
def __init__(self, *, implicit_names: bool = True) -> None:
72+
self.implicit_names = implicit_names
73+
74+
75+
def convert_binary_cache_to_json(data: bytes, *, implicit_names: bool = True) -> JsonDict:
7176
tree = MypyFile.read(Buffer(data))
72-
return convert_mypy_file_to_json(tree)
77+
return convert_mypy_file_to_json(tree, Config(implicit_names=implicit_names))
7378

7479

75-
def convert_mypy_file_to_json(self: MypyFile) -> JsonDict:
80+
def convert_mypy_file_to_json(self: MypyFile, cfg: Config) -> JsonDict:
7681
return {
7782
".class": "MypyFile",
7883
"_fullname": self._fullname,
79-
"names": convert_symbol_table(self.names, self._fullname),
84+
"names": convert_symbol_table(self.names, cfg),
8085
"is_stub": self.is_stub,
8186
"path": self.path,
8287
"is_partial_stub_package": self.is_partial_stub_package,
8388
"future_import_flags": sorted(self.future_import_flags),
8489
}
8590

8691

87-
def convert_symbol_table(self: SymbolTable, fullname: str) -> JsonDict:
92+
def convert_symbol_table(self: SymbolTable, cfg: Config) -> JsonDict:
8893
data: JsonDict = {".class": "SymbolTable"}
8994
for key, value in self.items():
9095
# Skip __builtins__: it's a reference to the builtins
@@ -93,11 +98,20 @@ def convert_symbol_table(self: SymbolTable, fullname: str) -> JsonDict:
9398
# accessed by users of the module.
9499
if key == "__builtins__" or value.no_serialize:
95100
continue
96-
data[key] = convert_symbol_table_node(value, fullname, key)
101+
if not cfg.implicit_names and key in {
102+
"__spec__",
103+
"__package__",
104+
"__file__",
105+
"__doc__",
106+
"__annotations__",
107+
"__name__",
108+
}:
109+
continue
110+
data[key] = convert_symbol_table_node(value, cfg)
97111
return data
98112

99113

100-
def convert_symbol_table_node(self: SymbolTableNode, prefix: str | None, name: str) -> JsonDict:
114+
def convert_symbol_table_node(self: SymbolTableNode, cfg: Config) -> JsonDict:
101115
data: JsonDict = {".class": "SymbolTableNode", "kind": node_kinds[self.kind]}
102116
if self.module_hidden:
103117
data["module_hidden"] = True
@@ -110,11 +124,11 @@ def convert_symbol_table_node(self: SymbolTableNode, prefix: str | None, name: s
110124
if self.cross_ref:
111125
data["cross_ref"] = self.cross_ref
112126
elif self.node is not None:
113-
data["node"] = convert_symbol_node(self.node)
127+
data["node"] = convert_symbol_node(self.node, cfg)
114128
return data
115129

116130

117-
def convert_symbol_node(self: SymbolNode) -> JsonDict:
131+
def convert_symbol_node(self: SymbolNode, cfg: Config) -> JsonDict:
118132
if isinstance(self, FuncDef):
119133
return convert_func_def(self)
120134
elif isinstance(self, OverloadedFuncDef):
@@ -124,7 +138,7 @@ def convert_symbol_node(self: SymbolNode) -> JsonDict:
124138
elif isinstance(self, Var):
125139
return convert_var(self)
126140
elif isinstance(self, TypeInfo):
127-
return convert_type_info(self)
141+
return convert_type_info(self, cfg)
128142
elif isinstance(self, TypeAlias):
129143
return convert_type_alias(self)
130144
elif isinstance(self, TypeVarExpr):
@@ -210,12 +224,12 @@ def convert_var(self: Var) -> JsonDict:
210224
return data
211225

212226

213-
def convert_type_info(self: TypeInfo) -> JsonDict:
227+
def convert_type_info(self: TypeInfo, cfg: Config) -> JsonDict:
214228
data = {
215229
".class": "TypeInfo",
216230
"module_name": self.module_name,
217231
"fullname": self.fullname,
218-
"names": convert_symbol_table(self.names, self.fullname),
232+
"names": convert_symbol_table(self.names, cfg),
219233
"defn": convert_class_def(self.defn),
220234
"abstract_attributes": self.abstract_attributes,
221235
"type_vars": self.type_vars,
@@ -250,7 +264,12 @@ def convert_type_info(self: TypeInfo) -> JsonDict:
250264

251265

252266
def convert_class_def(self: ClassDef) -> JsonDict:
253-
return {}
267+
return {
268+
".class": "ClassDef",
269+
"name": self.name,
270+
"fullname": self.fullname,
271+
"type_vars": [convert_type(v) for v in self.type_vars],
272+
}
254273

255274

256275
def convert_type_alias(self: TypeAlias) -> JsonDict:

mypy/test/testexportjson.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""Test cases for the mypy cache JSON export tool."""
2+
3+
from __future__ import annotations
4+
5+
import os
6+
import json
7+
import re
8+
import pprint
9+
import sys
10+
11+
from mypy import build
12+
from mypy.errors import CompileError
13+
from mypy.modulefinder import BuildSource
14+
from mypy.options import Options
15+
from mypy.test.config import test_temp_dir
16+
from mypy.test.data import DataDrivenTestCase, DataSuite
17+
from mypy.test.helpers import assert_string_arrays_equal
18+
from mypy.exportjson import convert_binary_cache_to_json
19+
20+
21+
class TypeExportSuite(DataSuite):
22+
required_out_section = True
23+
files = ["exportjson.test"]
24+
25+
def run_case(self, testcase: DataDrivenTestCase) -> None:
26+
try:
27+
src = "\n".join(testcase.input)
28+
options = Options()
29+
options.use_builtins_fixtures = True
30+
options.show_traceback = True
31+
options.allow_empty_bodies = True
32+
options.fixed_format_cache = True
33+
result = build.build(
34+
sources=[BuildSource("main", None, src)],
35+
options=options,
36+
alt_lib_path=test_temp_dir,
37+
)
38+
a = result.errors
39+
40+
major, minor = sys.version_info[:2]
41+
cache_dir = os.path.join(".mypy_cache", f"{major}.{minor}")
42+
43+
for module in result.files:
44+
if module in ("builtins", "typing", "_typeshed", "__main__"):
45+
continue
46+
fnam = os.path.join(cache_dir, f"{module}.data.ff")
47+
with open(fnam, "rb") as f:
48+
json_data = convert_binary_cache_to_json(f.read(), implicit_names=False)
49+
for line in json.dumps(json_data, indent=4).splitlines():
50+
if '"path": ' in line:
51+
# We source file path is unpredictable, so filter it out
52+
line = re.sub(r'"[^"]+\.pyi?"', "...", line)
53+
a.append(line)
54+
print(fnam)
55+
except CompileError as e:
56+
a = e.messages
57+
assert_string_arrays_equal(
58+
testcase.output,
59+
a,
60+
f"Invalid output ({testcase.file}, line {testcase.line})",
61+
)

test-data/unit/exportjson.test

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
-- Test cases for exporting mypy cache files to JSON (mypy.exportjson).
2+
--
3+
-- The tool is maintained on a best effort basis so we don't attempt to have
4+
-- full test coverage.
5+
6+
[case testExportVar]
7+
import m
8+
9+
[file m.py]
10+
x = 0
11+
12+
[out]
13+
{
14+
".class": "MypyFile",
15+
"_fullname": "m",
16+
"names": {
17+
".class": "SymbolTable",
18+
"x": {
19+
".class": "SymbolTableNode",
20+
"kind": "Gdef",
21+
"node": {
22+
".class": "Var",
23+
"name": "x",
24+
"fullname": "m.x",
25+
"type": {
26+
".class": "Instance",
27+
"type_ref": null,
28+
"args": []
29+
},
30+
"setter_type": null,
31+
"flags": [
32+
"is_ready",
33+
"is_inferred",
34+
"has_explicit_value"
35+
]
36+
}
37+
}
38+
},
39+
"is_stub": false,
40+
"path": ...,
41+
"is_partial_stub_package": false,
42+
"future_import_flags": []
43+
}
44+
45+
[case testExportClass]
46+
import m
47+
48+
[file m.py]
49+
class C:
50+
x: int
51+
52+
[out]
53+
{
54+
".class": "MypyFile",
55+
"_fullname": "m",
56+
"names": {
57+
".class": "SymbolTable",
58+
"C": {
59+
".class": "SymbolTableNode",
60+
"kind": "Gdef",
61+
"node": {
62+
".class": "TypeInfo",
63+
"module_name": "m",
64+
"fullname": "m.C",
65+
"names": {
66+
".class": "SymbolTable",
67+
"x": {
68+
".class": "SymbolTableNode",
69+
"kind": "Mdef",
70+
"node": {
71+
".class": "Var",
72+
"name": "x",
73+
"fullname": "m.C.x",
74+
"type": {
75+
".class": "Instance",
76+
"type_ref": null,
77+
"args": []
78+
},
79+
"setter_type": null,
80+
"flags": [
81+
"is_initialized_in_class",
82+
"is_ready"
83+
]
84+
}
85+
}
86+
},
87+
"defn": {
88+
".class": "ClassDef",
89+
"name": "C",
90+
"fullname": "m.C",
91+
"type_vars": []
92+
},
93+
"abstract_attributes": [],
94+
"type_vars": [],
95+
"has_param_spec_type": false,
96+
"bases": [
97+
{
98+
".class": "Instance",
99+
"type_ref": "builtins.object",
100+
"args": []
101+
}
102+
],
103+
"mro": [],
104+
"_promote": [],
105+
"alt_promote": null,
106+
"declared_metaclass": null,
107+
"metaclass_type": null,
108+
"tuple_type": null,
109+
"typeddict_type": null,
110+
"flags": [],
111+
"metadata": {},
112+
"slots": null,
113+
"deletable_attributes": [],
114+
"self_type": null,
115+
"dataclass_transform_spec": null,
116+
"deprecated": null
117+
}
118+
}
119+
},
120+
"is_stub": false,
121+
"path": ...,
122+
"is_partial_stub_package": false,
123+
"future_import_flags": []
124+
}

0 commit comments

Comments
 (0)