Skip to content

Commit

Permalink
updated rpc imports to avoid relative import when rpc module is not o…
Browse files Browse the repository at this point in the history
…n sys path
  • Loading branch information
jack-yao91 committed Aug 7, 2024
1 parent 1ed59ed commit 9d675cc
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 25 deletions.
6 changes: 5 additions & 1 deletion src/addons/send2ue/core/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

import os
import bpy
from ..dependencies.unreal import UnrealRemoteCalls, is_connected
from ..dependencies.unreal import is_connected
from ..dependencies.unreal import UnrealRemoteCalls as UnrealCalls
from ..dependencies.rpc.factory import make_remote

UnrealRemoteCalls = make_remote(UnrealCalls)


def set_property_error_message(property_name, error_message):
Expand Down
5 changes: 4 additions & 1 deletion src/addons/send2ue/core/ingest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
import bpy
from . import settings, extension
from ..constants import PathModes, ExtensionTasks, UnrealTypes
from ..dependencies.unreal import UnrealRemoteCalls
from ..dependencies.unreal import UnrealRemoteCalls as UnrealCalls
from .utilities import track_progress, get_asset_id
from ..dependencies.rpc.factory import make_remote

UnrealRemoteCalls = make_remote(UnrealCalls)


@track_progress(message='Importing asset "{attribute}"...', attribute='file_path')
Expand Down
5 changes: 4 additions & 1 deletion src/addons/send2ue/core/validations.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
import os
import bpy
from . import utilities, formatting, extension
from ..dependencies.unreal import UnrealRemoteCalls
from ..constants import BlenderTypes, PathModes, ToolInfo, Extensions, ExtensionTasks, RegexPresets
from ..dependencies.unreal import UnrealRemoteCalls as UnrealCalls
from ..dependencies.rpc.factory import make_remote

UnrealRemoteCalls = make_remote(UnrealCalls)


class ValidationManager:
Expand Down
104 changes: 104 additions & 0 deletions src/addons/send2ue/dependencies/rpc/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import inspect
import textwrap
import unittest
from typing import Any, Iterator, Optional, Union, List, Tuple, Callable
from xmlrpc.client import Fault

from .client import RPCClient
Expand Down Expand Up @@ -265,6 +266,109 @@ def decorate(cls):
return cls
return decorate

def execute_remotely(
port: int,
function: Callable,
args: Optional[Union[List, Tuple]] = None,
kwargs: Optional[dict] = None,
remap_pairs: Optional[List[Tuple[str, str]]] = None,
default_imports: Optional[List[str]] = None,
):
"""
Executes the given function remotely.
"""
if not args:
args = []

validate_file_is_saved(function)
validate_key_word_parameters(function, kwargs)
rpc_factory = RPCFactory(
rpc_client=RPCClient(port),
remap_pairs=remap_pairs,
default_imports=default_imports
)
return rpc_factory.run_function_remotely(function, args)

def _make_remote(
port: int,
remap_pairs: Optional[List[Tuple[str, str]]] = None,
default_imports: Optional[List[str]] = None
):
def decorator(function):
def wrapper(*args, **kwargs):
validate_key_word_parameters(function, kwargs)
return execute_remotely(
function=function,
args=args,
kwargs=kwargs,
port=port,
remap_pairs=remap_pairs,
default_imports=default_imports
)

return wrapper

return decorator


def get_all_parent_classes(cls) -> Iterator[Any]:
"""
Gets all parent classes recursively upward from the given class.
"""
for _cls in cls.__bases__:
if object not in _cls.__bases__ and len(_cls.__bases__) >= 1:
yield from get_all_parent_classes(_cls)
yield _cls

def make_remote(
reference: Any,
port: Optional[int] = None
) -> Callable:
"""
Makes the given class or function run remotely when invoked.
"""
default_imports = []
remap_pairs = []
unreal_port = int(os.environ.get('UNREAL_PORT', 9998))

# use a different remap pairs when inside a container
if os.environ.get('TEST_ENVIRONMENT'):
unreal_port = int(os.environ.get('UNREAL_PORT', 8998))
remap_pairs = [(os.environ.get('HOST_REPO_FOLDER', ''), os.environ.get('CONTAINER_REPO_FOLDER', ''))]


if not port:
port = unreal_port

# if this is not a class then decorate it
if not inspect.isclass(reference):
return _make_remote(
port=port,
remap_pairs=remap_pairs,
default_imports=default_imports
)(reference)

# if this is a class then decorate all its methods
methods = {}
for _cls in [*get_all_parent_classes(reference), reference]:
for attribute, value in _cls.__dict__.items():
# dont look at magic methods
if not attribute.startswith('__'):
validate_class_method(_cls, value)
# note that we use getattr instead of passing the value directly.
methods[attribute] = _make_remote(
port=port,
remap_pairs=remap_pairs,
default_imports=default_imports
)(getattr(_cls, attribute))

