Skip to content

Commit 68cb4f2

Browse files
committed
Refactor the PythonScript class to make possible the chain of methods (Builder pattern) and provided 2 methods: .perform() - for performing a lazyloaded action, .iterate() - for performing a lazyloaded loop
1 parent 07001a6 commit 68cb4f2

File tree

6 files changed

+117
-96
lines changed
  • common/spring-boot-python-executor-common/src/main/java/io/w4t3rcs/python/script
  • core/spring-boot-python-executor-core/src/main/java/io/w4t3rcs/python/file
  • other/py4j/spring-boot-python-executor-py4j-core/src/main/java/io/w4t3rcs/python/resolver
  • resolver
    • restricted/spring-boot-python-executor-restricted-resolver-core/src/main/java/io/w4t3rcs/python/resolver
    • result/spring-boot-python-executor-result-resolver-core/src/main/java/io/w4t3rcs/python/resolver
    • spelython/spring-boot-python-executor-spelython-resolver-core/src/main/java/io/w4t3rcs/python/resolver

6 files changed

+117
-96
lines changed

common/spring-boot-python-executor-common/src/main/java/io/w4t3rcs/python/script/PythonScript.java

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import java.util.stream.Stream;
1616

1717
public class PythonScript {
18-
1918
public static final String FILE_FORMAT = ".py";
2019
public static final int START_INDEX = 0;
2120
private final List<PythonImportLine> importLines;
@@ -48,42 +47,64 @@ public PythonScript(List<PythonImportLine> importLines, List<String> codeLines,
4847
}
4948
}
5049

