Skip to content

Commit

Permalink
make stdin, stdout and stderr thread specific
Browse files Browse the repository at this point in the history
  • Loading branch information
rlehfeld committed Aug 30, 2024
1 parent ef785d2 commit 2cbf0f0
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 25 deletions.
71 changes: 47 additions & 24 deletions RPyCRobotRemote/RPyCRobotRemoteServer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import logging
import io
import inspect
import threading
from typing import TextIO, Optional, Union
from robot.libraries.DateTime import convert_time
import rpyc
Expand All @@ -23,6 +24,43 @@
UNDEFINED = object()


class WrapTheadSpecific:
def __init__(self, default=None):
object.__setattr__(self, '_local', threading.local())
object.__setattr__(self, '_default', default)
object.__setattr__(self, '__doc__', default.__doc__)

def __getattr__(self, /, item):
return getattr(self.get_thread_specific_instance(), item)

def __setattr__(self, /, item, value):
return setattr(self.get_thread_specific_instance(), item, value)

def __delattr__(self, /, item):
return delattr(self.get_thread_specific_instance(), item)

def get_thread_specific_instance(self, /):
return getattr(self._local, 'value', self._default)

def set_thread_specific_instance(self, /, obj):
self._local.value = obj

def unset_thread_specific_instance(self, /):
try:
del self._local.value
except AttributeError:
pass


_stdin = WrapTheadSpecific(sys.stdin)
_stdout = WrapTheadSpecific(sys.stdout)
_stderr = WrapTheadSpecific(sys.stderr)

sys.stdin = _stdin
sys.stdout = _stdout
sys.stderr = _stderr


class RPyCRobotRemoteServer:
"""
Implements Remote Sever Interface for Robot Framework based on RPyC
Expand Down Expand Up @@ -66,9 +104,6 @@ def __init__(self, library):
super().__init__()
self.namespace = {}
self._library = library
self._stdin = UNDEFINED
self._stdout = UNDEFINED
self._stderr = UNDEFINED

if allow_remote_stop:
@staticmethod
Expand All @@ -82,14 +117,9 @@ def on_connect(self, conn):
on_connect()

def on_disconnect(self, conn):
if sys.stdin is self._stdin:
sys.stdin = sys.__stdin__

if sys.stdout is self._stdout:
sys.stdout = sys.__stdout__

if sys.stderr is self._stderr:
sys.stderr = sys.__stderr__
_stdin.unset_thread_specific_instance()
_stdout.unset_thread_specific_instance()
_stderr.unset_thread_specific_instance()

on_disconnect = getattr(self._library, '_on_disconnect', None)
if on_disconnect:
Expand Down Expand Up @@ -134,32 +164,29 @@ def library(self):
@property
def stdin(self):
"""wrapper to change stdout from remote"""
return sys.stdin
return _stdin.get_specific_instance()

@stdin.setter
def stdin(self, value: TextIO):
self._stdin = value
sys.stdin = value
_stdin.set_thread_specific_instance(value)

@property
def stdout(self):
"""wrapper to change stdout from remote"""
return sys.stdout
return _stdout.get_thread_specific_instance()

@stdout.setter
def stdout(self, value: TextIO):
self._stdout = value
sys.stdout = value
_stdout.set_thread_specific_instance(value)

@property
def stderr(self):
"""wrapper to change stderr from remote"""
return sys.stderr
return _stderr.get_thread_specific_instance()

@stderr.setter
def stderr(self, value: TextIO):
self._stderr = value
sys.stderr = value
_stderr.set_thread_specific_instance(value)

def _rpyc_setattr(self, name: str, value):
if name in ('stdin', 'stdout', 'stderr'):
Expand Down Expand Up @@ -230,10 +257,6 @@ def serve(self):
print(self.server_port, file=f)
self._server.start()

sys.stdin = sys.__stdin__
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__

if self._port_file and not isinstance(self._port_file, io.TextIOBase):
self._port_file.unlink()

Expand Down
2 changes: 1 addition & 1 deletion test/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
Provider(),
serve=False,
# port=0,
port_file=sys.stdout,
port_file=sys.__stdout__,
server=RPyCRobotRemote.SingleServer
)

Expand Down

0 comments on commit 2cbf0f0

Please sign in to comment.