diff --git a/CHANGELOG.md b/CHANGELOG.md index 27ac5a3..5788b8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.9.21 (2025-10-16) + +### Changes + +- Warn callers when they use the webstack client from different threads. + ## 0.9.20 (2025-10-10) ### Changes diff --git a/python/mujinwebstackclient/controllerwebclientraw.py b/python/mujinwebstackclient/controllerwebclientraw.py index 6326264..0a861ba 100644 --- a/python/mujinwebstackclient/controllerwebclientraw.py +++ b/python/mujinwebstackclient/controllerwebclientraw.py @@ -157,7 +157,20 @@ class ControllerWebClientRaw(object): _subscriptionLock: threading.Lock # Lock protecting _webSocket and _subscriptions _backgroundThread: BackgroundThread = None # The background thread to handle async operations - def __init__(self, baseurl: str, username: str, password: str, locale: Optional[str] = None, author: Optional[str] = None, userAgent: Optional[str] = None, additionalHeaders: Optional[Dict[str, str]] = None, unixEndpoint: Optional[str] = None) -> None: + _threadName: Optional[str] = None # The last thread this client was used in if we're warning on calls from different threads. + + def __init__( + self, + baseurl: str, + username: str, + password: str, + locale: Optional[str] = None, + author: Optional[str] = None, + userAgent: Optional[str] = None, + additionalHeaders: Optional[Dict[str, str]] = None, + unixEndpoint: Optional[str] = None, + warnOnUseFromDifferentThreads: bool = False, + ) -> None: self._baseurl = baseurl self._username = username self._password = password @@ -201,6 +214,11 @@ def __init__(self, baseurl: str, username: str, password: str, locale: Optional[ # Set user agent header self.SetUserAgent(userAgent) + if warnOnUseFromDifferentThreads: + self._threadName = threading.current_thread().name + log.info('initialized client with warning on calls from different threads enabled and this may degrade performance') + log.info('set "warnOnUseFromDifferentThreads" to "False" to disable this if performance is poor') + def __del__(self): self.Destroy() @@ -262,6 +280,12 @@ def Request( # by default, disallow redirect since DELETE with redirection is too dangerous kwargs['allow_redirects'] = method in ('GET',) + if self._threadName is not None: + currentName = threading.current_thread().name + if currentName != self._threadName: + log.warning('client has been called across multiple threads, was "%s", now "%s"', self._threadName, currentName) + self._threadName = currentName + response = self._session.request(method=method, url=url, timeout=timeout, headers=headers, **kwargs) # in verbose logging, log the caller diff --git a/python/mujinwebstackclient/version.py b/python/mujinwebstackclient/version.py index ff3b060..18d7d8f 100644 --- a/python/mujinwebstackclient/version.py +++ b/python/mujinwebstackclient/version.py @@ -1,3 +1,3 @@ -__version__ = '0.9.20' +__version__ = '0.9.21' # Do not forget to update CHANGELOG.md diff --git a/python/mujinwebstackclient/webstackclient.py b/python/mujinwebstackclient/webstackclient.py index f4927a9..c44ecff 100644 --- a/python/mujinwebstackclient/webstackclient.py +++ b/python/mujinwebstackclient/webstackclient.py @@ -119,7 +119,17 @@ def offset(self): controllerIp = '' # Hostname of the controller web server controllerPort = 80 # Port of the controller web server - def __init__(self, controllerurl='http://127.0.0.1', controllerusername='', controllerpassword='', author=None, userAgent=None, additionalHeaders=None, unixEndpoint=None): + def __init__( + self, + controllerurl='http://127.0.0.1', + controllerusername='', + controllerpassword='', + author=None, + userAgent=None, + additionalHeaders=None, + unixEndpoint=None, + warnOnUseFromDifferentThreads: bool = False, + ): """Logs into the Mujin controller. Args: @@ -129,6 +139,8 @@ def __init__(self, controllerurl='http://127.0.0.1', controllerusername='', cont userAgent (str): User agent to be sent on each request additionalHeaders (dict): Additional HTTP headers to be included in requests unixEndpoint (str): Unix socket endpoint for communicating with HTTP server over unix socket + warnOnUseFromDifferentThreads (bool): Whether to warn callers if the client is used from different threads. + Defaults to not warning since checking the thread name on each call may significantly degrade performance. """ # Parse controllerurl @@ -155,7 +167,16 @@ def __init__(self, controllerurl='http://127.0.0.1', controllerusername='', cont 'username': self.controllerusername, 'locale': os.environ.get('LANG', ''), } - self._webclient = controllerwebclientraw.ControllerWebClientRaw(self.controllerurl, self.controllerusername, self.controllerpassword, author=author, userAgent=userAgent, additionalHeaders=additionalHeaders, unixEndpoint=unixEndpoint) + self._webclient = controllerwebclientraw.ControllerWebClientRaw( + self.controllerurl, + self.controllerusername, + self.controllerpassword, + author=author, + userAgent=userAgent, + additionalHeaders=additionalHeaders, + unixEndpoint=unixEndpoint, + warnOnUseFromDifferentThreads=warnOnUseFromDifferentThreads, + ) def __del__(self): self.Destroy() @@ -652,7 +673,7 @@ def DeleteJobs(self, timeout=5): # def CreateLogEntries(self, logEntries, timeout=5): - # type: (List[Tuple[str, Any, Dict[str, bytes]]], int) -> Any + # type: (List[Tuple[str, Any, Dict[str, bytes]]], float) -> Any files = [] for logType, logEntry, attachments in logEntries: files.append(('logEntry/%s' % logType, ('', json.dumps(logEntry), 'application/json')))