51-
public void appendAll(String script) {
50+
public <T> PythonScript iterate(Iterable<T> iterable, Consumer<T> action) {
51+
return this.iterate(iterable, action, true);
52+
}
53+
54+
public <T> PythonScript iterate(Iterable<T> iterable, Consumer<T> action, boolean condition) {
55+
if (condition) iterable.forEach(action);
56+
return this;
57+
}
58+
59+
public PythonScript perform(Runnable action) {
60+
return this.perform(action, true);
61+
}
62+
63+
public PythonScript perform(Runnable action, boolean condition) {
64+
if (condition) action.run();
65+
return this;
66+
}
67+
68+
public PythonScript appendAll(String script) {
5269
script.lines().forEach(line -> {
5370
if (line.matches(PythonImportLine.IMPORT_REGEX)) {
5471
this.appendImport(line);
5572
} else {
5673
this.appendCode(line);
5774
}
5875
});
76+
return this;
5977
}
6078

61-
public void removeAll(String regex, int start, int end, Consumer<String> actionOnRemove) {
79+
public PythonScript removeAll(String regex, int start, int end, Consumer<String> actionOnRemove) {
6280
this.replaceAll(regex, start, end, group -> {
6381
actionOnRemove.accept(group);
6482
return "";
6583
});
84+
return this;
6685
}
6786

68-
public void replaceAll(String regex, int start, int end, Function<String, String> groupFunction) {
87+
public PythonScript replaceAll(String regex, int start, int end, Function<String, String> groupFunction) {
6988
this.replaceAll(regex, matchResult -> {
7089
String group = matchResult.group();
7190
String substring = group.substring(start, group.length() - end);
7291
return groupFunction.apply(substring);
7392
});
93+
return this;
7494
}
7595

76-
public void replaceAll(String regex, int start, int end, BiConsumer<String, StringBuilder> resultBuilderFunction) {
96+
public PythonScript replaceAll(String regex, int start, int end, BiConsumer<String, StringBuilder> resultBuilderFunction) {
7797
this.replaceAll(regex, matchResult -> {
7898
String group = matchResult.group();
7999
String substring = group.substring(start, group.length() - end);
80100
StringBuilder result = new StringBuilder();
81101
resultBuilderFunction.accept(substring, result);
82102
return result.toString();
83103
});
104+
return this;
84105
}
85106

86-
public void replaceAll(String regex, Function<MatchResult, String> function) {
107+
public PythonScript replaceAll(String regex, Function<MatchResult, String> function) {
87108
this.body = null;
88109
Pattern pattern = Pattern.compile(regex);
89110
List<String> codeLines = this.getCodeLines();
@@ -93,55 +114,51 @@ public void replaceAll(String regex, Function<MatchResult, String> function) {
93114
codeLine = matcher.replaceAll(function);
94115
this.setCode(codeLine, i);
95116
}
117+
return this;
96118
}
97119

98-
public void appendImport(String importLine) {
120+
public PythonScript appendImport(String importLine) {
99121
this.body = null;
100122
PythonImportLine line = new PythonImportLine(importLine);
101-
if (importLines.contains(line)) return;
123+
if (importLines.contains(line)) return this;
102124
this.getImportLines().add(line);
125+
return this;
103126
}
104127

105-
public List<String> getAllImportNames() {
106-
return this.getImportLines()
107-
.stream()
108-
.flatMap(pythonImportLine -> pythonImportLine.findNames().stream())
109-
.toList();
110-
}
111-
112-
public void wrapCode(String left, String right) {
113-
this.prependCode(left);
114-
this.appendCode(right);
115-
}
116-
117-
public void setCode(String codeLine, int index) {
128+
public PythonScript setCode(String codeLine, int index) {
118129
this.body = null;
119130
this.getCodeLines().set(index, codeLine);
131+
return this;
120132
}
121133

122-
public void insertCode(String codeLine, int index) {
134+
public PythonScript insertCode(String codeLine, int index) {
123135
this.body = null;
124136
this.getCodeLines().add(index, codeLine);
137+
return this;
125138
}
126139

127-
public void appendCode(String... codeLines) {
140+
public PythonScript appendCode(String... codeLines) {
128141
String joined = String.join("", codeLines);
129142
this.appendCode(joined);
143+
return this;
130144
}
131145

132-
public void appendCode(String codeLine) {
146+
public PythonScript appendCode(String codeLine) {
133147
this.body = null;
134148
this.getCodeLines().add(codeLine);
149+
return this;
135150
}
136151

137-
public void prependCode(String... codeLines) {
152+
public PythonScript prependCode(String... codeLines) {
138153
String joined = String.join("", codeLines);
139154
this.prependCode(joined);
155+
return this;
140156
}
141157

142-
public void prependCode(String codeLine) {
158+
public PythonScript prependCode(String codeLine) {
143159
this.body = null;
144160
this.insertCode(codeLine, START_INDEX);
161+
return this;
145162
}
146163

147164
public boolean isEmpty() {
@@ -182,6 +199,13 @@ public int getCodeIndex(String codeLine) {
182199
return this.getCodeLines().indexOf(codeLine);
183200
}
184201

202+
public List<String> getAllImportNames() {
203+
return this.getImportLines()
204+
.stream()
205+
.flatMap(pythonImportLine -> pythonImportLine.findNames().stream())
206+
.toList();
207+
}
208+
185209
public List<PythonImportLine> getImportLines() {
186210
return importLines;
187211
}

core/spring-boot-python-executor-core/src/main/java/io/w4t3rcs/python/file/BasicPythonFileReader.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,18 @@ public class BasicPythonFileReader implements PythonFileReader {
3737
/**
3838
* Reads the content of a Python script file resolved from the given path string.
3939
*
40-
* @param pythonScript the script object containing path string for the script file, must be non-null
40+
* @param script the script object containing path string for the script file, must be non-null
4141
* @return the script content as a {@link PythonScript}, never null but possibly empty
4242
* @throws PythonScriptReadingFromFileException if an I/O error occurs during reading
4343
*/
4444
@Override
45-
public PythonScript readScript(PythonScript pythonScript) {
46-
String source = pythonScript.getSource();
45+
public PythonScript readScript(PythonScript script) {
46+
String source = script.getSource();
4747
Path scriptPath = this.getScriptPath(source);
4848
try (BufferedReader bufferedReader = Files.newBufferedReader(scriptPath)) {
4949
String body = bufferedReader.lines().collect(Collectors.joining("\n"));
50-
pythonScript.appendAll(body);
51-
return pythonScript;
50+
script.appendAll(body);
51+
return script;
5252
} catch (IOException e) {
5353
throw new PythonScriptReadingFromFileException(e);
5454
}

other/py4j/spring-boot-python-executor-py4j-core/src/main/java/io/w4t3rcs/python/resolver/Py4JResolver.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,8 @@ public class Py4JResolver implements PythonResolver {
4141
public PythonScript resolve(PythonScript script, Map<String, Object> arguments) {
4242
String gatewayObject = resolverProperties.gatewayObject();
4343
String gatewayProperties = String.join(",\n\t\t", resolverProperties.gatewayProperties());
44-
String formatted = gatewayObject.formatted(gatewayProperties);
45-
script.prependCode(formatted);
46-
script.appendImport(resolverProperties.importLine());
47-
return script;
44+
String formattedGatewayObject = gatewayObject.formatted(gatewayProperties);
45+
return script.appendImport(resolverProperties.importLine())
46+
.prependCode(formattedGatewayObject);
4847
}
4948
}

resolver/restricted/spring-boot-python-executor-restricted-resolver-core/src/main/java/io/w4t3rcs/python/resolver/RestrictedPythonResolver.java

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -44,33 +44,33 @@ public class RestrictedPythonResolver implements PythonResolver {
4444
*/
4545
@Override
4646
public PythonScript resolve(PythonScript script, Map<String, Object> arguments) {
47-
List<String> importNames = script.getAllImportNames();
4847
String codeVariableName = resolverProperties.codeVariableName();
4948
String localVariablesName = resolverProperties.localVariablesName();
49+
List<String> importNames = script.getAllImportNames();
50+
String safeGlobalsVariable = "safe_globals_with_imports = dict(safe_globals)";
5051
String resultAppearance = resolverProperties.resultAppearance();
51-
script.prependCode(codeVariableName, " = \"\"\"");
52-
script.appendCode("\"\"\"");
53-
script.appendCode(localVariablesName, " = {}");
54-
script.appendCode("restricted_byte_code = compile_restricted(", codeVariableName, ", '<inline>', 'exec')");
55-
script.appendCode("exec(restricted_byte_code, safe_globals_with_imports, ", localVariablesName, ")");
56-
for (String importName : importNames) {
57-
script.prependCode("safe_globals_with_imports['", importName, "'] = ", importName);
58-
}
52+
String importLine = resolverProperties.importLine();
5953
boolean printEnabled = resolverProperties.printEnabled();
60-
String safeGlobalsVariable = "safe_globals_with_imports = dict(safe_globals)";
61-
script.prependCode(safeGlobalsVariable);
62-
script.appendImport(resolverProperties.importLine());
63-
if (script.containsCode(resultAppearance)) {
64-
script.appendCode(resultAppearance, " = execution_result['", resultAppearance, "']");
65-
}
66-
if (printEnabled) {
67-
script.prependCode("_getattr_ = getattr");
68-
script.prependCode("_print_ = PrintCollector");
69-
script.appendImport("from RestrictedPython.PrintCollector import PrintCollector");
70-
int index = script.getCodeIndex(safeGlobalsVariable) + 1;
71-
script.insertCode("safe_globals_with_imports['_getattr_'] = _getattr_", index);
72-
script.insertCode("safe_globals_with_imports['_print_'] = _print_", index);
73-
}
74-
return script;
54+
return script.prependCode(codeVariableName, " = \"\"\"")
55+
.appendCode("\"\"\"")
56+
.appendCode(localVariablesName, " = {}")
57+
.appendCode("restricted_byte_code = compile_restricted(", codeVariableName, ", '<inline>', 'exec')")
58+
.appendCode("exec(restricted_byte_code, safe_globals_with_imports, ", localVariablesName, ")")
59+
.iterate(importNames, importName -> {
60+
script.prependCode("safe_globals_with_imports['", importName, "'] = ", importName);
61+
})
62+
.prependCode(safeGlobalsVariable)
63+
.appendImport(importLine)
64+
.perform(() -> {
65+
script.appendCode(resultAppearance, " = execution_result['", resultAppearance, "']");
66+
}, script.containsCode(resultAppearance))
67+
.perform(() -> {
68+
script.prependCode("_getattr_ = getattr")
69+
.prependCode("_print_ = PrintCollector")
70+
.appendImport("from RestrictedPython.PrintCollector import PrintCollector");
71+
int index = script.getCodeIndex(safeGlobalsVariable) + 1;
72+
script.insertCode("safe_globals_with_imports['_getattr_'] = _getattr_", index)
73+
.insertCode("safe_globals_with_imports['_print_'] = _print_", index);
74+
}, printEnabled);
7575
}
7676
}

resolver/result/spring-boot-python-executor-result-resolver-core/src/main/java/io/w4t3rcs/python/resolver/ResultResolver.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,15 @@ public class ResultResolver implements PythonResolver {
3535
@Override
3636
public PythonScript resolve(PythonScript script, Map<String, Object> arguments) {
3737
String resultAppearance = resolverProperties.appearance();
38-
script.appendImport(IMPORT_JSON);
39-
script.removeAll(resolverProperties.regex(),
40-
resolverProperties.positionFromStart(),
41-
resolverProperties.positionFromEnd(),
42-
group -> {
43-
script.appendCode(resultAppearance, " = json.loads(json.dumps(", group, "))");
44-
if (resolverProperties.isPrinted()) {
45-
script.appendCode("print('", resultAppearance, "' + json.dumps(", resultAppearance, "))");
46-
}
47-
});
48-
return script;
38+
return script.appendImport(IMPORT_JSON)
39+
.removeAll(resolverProperties.regex(),
40+
resolverProperties.positionFromStart(),
41+
resolverProperties.positionFromEnd(),
42+
group -> {
43+
script.appendCode(resultAppearance, " = json.loads(json.dumps(", group, "))")
44+
.perform(() -> {
45+
script.appendCode("print('", resultAppearance, "' + json.dumps(", resultAppearance, "))");
46+
}, resolverProperties.isPrinted());
47+
});
4948
}
5049
}

resolver/spelython/spring-boot-python-executor-spelython-resolver-core/src/main/java/io/w4t3rcs/python/resolver/SpelythonResolver.java

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ public class SpelythonResolver implements PythonResolver {
5454
*/
5555
@Override
5656
public PythonScript resolve(PythonScript script, Map<String, Object> arguments) {
57-
script.appendImport(IMPORT_JSON);
5857
ExpressionParser parser = new SpelExpressionParser();
5958
StandardEvaluationContext context = new StandardEvaluationContext();
6059
if (arguments != null && !arguments.isEmpty()) {
@@ -63,30 +62,30 @@ public PythonScript resolve(PythonScript script, Map<String, Object> arguments)
6362
.setValue(context, value));
6463
}
6564
context.setBeanResolver(new BeanFactoryResolver(applicationContext));
66-
script.replaceAll(resolverProperties.regex(),
67-
resolverProperties.positionFromStart(),
68-
resolverProperties.positionFromEnd(),
69-
(group, result) -> {
70-
try {
71-
Expression expression = parser.parseExpression(group);
72-
Object expressionValue = expression.getValue(context, Object.class);
73-
String jsonResult = objectMapper.writeValueAsString(expressionValue).replace("'", "\\'");
74-
if (jsonResult.startsWith("\"\\\"") && jsonResult.endsWith("\\\"\"")) {
75-
int beginIndex = 3;
76-
int endIndex = jsonResult.length() - beginIndex;
77-
jsonResult = jsonResult.substring(beginIndex, endIndex);
78-
result.append("'")
79-
.append(jsonResult)
80-
.append("'");
81-
} else {
82-
result.append("json.loads('")
83-
.append(jsonResult)
84-
.append("')");
85-
}
86-
} catch (JsonProcessingException e) {
87-
throw new SpelythonProcessingException(e);
88-
}
89-
});
90-
return script;
65+
return script.appendImport(IMPORT_JSON)
66+
.replaceAll(resolverProperties.regex(),
67+
resolverProperties.positionFromStart(),
68+
resolverProperties.positionFromEnd(),
69+
(group, result) -> {
70+
try {
71+
Expression expression = parser.parseExpression(group);
72+
Object expressionValue = expression.getValue(context, Object.class);
73+
String jsonResult = objectMapper.writeValueAsString(expressionValue).replace("'", "\\'");
74+
if (jsonResult.startsWith("\"\\\"") && jsonResult.endsWith("\\\"\"")) {
75+
int beginIndex = 3;
76+
int endIndex = jsonResult.length() - beginIndex;
77+
jsonResult = jsonResult.substring(beginIndex, endIndex);
78+
result.append("'")
79+
.append(jsonResult)
80+
.append("'");
81+
} else {
82+
result.append("json.loads('")
83+
.append(jsonResult)
84+
.append("')");
85+
}
86+
} catch (JsonProcessingException e) {
87+
throw new SpelythonProcessingException(e);
88+
}
89+
});
9190
}
9291
}

0 commit comments

Comments
 (0)