-
Notifications
You must be signed in to change notification settings - Fork 9
UPYRE
Unreal PYthon Remote Execution
This module allows you do execute python code in Unreal, from another python process.
Code can be string, or file path. All the dependencies of the executed script must be available in Unreal environment.
The module needs to fetch few settings from the python plugin in Unreal, such as multicast group, ip etc.

This can be done either by creating a config object from a given .uproject path:
from upyrc import upyre
PROJECT_FOLDER = "D:/Projects/UpyreTest/UnrealRemoteControlTestData"
config = upyre.RemoteExecutionConfig.from_uproject_path(f"{PROJECT_FOLDER}/UpyreTest.uproject")or you can create the config object manually:
config = upyre.RemoteExecutionConfig(buffer_size=0, multicast_group=(239.0.01, 6766), project_name='UpyreTest')Once the config object is created, you can open a connection, the connection needs to be closed once you're done executing the python code. This can be done either with a context-like object, or opening / closing the connection manually:
with upyre.PythonRemoteConnection(config) as conn:
...
# Python code execution here
conn = upyre.PythonRemoteConnection(config)
conn.open_connection() # Connection opened with Unreal.
conn.close_connection() # Close the connection.Once the connection is opened, you can execute python code remotly, either as string or file path, with or without arguments.
# Create the connection
with upyre.PythonRemoteConnection(config) as conn:
# This execute simple multiline python statement OR file
result = conn.execute_python_command("unreal.log('Hello')\nunreal.log('World ?')", exec_type=upyre.ExecTypes.EXECUTE_FILE, raise_exc=True)
print(result)
# Example with a file path
result_file = conn.execute_python_command(f"{PROJECT_FOLDER}/Content/Blueprints/test_pyfile_ue.py", exec_type=upyre.ExecTypes.EXECUTE_FILE)
print(result_file)
# Example with a file path and arguments
result_file = conn.execute_python_command(f"{PROJECT_FOLDER}/Content/Blueprints/test_pyfile_ue.py hello world", exec_type=upyre.ExecTypes.EXECUTE_FILE)
print(result_file)
# Here the two arguments "hello" and "world" will be sent to the file before execution. And can be accessed with sys.argv list (the first entry being the file path).
# >>> From cmd arguments: hello world.
# You can also evalurate a single line statement and get the result back
result_statement = conn.execute_python_command("1 + 2", exec_type=upyre.ExecTypes.EVALUATE_STATEMENT)
print(result_statement.result) # This print 3
The module can construct python code with the help of jinja2 templates. This allows to create more complex code, and to easily send variables, without the need to have everything set on Unreal side.
You can see few example of it with the helpers in place in the module, such as:
Logging:
# Print out some logs.
conn.log("I'm a message.")
conn.log_warning("Uho, I'm a warning...")
conn.log_error("Oh bummer I'm an error !!!")
# ---> In Unreal: Will print the logs in the "Output log" tab.Or executing editor utility bp function or setting parameter on them:
# Set a property of a utility BP.
conn.set_bp_property("/Game/Blueprints/BPU_TestUtils.BPU_TestUtils_C", properties={"VarToChange":True})
# Run utility BP functions.
conn.execute_bp_method("/Game/Blueprints/BPU_TestUtils.BPU_TestUtils_C", "SimplePrint")
conn.execute_bp_method("/Game/Blueprints/BPU_TestUtils.BPU_TestUtils_C", "PrintWithArg", args=("Hello", 5), raise_exc=True)The templates used for those helpers can be found in 📁upyrc\re_templates folder.
You can execute your own template file with this method:
# Execute custom template.
conn.execute_template_file(f"{PROJECT_FOLDER}/Content/Blueprints/test_template.jinja", template_kwargs={"msg":"Hello !"})
# ---> In Unreal: Will print "Im printed from a custom template ! Hello ! End of template" in the "Output log" tab.More info on this method:
def execute_template_file(self, file_path: Union[Path, str], template_kwargs: dict={},
search_paths: List[Union[Path, str]]=[],
timeout: float=5.0, raise_exc=True) -> PythonCommandResult:
''' Render and execute a given .jinja template file.
An optionnal list of search paths can be given, to find other inherited templates.
'''In the remote script (Unreal side) you can print out data starting with an identifier (XXX=) and fetch it back in the PythonCommandResult object:
# In the python command you execute Unreal side (my_command.py):
print("mydata=bar")
# In the remote python process (not Unreal).
with upyre.PythonRemoteConnection(config, open_json_output_pipe=True) as conn:
cmd_result = conn.execute_python_command("my_command.py")
cmd_result.get_first_output_with_identifier("mydata") # -> returns "bar"This is a one way only data exchange, and only string data is supported atm. (But a json string can be sent that way)
In order to be able to write and read data between processes ( Python Unreal and Remote Python process ), a json pipe is available. At the connection creation, a temporary json file is created, and data can be write in it by the connection itself, and the remote script executed on Unreal side as well.
See this graph to see more details:

To open the json pipe, just use the keyword argument:
with upyre.PythonRemoteConnection(config, open_json_output_pipe=True) as conn:
...You can also open the pipe manually as such:
with upyre.PythonRemoteConnection(config) as conn:
conn.init_json_pipe()To write and read data from the connection object, use:
with upyre.PythonRemoteConnection(config, open_json_output_pipe=True) as conn:
conn.write("data_entry", 555)
data = conn.read("data_entry") # will return 555
conn.flush() # Will flush the entier json file, the data won't be available anymore. Automatically done at the connection closure.In the executed command, for instance, in a python file you want to execute on Unreal's side, you can use the json pipeline as such:
# my_script_.py
from upyre_json_pipe import json_pipe
# => The import upyre_json_pipe will fail is the json pipe wasn't opened !
# Write data to the json file.
json_pipe.write("test_data", 123)
# Data written at the connection level can be read as well.
data = json_pipe.read("data_entry") # will return 555
# ---------------------------------------------------------
# remote_session.py
import upyre
with upyre.PythonRemoteConnection(config, open_json_output_pipe=True) as conn:
result = conn.execute_python_command(f"my_script_.py", exec_type=upyre.ExecTypes.EXECUTE_FILE)
# The data can be fetched in the PythonCommandResult object:
result.read("test_data") # => returns 123
# Or in the Connection object directly:
conn.read("test_data") # => returns 123