Skip to content

Commit 2d00e80

Browse files
author
Roberto De Ioris
committed
added new test suite skel and sandboxed exec
1 parent 3a2435b commit 2d00e80

File tree

10 files changed

+178
-73
lines changed

10 files changed

+178
-73
lines changed

Source/UnrealEnginePython/Private/UEPyFVector.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,10 +267,38 @@ static int ue_py_fvector_init(ue_PyFVector *self, PyObject *args, PyObject *kwar
267267
return 0;
268268
}
269269

270+
static PyObject *ue_py_fvector_richcompare(ue_PyFVector *vec1, PyObject *b, int op) {
271+
ue_PyFVector *vec2 = py_ue_is_fvector(b);
272+
if (!vec2 || (op != Py_EQ && op != Py_NE)) {
273+
return PyErr_Format(PyExc_NotImplementedError, "can only compare with another FVector");
274+
}
275+
276+
if (op == Py_EQ) {
277+
if (vec1->vec.X == vec2->vec.X &&
278+
vec1->vec.Y == vec2->vec.Y &&
279+
vec1->vec.Z == vec2->vec.Z) {
280+
Py_INCREF(Py_True);
281+
return Py_True;
282+
}
283+
Py_INCREF(Py_False);
284+
return Py_False;
285+
}
286+
287+
if (vec1->vec.X == vec2->vec.X &&
288+
vec1->vec.Y == vec2->vec.Y &&
289+
vec1->vec.Z == vec2->vec.Z) {
290+
Py_INCREF(Py_False);
291+
return Py_False;
292+
}
293+
Py_INCREF(Py_True);
294+
return Py_True;
295+
}
296+
270297
void ue_python_init_fvector(PyObject *ue_module) {
271298
ue_PyFVectorType.tp_new = PyType_GenericNew;
272299

273300
ue_PyFVectorType.tp_init = (initproc)ue_py_fvector_init;
301+
ue_PyFVectorType.tp_richcompare = (richcmpfunc)ue_py_fvector_richcompare;
274302

275303
memset(&ue_PyFVector_number_methods, 0, sizeof(PyNumberMethods));
276304
ue_PyFVectorType.tp_as_number = &ue_PyFVector_number_methods;

Source/UnrealEnginePython/Private/UEPyIPlugin.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ static PyGetSetDef ue_PyIPlugin_getseters[] = {
226226

227227
static PyObject *ue_PyIPlugin_str(ue_PyIPlugin *self)
228228
{
229-
return PyUnicode_FromFormat("<unreal_engine.EdGraphPin {'name': '%s'}>",
229+
return PyUnicode_FromFormat("<unreal_engine.IPlugin {'name': '%s'}>",
230230
TCHAR_TO_UTF8(*self->plugin->GetName()));
231231
}
232232

Source/UnrealEnginePython/Private/UEPyModule.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,17 @@ static PyObject *py_unreal_engine_exec(PyObject * self, PyObject * args) {
9292
return Py_None;
9393
}
9494

95+
static PyObject *py_unreal_engine_sandbox_exec(PyObject * self, PyObject * args) {
96+
char *filename = nullptr;
97+
if (!PyArg_ParseTuple(args, "s:sandbox_exec", &filename)) {
98+
return NULL;
99+
}
100+
FUnrealEnginePythonModule &PythonModule = FModuleManager::GetModuleChecked<FUnrealEnginePythonModule>("UnrealEnginePython");
101+
PythonModule.RunFileSandboxed(filename);
102+
Py_INCREF(Py_None);
103+
return Py_None;
104+
}
105+
95106
static PyObject *py_ue_get_py_proxy(ue_PyUObject *self, PyObject * args) {
96107

97108
ue_py_check(self);
@@ -232,6 +243,8 @@ static PyMethodDef unreal_engine_methods[] = {
232243
#endif
233244
{ "py_exec", py_unreal_engine_exec, METH_VARARGS, "" },
234245

246+
{ "sandbox_exec", py_unreal_engine_sandbox_exec, METH_VARARGS, "" },
247+
235248
{ "get_engine_defined_action_mappings", py_unreal_engine_get_engine_defined_action_mappings, METH_VARARGS, "" },
236249

237250
{ "get_viewport_screenshot", py_unreal_engine_get_viewport_screenshot, METH_VARARGS, "" },

Source/UnrealEnginePython/Private/UnrealEnginePython.cpp

Lines changed: 90 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,20 +61,7 @@ bool FUnrealEnginePythonModule::PythonGILAcquire() {
6161
return true;
6262
}
6363

64-
void FUnrealEnginePythonModule::StartupModule()
65-
{
66-
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
67-
68-
Py_Initialize();
69-
#if PY_MAJOR_VERSION >= 3
70-
wchar_t *argv[] = { UTF8_TO_TCHAR("UnrealEngine"), NULL };
71-
#else
72-
char *argv[] = { (char *)"UnrealEngine", NULL };
73-
#endif
74-
PySys_SetArgv(1, argv);
75-
76-
PyEval_InitThreads();
77-
64+
static void UESetupPythonInterpeter(bool verbose) {
7865
unreal_engine_init_py_module();
7966

8067
PyObject *py_sys = PyImport_ImportModule("sys");
@@ -90,9 +77,27 @@ void FUnrealEnginePythonModule::StartupModule()
9077
PyObject *py_scripts_path = PyUnicode_FromString(scripts_path);
9178
PyList_Insert(py_path, 0, py_scripts_path);
9279

80+
if (verbose) {
81+
UE_LOG(LogPython, Log, TEXT("Python VM initialized: %s"), UTF8_TO_TCHAR(Py_GetVersion()));
82+
UE_LOG(LogPython, Log, TEXT("Python Scripts search path: %s"), UTF8_TO_TCHAR(scripts_path));
83+
}
84+
}
85+
86+
void FUnrealEnginePythonModule::StartupModule()
87+
{
88+
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
89+
90+
Py_Initialize();
91+
#if PY_MAJOR_VERSION >= 3
92+
wchar_t *argv[] = { UTF8_TO_TCHAR("UnrealEngine"), NULL };
93+
#else
94+
char *argv[] = { (char *)"UnrealEngine", NULL };
95+
#endif
96+
PySys_SetArgv(1, argv);
9397

94-
UE_LOG(LogPython, Log, TEXT("Python VM initialized: %s"), UTF8_TO_TCHAR(Py_GetVersion()));
95-
UE_LOG(LogPython, Log, TEXT("Python Scripts search path: %s"), UTF8_TO_TCHAR(scripts_path));
98+
PyEval_InitThreads();
99+
100+
UESetupPythonInterpeter(true);
96101

97102
PyObject *main_module = PyImport_AddModule("__main__");
98103
main_dict = PyModule_GetDict(main_module);
@@ -175,9 +180,78 @@ void FUnrealEnginePythonModule::RunFile(char *filename) {
175180
if (!eval_ret) {
176181
unreal_engine_py_log_error();
177182
return;
183+
}
184+
#endif
185+
178186
}
187+
188+
// run a python script in a new sub interpreter (useful for unit tests)
189+
void FUnrealEnginePythonModule::RunFileSandboxed(char *filename) {
190+
FScopePythonGIL gil;
191+
char *full_path = filename;
192+
if (!FPaths::FileExists(filename))
193+
{
194+
full_path = TCHAR_TO_UTF8(*FPaths::Combine(*FPaths::GameContentDir(), UTF8_TO_TCHAR("Scripts"), *FString("/"), UTF8_TO_TCHAR(filename)));
195+
}
196+
#if PY_MAJOR_VERSION >= 3
197+
FILE *fd = nullptr;
198+
199+
#if PLATFORM_WINDOWS
200+
if (fopen_s(&fd, full_path, "r") != 0) {
201+
UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path));
202+
return;
203+
}
204+
#else
205+
fd = fopen(full_path, "r");
206+
if (!fd) {
207+
UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path));
208+
return;
209+
}
179210
#endif
180211

212+
PyThreadState *_main = PyThreadState_Get();
213+
214+
PyThreadState *py_new_state = Py_NewInterpreter();
215+
if (!py_new_state) {
216+
UE_LOG(LogPython, Error, TEXT("Unable to create new Python interpreter"));
217+
return;
218+
}
219+
PyThreadState_Swap(nullptr);
220+
PyThreadState_Swap(py_new_state);
221+
222+
UESetupPythonInterpeter(false);
223+
224+
PyObject *m = PyImport_AddModule("__main__");
225+
if (m == NULL) {
226+
UE_LOG(LogPython, Error, TEXT("Unable to create new global dict"));
227+
Py_EndInterpreter(py_new_state);
228+
PyThreadState_Swap(_main);
229+
return;
230+
}
231+
PyObject *global_dict = PyModule_GetDict(m);
232+
233+
PyObject *eval_ret = PyRun_File(fd, full_path, Py_file_input, global_dict, global_dict);
234+
fclose(fd);
235+
if (!eval_ret) {
236+
unreal_engine_py_log_error();
237+
Py_EndInterpreter(py_new_state);
238+
PyThreadState_Swap(_main);
239+
return;
240+
}
241+
Py_DECREF(eval_ret);
242+
#else
243+
// damn, this is horrible, but it is the only way i found to avoid the CRT error :(
244+
FString command = FString::Printf(TEXT("execfile(\"%s\")"), UTF8_TO_TCHAR(full_path));
245+
PyObject *eval_ret = PyRun_String(TCHAR_TO_UTF8(*command), Py_file_input, global_dict, global_dict);
246+
if (!eval_ret) {
247+
unreal_engine_py_log_error();
248+
Py_EndInterpreter(py_new_state);
249+
PyThreadState_Swap(_main);
250+
return;
251+
}
252+
#endif
253+
Py_EndInterpreter(py_new_state);
254+
PyThreadState_Swap(_main);
181255
}
182256

183257
#undef LOCTEXT_NAMESPACE

Source/UnrealEnginePython/Public/UnrealEnginePython.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class UNREALENGINEPYTHON_API FUnrealEnginePythonModule : public IModuleInterface
1919

2020
void RunString(char *);
2121
void RunFile(char *);
22+
void RunFileSandboxed(char *);
2223

2324
private:
2425
void *ue_python_gil;

TestingActor.uasset

-20.6 KB
Binary file not shown.

run_tests.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import sys
2+
import unittest
3+
import unreal_engine as ue
4+
import os.path
5+
6+
# ue.sandbox_exec(ue.find_plugin('UnrealEnginePython').get_base_dir() + '/run_tests.py')
7+
8+
# setup classic stdout/stderr
9+
class UnrealEngineOutput:
10+
def __init__(self, logger):
11+
self.logger = logger
12+
def write(self, buf):
13+
self.logger(buf)
14+
def flush(self):
15+
return
16+
17+
sys.stdout = UnrealEngineOutput(ue.log)
18+
sys.stderr = UnrealEngineOutput(ue.log_error)
19+
20+
uep_base = ue.find_plugin('UnrealEnginePython').get_base_dir()
21+
22+
loader = unittest.TestLoader()
23+
tests = loader.discover(os.path.join(uep_base, 'tests'))
24+
25+
runner = unittest.runner.TextTestRunner()
26+
runner.run(tests)

tests.py

Lines changed: 0 additions & 56 deletions
This file was deleted.

tests/__init__.py

Whitespace-only changes.

tests/test_transform.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import unittest
2+
import unreal_engine as ue
3+
from unreal_engine import FVector, FRotator
4+
5+
class TestVector(unittest.TestCase):
6+
7+
def test_add(self):
8+
vector0 = FVector(1, 2, 3)
9+
vector1 = FVector(4, 5, 6)
10+
self.assertEqual( vector0 + vector1, FVector(5, 7, 9))
11+
12+
def test_add_negative(self):
13+
vector0 = FVector(1, 2, 3)
14+
vector1 = FVector(-4, -5, -6)
15+
self.assertEqual( vector0 + vector1, FVector(-3, -3, -3))
16+
17+
def test_scale(self):
18+
vector0 = FVector(1, 2, -3)
19+
self.assertEqual( vector0 * 2, FVector(2, 4, -6))

0 commit comments

Comments
 (0)