# return a new class with the decorated methods all in the same class
return type(
reference.__name__,
(object,),
methods
)


class RPCTestCase(unittest.TestCase):
"""
Expand Down
2 changes: 0 additions & 2 deletions src/addons/send2ue/dependencies/rpc/server.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import os
import sys
sys.path.append(os.path.dirname(__file__))

from base_server import BaseRPCServerManager

Expand Down
23 changes: 7 additions & 16 deletions src/addons/send2ue/dependencies/unreal.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,17 @@
from xmlrpc.client import ProtocolError
from http.client import RemoteDisconnected

sys.path.append(os.path.dirname(__file__))
import rpc.factory
import remote_execution

try:
import unreal
except ModuleNotFoundError:
pass

REMAP_PAIRS = []
UNREAL_PORT = int(os.environ.get('UNREAL_PORT', 9998))

# use a different remap pairs when inside a container
if os.environ.get('TEST_ENVIRONMENT'):
UNREAL_PORT = int(os.environ.get('UNREAL_PORT', 8998))
REMAP_PAIRS = [(os.environ.get('HOST_REPO_FOLDER'), os.environ.get('CONTAINER_REPO_FOLDER'))]

# this defines a the decorator that makes function run as remote call in unreal
remote_unreal_decorator = rpc.factory.remote_call(
port=UNREAL_PORT,
default_imports=['import unreal'],
remap_pairs=REMAP_PAIRS,
)

unreal_response = ''


Expand Down Expand Up @@ -152,6 +140,8 @@ def run_commands(commands):
:param list commands: A formatted string of python commands that will be run by unreal engine.
:return str: The stdout produced by the remote python command.
"""
from . import remote_execution

# wrap the commands in a try except so that all exceptions can be logged in the output
commands = ['try:'] + add_indent(commands, '\t') + ['except Exception as error:', '\tprint(error)']

Expand All @@ -168,7 +158,8 @@ def is_connected():
Checks the rpc server connection
"""
try:
rpc_client = rpc.client.RPCClient(port=UNREAL_PORT)
from .rpc import client
rpc_client = client.RPCClient(port=UNREAL_PORT)
return rpc_client.proxy.is_running()
except (RemoteDisconnected, ConnectionRefusedError, ProtocolError):
return False
Expand All @@ -178,7 +169,8 @@ def set_rpc_env(key, value):
"""
Sets an env value on the unreal RPC server.
"""
rpc_client = rpc.client.RPCClient(port=UNREAL_PORT)
from .rpc import client
rpc_client = client.RPCClient(port=UNREAL_PORT)
rpc_client.proxy.set_env(key, value)


Expand Down Expand Up @@ -986,7 +978,6 @@ def run_import(self, import_type='control_rig'):
)


@rpc.factory.remote_class(remote_unreal_decorator)
class UnrealRemoteCalls:
@staticmethod
def get_lod_count(asset_path):
Expand Down
6 changes: 3 additions & 3 deletions tests/test_files/send2ue_extensions/example_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
import bpy
from pprint import pprint
from send2ue.core.extension import ExtensionBase
from send2ue.dependencies.unreal import remote_unreal_decorator
from send2ue.dependencies.rpc.factory import make_remote


@remote_unreal_decorator
def rename_unreal_asset(source_asset_path, destination_asset_path):
if unreal.EditorAssetLibrary.does_asset_exist(destination_asset_path):
unreal.EditorAssetLibrary.delete_asset(destination_asset_path)
Expand Down Expand Up @@ -99,7 +98,8 @@ def post_import(self, asset_data, properties):
print('After the import task')
asset_path = asset_data.get('asset_path')
if asset_path:
rename_unreal_asset(asset_path, f'{asset_path}_renamed_again')
remote_rename_unreal_asset = make_remote(rename_unreal_asset)
remote_rename_unreal_asset(asset_path, f'{asset_path}_renamed_again')

def post_operation(self, properties):
"""
Expand Down
3 changes: 2 additions & 1 deletion tests/utils/base_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ def __init__(self, *args, **kwargs):

from utils.blender import BlenderRemoteCalls
from send2ue.dependencies.unreal import UnrealRemoteCalls
from send2ue.dependencies.rpc.factory import make_remote
self.blender = BlenderRemoteCalls
self.unreal = UnrealRemoteCalls
self.unreal = make_remote(UnrealRemoteCalls)

def setUp(self):
# load in the object from the file you will run tests with
Expand Down

0 comments on commit 9d675cc

Please sign in to comment.