From 004b56961a53f3d32c34c97c30579dd56743d607 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Mon, 5 Oct 2020 15:49:19 -0700 Subject: [PATCH 1/9] pike.core: convert _value_str to static Make pike.core.Frame._value_str a staticmethod which can be used in another module to format values the same way as the Frame pretty printer. --- pike/core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pike/core.py b/pike/core.py index 8ce5f561..49012547 100644 --- a/pike/core.py +++ b/pike/core.py @@ -468,7 +468,8 @@ def __setattr__(self, name, value): def __str__(self): return self._str(1) - def _value_str(self, value): + @staticmethod + def _value_str(value): if isinstance(value, array.array) and value.typecode == 'B': return '0x' + ''.join('%.2x' % b for b in value) else: From 08c1e97ec843fec1f9f24768053d56972000c543 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Mon, 5 Oct 2020 15:52:08 -0700 Subject: [PATCH 2/9] pike.model: resolve default_timeout at runtime Allow a running program to overwrite pike.model.default_timeout for Future timeout. (as written before, the default_timeout gets baked into the function signature at module import time and changing the default_timeout variable has no effect) --- pike/model.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pike/model.py b/pike/model.py index 3b8d6c03..365e79be 100644 --- a/pike/model.py +++ b/pike/model.py @@ -202,12 +202,14 @@ def interim(self, response): """ self.interim_response = response - def wait(self, timeout=default_timeout): + def wait(self, timeout=None): """ Wait for future result to become available. @param timeout: The time in seconds before giving up and raising TimeoutError """ + if timeout is None: + timeout = default_timeout deadline = time.time() + timeout while self.response is None: now = time.time() @@ -217,12 +219,14 @@ def wait(self, timeout=default_timeout): return self - def wait_interim(self, timeout=default_timeout): + def wait_interim(self, timeout=None): """ Wait for interim response or actual result to become available. @param timeout: The time in seconds before giving up and raising TimeoutError """ + if timeout is None: + timeout = default_timeout deadline = time.time() + timeout while self.response is None and self.interim_response is None: now = time.time() @@ -232,7 +236,7 @@ def wait_interim(self, timeout=default_timeout): return self - def result(self, timeout=default_timeout): + def result(self, timeout=None): """ Return result of future. From 07d6ff332c652cff1d15e724f395146d89c33236 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Mon, 5 Oct 2020 15:53:48 -0700 Subject: [PATCH 3/9] pike.model: extend TimeoutError TimeoutError.with_future is a factory classmethod that returns a TimeoutError with the .future attribute set. TimeoutError.__str__ formats the exception with the Future.request and Future.interim_response (if they exist). --- pike/model.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/pike/model.py b/pike/model.py index 365e79be..25f66703 100644 --- a/pike/model.py +++ b/pike/model.py @@ -88,7 +88,38 @@ def loop(timeout=None, count=None): transport.loop(timeout=timeout, count=count) class TimeoutError(Exception): - pass + """Future completion timed out""" + future = None + + @classmethod + def with_future(cls, future, *args): + """ + Instantiate TimeoutError from a given future. + + :param future: Future that timed out + :param args: passed to Exception.__init__ + :return: TimeoutError + """ + ex = cls(*args) + ex.future = future + return ex + + def __str__(self): + s = super(TimeoutError, self).__str__() + if self.future is not None: + if self.future.request is not None: + requests = [str(self.future.request)] + if not isinstance(self.future.request, (core.Frame, str, bytes)): + # attempt to recursively str format other iterables + try: + requests = [str(r) for r in self.future.request] + except TypeError: + pass + s += "\nRequest: {}".format("\n".join(requests)) + if self.future.interim_response is not None: + s += "\nInterim: {}".format(self.future.interim_response) + return s + class StateError(Exception): pass From 91f12dab28d675fa4fb1ccebfcce58e896288cf6 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Mon, 5 Oct 2020 15:55:14 -0700 Subject: [PATCH 4/9] pike.model: Future raises TimeoutError.with_future Include a reference to the Future that timed out for better post-mortem analysis. --- pike/model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pike/model.py b/pike/model.py index 25f66703..01c3635e 100644 --- a/pike/model.py +++ b/pike/model.py @@ -245,7 +245,7 @@ def wait(self, timeout=None): while self.response is None: now = time.time() if now > deadline: - raise TimeoutError('Timed out after %s seconds' % timeout) + raise TimeoutError.with_future(self, 'Timed out after %s seconds' % timeout) loop(timeout=deadline-now, count=1) return self @@ -262,7 +262,7 @@ def wait_interim(self, timeout=None): while self.response is None and self.interim_response is None: now = time.time() if now > deadline: - raise TimeoutError('Timed out after %s seconds' % timeout) + raise TimeoutError.with_future(self, 'Timed out after %s seconds' % timeout) loop(timeout=deadline-now, count=1) return self From 1ffdfc4380edf114bd307e296323ae9a88aa6605 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Mon, 5 Oct 2020 15:56:45 -0700 Subject: [PATCH 5/9] pike.model: provide requests for all Futures Pass a request value to all Futures to provide contextual information if the Future times out. These are not necessarily core.Frame objects. --- pike/model.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pike/model.py b/pike/model.py index 01c3635e..6a21d3af 100644 --- a/pike/model.py +++ b/pike/model.py @@ -465,7 +465,7 @@ def oplock_break_future(self, file_id): @param file_id: The file ID of the oplocked file. """ - future = Future(None) + future = Future(request=("OplockBreak", file_id)) for smb_res in self._oplock_break_queue[:]: if smb_res[0].file_id == file_id: @@ -489,7 +489,7 @@ def lease_break_future(self, lease_key): @param lease_key: The lease key for the lease. """ - future = Future(None) + future = Future(request=("LeaseBreak", core.Frame._value_str(lease_key))) for smb_res in self._lease_break_queue[:]: if smb_res[0].lease_key == lease_key: @@ -583,7 +583,7 @@ def __init__(self, client, server, port=445): self._negotiate_request = None self._negotiate_response = None self.callbacks = {} - self.connection_future = Future() + self.connection_future = Future(request=(server, port)) self.credits = 0 self.client = client self.server = server @@ -1045,7 +1045,7 @@ def __init__(self, conn, creds=None, bind=None, resume=None, self.session_id = 0 self.requests = [] self.responses = [] - self.session_future = Future() + self.session_future = Future(request=self.requests) self.interim_future = None if bind: @@ -1359,7 +1359,7 @@ def tree_connect_request(self, path): return tree_req def tree_connect_submit(self, tree_req): - tree_future = Future() + tree_future = Future(request=tree_req.parent) resp_future = self.connection.submit(tree_req.parent.parent)[0] resp_future.then(lambda f: tree_future.complete(Tree(self.session, tree_req.path, @@ -1493,7 +1493,7 @@ def create_request( timewarp_req = smb2.TimewarpTokenRequest(create_req) timewarp_req.timestamp = nttime.NtTime(timewarp) - open_future = Future(None) + open_future = Future(request=create_req.parent) def finish(f): with open_future: open_future( Open( From 1a86deb01385b1eee8ff4dbe6699dc3e2cd42335 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Tue, 6 Oct 2020 13:00:35 -0700 Subject: [PATCH 6/9] pike.core: update authors information add Masen Furer to authors comment --- pike/core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pike/core.py b/pike/core.py index 49012547..30c11e5b 100644 --- a/pike/core.py +++ b/pike/core.py @@ -32,6 +32,7 @@ # Core API # # Authors: Brian Koropoff (brian.koropoff@emc.com) +# Masen Furer (masen.furer@dell.com) # """ From 07f1079ecf33a39c13f29b7656a0dcdc09a94c00 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Tue, 6 Oct 2020 13:00:49 -0700 Subject: [PATCH 7/9] pike.model: update authors information add Masen Furer to authors comment --- pike/model.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pike/model.py b/pike/model.py index 6a21d3af..fe8371f2 100644 --- a/pike/model.py +++ b/pike/model.py @@ -32,6 +32,7 @@ # Transport and object model # # Authors: Brian Koropoff (brian.koropoff@emc.com) +# Masen Furer (masen.furer@dell.com) # """ From a40b24eacb40ddacfd5bdbc12458e7caa1422833 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Tue, 6 Oct 2020 13:03:12 -0700 Subject: [PATCH 8/9] pike.model: pass smb_req by kwarg Improve readability by passing smb_req by kwarg when creating new Future instances. --- pike/model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pike/model.py b/pike/model.py index fe8371f2..47a443c1 100644 --- a/pike/model.py +++ b/pike/model.py @@ -945,10 +945,10 @@ def submit(self, req): # Cancel by message id, still in send queue future = [f for f in self._out_queue if f.request.message_id == smb_req.message_id][0] # Add fake future for cancel since cancel has no response - self._out_queue.append(Future(smb_req)) + self._out_queue.append(Future(request=smb_req)) futures.append(future) else: - future = Future(smb_req) + future = Future(request=smb_req) self._out_queue.append(future) futures.append(future) From 42ca6f8cd183e3d465f7e73cfb2cea08a5917971 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Tue, 6 Oct 2020 13:07:29 -0700 Subject: [PATCH 9/9] workflows/test_samba.yml: use python 3.9 release 3.9 was released yesterday and deadsnakes wont use the -dev version anymore --- .github/workflows/test_samba.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_samba.yml b/.github/workflows/test_samba.yml index a1d188d3..2c140c37 100644 --- a/.github/workflows/test_samba.yml +++ b/.github/workflows/test_samba.yml @@ -9,7 +9,7 @@ jobs: strategy: max-parallel: 5 matrix: - python-version: [2.7, 3.6, 3.7, 3.8, 3.9-dev, 3.10-dev] + python-version: [2.7, 3.6, 3.7, 3.8, 3.9, 3.10-dev] services: samba: