Skip to content

Commit 0e0447a

Browse files
committed
Remove unnecessary multiprocessing lock in API requests
Refactored the request handling logic by eliminating the use of a multiprocessing `Lock`, simplifying the code and improving readability. This change does not affect thread safety as the lock usage was redundant in the current context.
1 parent bc05f6d commit 0e0447a

File tree

1 file changed

+79
-82
lines changed

1 file changed

+79
-82
lines changed

tksbrokerapi/TKSBrokerAPI.py

Lines changed: 79 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
from time import sleep
9595
from argparse import ArgumentParser
9696
from importlib.metadata import version
97-
from multiprocessing import cpu_count, Lock
97+
from multiprocessing import cpu_count
9898

9999
# Third-party library imports:
100100
import numpy as np
@@ -193,8 +193,6 @@ def __init__(self, token: str, accountId: str = None, useCache: bool = True, def
193193
self._tag = ""
194194
"""Identification TKSBrokerAPI tag in log messages to simplify debugging when platform instances runs in parallel mode. Default: `""` (empty string)."""
195195

196-
self.__lock = Lock() # initialize multiprocessing mutex lock
197-
198196
self._precision = 4 # precision, signs after comma, e.g. 2 for instruments like PLZL, 4 for instruments like USDRUB, if -1 then auto detect it when load data-file
199197

200198
self.aliases = TKS_TICKER_ALIASES
@@ -567,119 +565,118 @@ def SendAPIRequest(self, url: str, reqType: str = "GET") -> dict:
567565
break
568566

569567
if oK:
570-
with self.__lock: # acquire the mutex lock
571-
counter = 0
572-
response = None
573-
errMsg = ""
574-
currentPause = self.pause # initial pause
568+
counter = 0
569+
response = None
570+
errMsg = ""
571+
currentPause = self.pause # initial pause
575572

576-
while not response and counter <= self.retry:
577-
try:
578-
# try to send REST-request:
579-
if reqType == "GET":
580-
response = requests.get(url, headers=self.headers, data=self.body, timeout=self.timeout)
573+
while not response and counter <= self.retry:
574+
try:
575+
# try to send REST-request:
576+
if reqType == "GET":
577+
response = requests.get(url, headers=self.headers, data=self.body, timeout=self.timeout)
581578

582-
if reqType == "POST":
583-
response = requests.post(url, headers=self.headers, data=self.body, timeout=self.timeout)
579+
if reqType == "POST":
580+
response = requests.post(url, headers=self.headers, data=self.body, timeout=self.timeout)
584581

585-
except requests.exceptions.RequestException as e:
586-
# catch expected connection/request exceptions:
587-
uLogger.debug("Request exception occurred: {}.".format(e))
582+
except requests.exceptions.RequestException as e:
583+
# catch expected connection/request exceptions:
584+
uLogger.debug("Request exception occurred: {}.".format(e))
588585

589-
except Exception as e:
590-
# catch any unexpected exception (safety net):
591-
uLogger.debug("Unexpected exception occurred during request: {}.".format(e))
586+
except Exception as e:
587+
# catch any unexpected exception (safety net):
588+
uLogger.debug("Unexpected exception occurred during request: {}.".format(e))
592589

593-
finally:
594-
# always check: if no valid response received, increment counter and retry after a pause
595-
if not response:
596-
counter += 1 # increase retry counter
590+
finally:
591+
# always check: if no valid response received, increment counter and retry after a pause
592+
if not response:
593+
counter += 1 # increase retry counter
597594

598-
if counter > self.retry:
599-
uLogger.debug("Maximum retry limit ({}) exceeded without success.".format(self.retry))
595+
if counter > self.retry:
596+
uLogger.debug("Maximum retry limit ({}) exceeded without success.".format(self.retry))
600597

601-
return responseJSON # explicitly exit the method
598+
return responseJSON # explicitly exit the method
602599

603-
uLogger.debug("Retry {}/{} after {} sec...".format(counter, self.retry, currentPause))
600+
uLogger.debug("Retry {}/{} after {} sec...".format(counter, self.retry, currentPause))
604601

605-
sleep(currentPause) # wait before the next retry
602+
sleep(currentPause) # wait before the next retry
606603

607-
currentPause = min(currentPause * 2, 30) # exponentially increase wait time but limit maximum pause
604+
currentPause = min(currentPause * 2, 30) # exponentially increase wait time but limit maximum pause
608605

609-
continue # next retry attempt
606+
continue # next retry attempt
610607

611-
if self.moreDebug and response:
612-
uLogger.debug("Response:")
613-
uLogger.debug(" - status code: {}".format(response.status_code))
614-
uLogger.debug(" - reason: {}".format(response.reason))
615-
uLogger.debug(" - body length: {}".format(len(response.text)))
616-
uLogger.debug(" - headers:\n{}".format(response.headers))
608+
if self.moreDebug and response:
609+
uLogger.debug("Response:")
610+
uLogger.debug(" - status code: {}".format(response.status_code))
611+
uLogger.debug(" - reason: {}".format(response.reason))
612+
uLogger.debug(" - body length: {}".format(len(response.text)))
613+
uLogger.debug(" - headers:\n{}".format(response.headers))
617614

618-
# check rate limit headers: https://tinkoff.github.io/investAPI/grpc/#kreya
619-
if response and response.headers and response.headers.get("x-ratelimit-remaining") == "0":
620-
rateLimitWait = int(response.headers["x-ratelimit-reset"])
615+
# check rate limit headers: https://tinkoff.github.io/investAPI/grpc/#kreya
616+
if response and response.headers and response.headers.get("x-ratelimit-remaining") == "0":
617+
rateLimitWait = int(response.headers["x-ratelimit-reset"])
621618

622-
uLogger.debug("Rate limit exceeded. Waiting {} sec. for reset rate limit and then repeat again...".format(rateLimitWait))
619+
uLogger.debug("Rate limit exceeded. Waiting {} sec. for reset rate limit and then repeat again...".format(rateLimitWait))
623620

624-
sleep(rateLimitWait)
621+
sleep(rateLimitWait)
625622

626-
# handling 4xx client errors: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
627-
if response and 400 <= response.status_code < 500:
628-
bodyPreview = response.text[:256] + ("..." if len(response.text) > 256 else "")
629-
msg = "status code: [{}], response body: {}".format(response.status_code, bodyPreview)
623+
# handling 4xx client errors: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
624+
if response and 400 <= response.status_code < 500:
625+
bodyPreview = response.text[:256] + ("..." if len(response.text) > 256 else "")
626+
msg = "status code: [{}], response body: {}".format(response.status_code, bodyPreview)
630627

631-
uLogger.debug(" - not oK, but do not retry for 4xx errors, {}".format(msg))
628+
uLogger.debug(" - not oK, but do not retry for 4xx errors, {}".format(msg))
632629

633-
if "code" in response.text and "message" in response.text:
634-
if "Authentication token is missing or invalid" in response.text:
635-
uLogger.warning("Authentication token is missing or invalid! Generate a new token, please.")
630+
if "code" in response.text and "message" in response.text:
631+
if "Authentication token is missing or invalid" in response.text:
632+
uLogger.warning("Authentication token is missing or invalid! Generate a new token, please.")
636633

637-
elif "Insufficient privileges" in response.text:
638-
uLogger.warning("Insufficient privileges to perform this operation! Please verify that the token matches the user's account.")
634+
elif "Insufficient privileges" in response.text:
635+
uLogger.warning("Insufficient privileges to perform this operation! Please verify that the token matches the user's account.")
639636

640-
else:
641-
msgDict = self._ParseJSON(rawData=response.text)
637+
else:
638+
msgDict = self._ParseJSON(rawData=response.text)
642639

643-
uLogger.debug("HTTP-status code [{}], server message: {}".format(response.status_code, msgDict.get("message", "")))
640+
uLogger.debug("HTTP-status code [{}], server message: {}".format(response.status_code, msgDict.get("message", "")))
644641

645-
else:
646-
uLogger.debug("HTTP-status code [{}], raw server response: {}".format(response.status_code, response.text))
642+
else:
643+
uLogger.debug("HTTP-status code [{}], raw server response: {}".format(response.status_code, response.text))
647644

648-
responseJSON = self._ParseJSON(rawData=response.text) # parse response for errors
649-
counter = self.retry + 1 # do not retry for 4xx errors
645+
responseJSON = self._ParseJSON(rawData=response.text) # parse response for errors
646+
counter = self.retry + 1 # do not retry for 4xx errors
650647

651-
# handling 5xx server errors:
652-
if response and 500 <= response.status_code < 600:
653-
bodyPreview = response.text[:256] + ("..." if len(response.text) > 256 else "")
654-
errMsg = "status code: [{}], response body: {}".format(response.status_code, bodyPreview)
648+
# handling 5xx server errors:
649+
if response and 500 <= response.status_code < 600:
650+
bodyPreview = response.text[:256] + ("..." if len(response.text) > 256 else "")
651+
errMsg = "status code: [{}], response body: {}".format(response.status_code, bodyPreview)
655652

656-
uLogger.debug(" - not oK, {}".format(errMsg))
653+
uLogger.debug(" - not oK, {}".format(errMsg))
657654

658-
if "code" in response.text and "message" in response.text:
659-
errMsgDict = self._ParseJSON(rawData=response.text)
655+
if "code" in response.text and "message" in response.text:
656+
errMsgDict = self._ParseJSON(rawData=response.text)
660657

661-
uLogger.debug("HTTP-status code [{}], error message: {}".format(response.status_code, errMsgDict.get("message", "")))
658+
uLogger.debug("HTTP-status code [{}], error message: {}".format(response.status_code, errMsgDict.get("message", "")))
662659

663-
else:
664-
uLogger.debug("HTTP-status code [{}], raw server response: {}".format(response.status_code, response.text))
660+
else:
661+
uLogger.debug("HTTP-status code [{}], raw server response: {}".format(response.status_code, response.text))
665662

666-
responseJSON = self._ParseJSON(rawData=response.text) # parse response for errors
667-
counter += 1
668-
response = None # clear response to allow retry
663+
responseJSON = self._ParseJSON(rawData=response.text) # parse response for errors
664+
counter += 1
665+
response = None # clear response to allow retry
669666

670-
if counter <= self.retry:
671-
uLogger.debug("Retry: [{}]. Wait {} sec. and try again...".format(counter, currentPause))
667+
if counter <= self.retry:
668+
uLogger.debug("Retry: [{}]. Wait {} sec. and try again...".format(counter, currentPause))
672669

673-
sleep(currentPause)
670+
sleep(currentPause)
674671

675-
currentPause = min(currentPause * 2, 60) # limit the maximum backoff to 60 seconds
672+
currentPause = min(currentPause * 2, 60) # limit the maximum backoff to 60 seconds
676673

677-
if response:
678-
responseJSON = self._ParseJSON(rawData=response.text)
674+
if response:
675+
responseJSON = self._ParseJSON(rawData=response.text)
679676

680-
if errMsg:
681-
uLogger.error("Server returns not `oK` status! See full debug log. Errors messages: https://tinkoff.github.io/investAPI/errors/")
682-
uLogger.error(" - not oK, {}".format(errMsg))
677+
if errMsg:
678+
uLogger.error("Server returns not `oK` status! See full debug log. Errors messages: https://tinkoff.github.io/investAPI/errors/")
679+
uLogger.error(" - not oK, {}".format(errMsg))
683680

684681
return responseJSON
685682

0 commit comments

Comments
 (0)