source: java2python/src/main/java/tudelft/utilities/j2p/PyProgram.java@ 391

Last change on this file since 391 was 386, checked in by wouter, 2 years ago

#115 numerous small fixes.

File size: 5.1 KB
Line 
1package tudelft.utilities.j2p;
2
3import java.io.BufferedOutputStream;
4import java.io.File;
5import java.io.FileNotFoundException;
6import java.io.FileOutputStream;
7import java.io.FileWriter;
8import java.io.IOException;
9import java.nio.file.Files;
10import java.nio.file.Path;
11import java.util.Arrays;
12import java.util.LinkedList;
13import java.util.List;
14
15import tudelft.utilities.pyrunner.Call;
16import tudelft.utilities.pyrunner.PythonError;
17import tudelft.utilities.pyrunner.PythonVenv;
18
19/**
20 * Contains a fully runnable python program. This contains multiple
21 * {@link PyModule}s.
22 *
23 */
24public class PyProgram {
25 private final List<PyModule> modules;
26
27 public PyProgram(List<PyModule> modules) {
28 this.modules = modules;
29 }
30
31 /**
32 *
33 * /**
34 *
35 * @param javaBaseDir the java base dir from where packages are found,
36 * typically src/main/java.
37 * @param modulenames list of full.qualified.path.java list of java programs
38 * inside the base dir. Each java source file of the
39 * program is relative to the javaBaseDir. This
40 * relativeness allows us to derive the correct filename,
41 * class path etc, without requesting an actual class
42 * object.
43 * @return PyProgram containing the translated java program including all
44 * specified packages
45 * @throws FileNotFoundException if file can't be found
46 */
47
48 public static PyProgram fromModules(Path javaBaseDir, List<File> modules)
49 throws FileNotFoundException {
50 List<PyModule> pymodules = new LinkedList<>();
51
52 for (File module : modules) {
53 pymodules.add(PyModule.fromJavaFile(javaBaseDir, module));
54 }
55 return new PyProgram(pymodules);
56
57 }
58
59 /**
60 *
61 * @param pycode the python code to be zipped as pip-installable zip. It
62 * is assumed that the code is just 1 file.
63 * @param modulename The name of the module. It is assumed there is just 1
64 * module, and this module is also a directory in the root
65 * folder, and only 1 level deep. The main program must be
66 * in this module. FIXME should be in pycode.
67 * @return a zip {@link File} containing all the code, as a python tar.gz
68 * package that can be pip install'ed . For instance like this:
69 *
70 * <pre><!-- @formatter:off -->
71 * PythonVenv venv = new PythonVenv(zipfile, reporter);
72 * String res = venv.call(Arrays.asList("-m", "testcode"), 10000);
73 </pre><!-- @formatter:on -->
74 * @throws IOException
75 * @throws PythonError if python code to build tar.gz fails for
76 * some reason
77 * @throws InterruptedException
78 */
79 public File getZip() throws IOException, InterruptedException, PythonError {
80 // HACK we need to find good name and properly enumerate ALL modules,
81 // not just first one.
82 String modulename = modules.get(0).getSourceFile().toString()
83 .split("/")[0];
84
85 Path tmppath = Files.createTempDirectory("j2p");
86 System.out.println(tmppath);
87
88 List<String> installs = new LinkedList<String>();
89 List<String> modulenames = new LinkedList<>();
90 // write the translated file
91 for (PyModule pymodule : modules) {
92 String modname = pymodule.getSourceFile().getPath().replace(".java",
93 "");
94 modulenames.add("'" + modname + "'");
95 File f = tmppath.resolve(modname + ".py").toFile();
96 f.getParentFile().mkdirs();// create dirs
97 FileOutputStream fos = new FileOutputStream(f);
98 BufferedOutputStream bos = new BufferedOutputStream(fos);
99 // convert string to byte array
100 String filter = "from " + modname.replace("/", ".") + " .*";
101 byte[] bytes = pymodule.getCode().toCode(filter).getBytes();
102 // write byte array to file
103 bos.write(bytes);
104 bos.close();
105 fos.close();
106 installs.addAll(pymodule.getCode().getInstalls());
107 }
108
109 List<String> requirements = installs.stream()
110 .map(str -> "\"" + str + "\"").toList();
111 // we must provide many nonsense fields like url and author to get this
112 // working...
113 String setuppy = "from setuptools import setup, find_packages\n" + "\n"
114 + "setup(\n" + " name='" + modulename + "',\n"
115 + " version=1,\n" + " author='Whatever',\n"
116 + " author_email='Whatever',\n"
117 + " url='http://nonsense',\n" + " py_modules="
118 + modulenames + ",\n" + " install_requires=" + requirements
119 + ",\n" + "\n" + " classifiers=[\n"
120 + " 'Programming Language :: Python :: 3.8',\n"
121 + " 'Programming Language :: Python :: 3.9'\n"
122 + " ],\n" + ")\n" + "";
123
124 File f = tmppath.resolve("setup.py").toFile();
125 FileWriter fw = new FileWriter(f);
126 fw.write(setuppy);
127 fw.close();
128
129 // without readme, python complains
130 f = tmppath.resolve("README").toFile();
131 fw = new FileWriter(f);
132 fw.write("");
133 fw.close();
134
135 // call python to build the package to be sure we have a correct package
136 new Call(PythonVenv.PYTHON3, Arrays.asList("setup.py", "sdist"), 30000,
137 tmppath.toFile()).run();
138
139 // -1 is the version number added py setup.py to the file
140 return tmppath.resolve("dist/" + modulename + "-1.tar.gz").toFile();
141 }
142
143 @Override
144 public String toString() {
145 return "PyProgram[" + modules + "]";
146 }
147}
Note: See TracBrowser for help on using the repository browser.