Coverage for skema/program_analysis/python2cast.py: 63%
76 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-04-30 17:15 +0000
« prev ^ index » next coverage.py v7.5.0, created at 2024-04-30 17:15 +0000
1import os
2import sys
3import ast
4import json
5import argparse
6from pathlib import Path
7from tempfile import TemporaryDirectory
9from skema.program_analysis.CAST.pythonAST import py_ast_to_cast
10from skema.program_analysis.CAST2FN import cast
11from skema.program_analysis.CAST2FN.model.cast import SourceRef
12from skema.program_analysis.CAST2FN.cast import CAST
13from skema.program_analysis.CAST2FN.visitors.cast_to_agraph_visitor import (
14 CASTToAGraphVisitor,
15)
17from skema.program_analysis.CAST.python.ts2cast import TS2CAST
18from skema.program_analysis.python_preprocessor import preprocess
19from typing import Optional
21def get_args():
22 parser = argparse.ArgumentParser(
23 "Runs Python to CAST pipeline on input Python source file."
24 )
25 parser.add_argument(
26 "--rawjson",
27 help="Dumps out raw JSON contents to stdout",
28 action="store_true",
29 )
30 parser.add_argument(
31 "--stdout",
32 help="Dumps CAST JSON to stdout instead of a file",
33 action="store_true",
34 )
35 parser.add_argument(
36 "--agraph",
37 help="Generates visualization of CAST as a PDF file",
38 action="store_true",
39 )
40 parser.add_argument(
41 "--legacy",
42 help="Generate CAST for GrFN 2.2 pipeline",
43 action="store_true",
44 )
45 parser.add_argument(
46 "--ts",
47 help="Generate CAST using tree-sitter parser generator instead of the Python AST",
48 action="store_true",
49 )
50 parser.add_argument("pyfile_path", help="input Python source file")
51 options = parser.parse_args()
52 return options
55def python_to_cast(
56 pyfile_path,
57 agraph=False,
58 astprint=False,
59 std_out=False,
60 rawjson=False,
61 legacy=False,
62 cast_obj=False,
63 tree_sitter=False
64) -> Optional[CAST]:
65 """Create a CAST object from a Python file and serialize it to JSON.
67 Args:
68 pyfile_path: Path to the Python source file
69 agraph: If true, a PDF visualization of the graph is created.
70 std_out: If true, the CAST JSON is printed to stdout instead
71 of written to a file.
72 rawjson: If true, the raw JSON contents are printed to stdout.
73 legacy: If true, generate CAST for GrFN 2.2 pipeline.
74 cast_obj: If true, returns the CAST as an object instead of printing to
75 stdout.
77 Returns:
78 If cast_obj is set to True, returns the CAST as an object. Else,
79 returns None.
80 """
82 # Run the Python preprocessor to convert Python 2 to Python 3
83 source_filename = Path(pyfile_path).name
84 python2_source = Path(pyfile_path).read_text()
85 python3_source = preprocess(python2_source)
86 with TemporaryDirectory() as tmp:
87 new_path = Path(tmp) / source_filename
88 new_path.write_text(python3_source)
89 pyfile_path = str(new_path)
91 if not tree_sitter:
92 # Open Python file as a giant string
93 with open(pyfile_path) as f:
94 file_contents = f.read()
96 file_name = pyfile_path.split("/")[-1]
98 # Count the number of lines in the file
99 with open(pyfile_path) as f:
100 file_list = f.readlines()
101 line_count = len(file_list)
103 # Create a PyASTToCAST Object
104 if legacy:
105 convert = py_ast_to_cast.PyASTToCAST(file_name, legacy=True)
106 else:
107 convert = py_ast_to_cast.PyASTToCAST(file_name)
109 # 'Root' the current working directory so that it's where the
110 # Source file we're generating CAST for is (for Import statements)
111 old_path = os.getcwd()
112 try:
113 idx = pyfile_path.rfind("/")
115 if idx > -1:
116 curr_path = pyfile_path[0:idx]
117 os.chdir(curr_path)
118 else:
119 curr_path = "./" + pyfile_path
121 # Parse the Python program's AST and create the CAST
122 contents = ast.parse(file_contents)
123 C = convert.visit(contents, {}, {})
124 C.source_refs = [SourceRef(file_name, None, None, 1, line_count)]
125 finally:
126 os.chdir(old_path)
128 out_cast = cast.CAST([C], "python")
129 else:
130 file_name = pyfile_path.split("/")[-1]
131 out_cast = TS2CAST(pyfile_path).out_cast
133 if agraph:
134 V = CASTToAGraphVisitor(out_cast)
135 last_slash_idx = file_name.rfind("/")
136 file_ending_idx = file_name.rfind(".")
137 pdf_file_name = (
138 f"{file_name[last_slash_idx + 1 : file_ending_idx]}.pdf"
139 )
140 V.to_pdf(pdf_file_name)
142 # Then, print CAST as JSON
143 if cast_obj:
144 return out_cast
145 else:
146 if rawjson:
147 print(
148 json.dumps(
149 out_cast.to_json_object(), sort_keys=True, indent=None
150 )
151 )
152 else:
153 if std_out:
154 print(out_cast.to_json_str())
155 else:
156 out_name = file_name.split(".")[0]
157 print("Writing CAST to " + out_name + "--CAST.json")
158 out_handle = open(out_name + "--CAST.json", "w")
159 out_handle.write(out_cast.to_json_str())
162if __name__ == "__main__":
163 args = get_args()
164 python_to_cast(
165 args.pyfile_path,
166 args.agraph,
167 args.stdout,
168 args.rawjson,
169 args.legacy,
170 tree_sitter=args.ts
171 )