Search code examples
pythonunreal-engine5

How to send a python script as a string to function? (Unreal Engine 5 Python Editor Script Plugin)


I have some code for sending a command from external software, for example Blender Python, to Unreal Engine editor. This is what I got so far which is working:

import sys
sys.path.append(r'C:\UnrealEngine5\Engine\Plugins\Experimental\PythonScriptPlugin\Content\Python')
import remote_execution as remote

def executeCommand(command):
    remote_exec = remote.RemoteExecution()
    remote_exec.start()
    remote_exec.open_command_connection(remote_exec.remote_nodes)
    exec_mode = 'EvaluateStatement'
    rec = remote_exec.run_command(command, exec_mode=exec_mode)
    return rec



command = "print('hello')"
result = executeCommand(command)

This will print "hello" in running UE editor output log.

My question is how would I send an entire script to the executeCommand() function? It expects a string and not a .py file. Here is the function & documentation for the remote_exec.run_command:

    def run_command(self, command, unattended=True, exec_mode=MODE_EXEC_FILE, raise_on_failure=False):
        '''
        Run a command remotely based on the current command connection.

        Args:
            command (string): The Python command to run remotely.
            unattended (bool): True to run this command in "unattended" mode (suppressing some UI).
            exec_mode (string): The execution mode to use as a string value (must be one of MODE_EXEC_FILE, MODE_EXEC_STATEMENT, or MODE_EVAL_STATEMENT).
            raise_on_failure (bool): True to raise a RuntimeError if the command fails on the remote target.

        Returns:
            dict: The result from running the remote command (see `command_result` from the protocol definition).
        '''
        data = self._command_connection.run_command(command, unattended, exec_mode)
        if raise_on_failure and not data['success']:
            raise RuntimeError('Remote Python Command failed! {0}'.format(data['result']))
        return data

And the definition of Execution modes:

# Execution modes (these must match the names given to LexToString for EPythonCommandExecutionMode in IPythonScriptPlugin.h)
MODE_EXEC_FILE = 'ExecuteFile'                          # Execute the Python command as a file. This allows you to execute either a literal Python script containing multiple statements, or a file with optional arguments
MODE_EXEC_STATEMENT = 'ExecuteStatement'                # Execute the Python command as a single statement. This will execute a single statement and print the result. This mode cannot run files
MODE_EVAL_STATEMENT = 'EvaluateStatement'               # Evaluate the Python command as a single statement. This will evaluate a single statement and return the result. This mode cannot run files

The remote_execution.py file is located in UnrealEngine5\Engine\Plugins\Experimental\PythonScriptPlugin\Content\Python

UPDATE:

Changing Evaluate Statement to Execute file, and running this script give me another error:

Python: Traceback (most recent call last):
  File "\test_remote_import.py", line 19, in <module>
    result = executeCommand(command)
             ^^^^^^^^^^^^^^^^^^^^^^^
  File "\test_remote_import.py", line 11, in executeCommand
    rec = remote_exec.run_command(command, exec_mode=exec_mode)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\UnrealEngine5\Engine\Plugins\Experimental\PythonScriptPlugin\Content\Python\remote_execution.py", line 124, in run_command
    data = self._command_connection.run_command(command, unattended, exec_mode)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\UnrealEngine5\Engine\Plugins\Experimental\PythonScriptPlugin\Content\Python\remote_execution.py", line 429, in run_command
    self._send_message(_RemoteExecutionMessage(_TYPE_COMMAND, self._node_id, self._remote_node_id, {
  File "C:\UnrealEngine5\Engine\Plugins\Experimental\PythonScriptPlugin\Content\Python\remote_execution.py", line 444, in _send_message
    self._command_channel_socket.sendall(message.to_json_bytes())
                                         ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\UnrealEngine5\Engine\Plugins\Experimental\PythonScriptPlugin\Content\Python\remote_execution.py", line 551, in to_json_bytes
    json_str = self.to_json()
               ^^^^^^^^^^^^^^
  File "C:\UnrealEngine5\Engine\Plugins\Experimental\PythonScriptPlugin\Content\Python\remote_execution.py", line 542, in to_json
    return _json.dumps(json_obj, ensure_ascii=False)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Blender Foundation\Blender 4.2\4.2\python\Lib\json\__init__.py", line 238, in dumps
    **kw).encode(obj)
          ^^^^^^^^^^^
  File "C:\Program Files\Blender Foundation\Blender 4.2\4.2\python\Lib\json\encoder.py", line 200, in encode
    chunks = self.iterencode(o, _one_shot=True)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Blender Foundation\Blender 4.2\4.2\python\Lib\json\encoder.py", line 258, in iterencode
    return _iterencode(o, 0)
           ^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Blender Foundation\Blender 4.2\4.2\python\Lib\json\encoder.py", line 180, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type module is not JSON serializable

While running this updated script:

import sys
sys.path.append(r'C:UnrealEngine5\Engine\Plugins\Experimental\PythonScriptPlugin\Content\Python')
import remote_execution as remote
import test_print as test

def executeCommand(command):
    remote_exec = remote.RemoteExecution()
    remote_exec.start()
    remote_exec.open_command_connection(remote_exec.remote_nodes)
    exec_mode = 'ExecuteFile' 
    rec = remote_exec.run_command(command, exec_mode=exec_mode)
    return rec



command = test
result = executeCommand(command)

test_print.py simply contains

print("success")

Solution

  • If I understand your question correctly, you can pass the script as a string by putting it in triple quotations: your_script = """ your script """

    if you are loading a script you should be able to open the file using a standard method:

    def read_py_script(filepath):
        with open(filepath, 'r') as file:
            return file.read()
    
    script_string = read_py_script('your_script.py')
    print(script_string)