Building a retry mechanism in a while loop in python - python

while true:
ticker = binance.fetch_ticker("BTC/USDT")
current_price = ticker['last']
function_A(current_price)
I have a while loop that keeps running to check the current price of Bitcoin every second. Then I also have a function that takes current_price as an input.
However, occasionally, I am getting
"requests.exceptions.ConnectionError: ('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer'))"
So I'm trying to use try, except to make a retry mechanism whenever this error comes up.
I've tried this:
while true:
try_count = 10
while try_count > 0:
try:
ticker = binance.fetch_ticker("BTC/USDT")
current_price = ticker['last']
try_count = 0
break
except (requests.ConnectionError, requests.ReadTimeout) as error:
print(error, " detected. Trying again...")
try_count -= 1
function_A(current_price)
The problem is that if I do this, current_price ends up being undefined when I plug it in as an input in function_A on the last line. How can I fix this mechanism?

Defining current_price in a scope outside the second while loop can prevent the problem with current_price sometimes being undefined when calling function_A.
while True:
current_price = None
try_count = 10
while try_count > 0:
try:
ticker = binance.fetch_ticker("BTC/USDT")
current_price = ticker['last']
try_count = 0
break
except (requests.ConnectionError, requests.ReadTimeout) as error:
print(error, " detected. Trying again...")
try_count -= 1
if current_price is not None:
function_A(current_price)

you may want to explore the backoff library which helps to retry your function.
https://pypi.org/project/backoff/
you can just add a backoff decorator on fetch_ticker function and it should retries when it hits RequestException error.
the code should look something like
#backoff.on_exception(backoff.expo, requests.exceptions.RequestException)
def fetch_ticker(ticker):
# binance.fetch_ticker

Related

AttributeError: 'QuerySet' object has no attribute 'url'

this code has this error and I am absolutely lost how to fix it. the code runs fine until it gets to the last object saved in the database and after that it throws this error. the code is a tool that checks the html of a website between 2 points in time to check if it has an error, even if the website is running well and giving a 200 response code
This is the error:
in check_html print(monitor.url) AttributeError: 'QuerySet' object has no attribute 'url'
def run_monitors():
delete_from_db()
monitors = Monitor.objects.filter(is_active=True)
monitors._fetch_all()
asyncio.run(_run_monitors(monitors))
check_html(monitor=monitors)
def check_html(monitor):
start_time = time.time()
print(monitor.url)
# The URLs to compare
old_html = monitor.html_compare
new_url = monitor.url
# Get the HTML of each URL
try:
old_html = old_html
# html1.raise_for_status()
except Exception as e:
print(e)
try:
html2 = requests.get(new_url)
html2.raise_for_status()
except Exception as e:
print(e)
return None
html2 = html2.text[:10000]
# Create a SequenceMatcher object to compare the HTML of the two URLs
matcher = difflib.SequenceMatcher(None, old_html, html2)
similarity_ratio = matcher.ratio() * 100
response_time = time.time() - start_time
monitor.html_compare = html2
html_failure = False
counter = monitor.fault_counter
if similarity_ratio <= 90 and counter == 0:
print(f"The two HTMLs have {similarity_ratio:}% in common.")
print("change detected")
html_failure = False
counter += 1
elif similarity_ratio > 90 and counter == 0:
print(f"The two HTMLs have {similarity_ratio:.2f}% in common.")
print("no change detected")
html_failure = False
counter = 0
elif similarity_ratio > 90 and counter >= 1:
print(f"The two HTMLs have {similarity_ratio:.2f}% in common.")
if counter >= 4:
print(f"HTML fault detected")
html_failure = True
else:
counter += 1
print(f"checks if fault persists, current fault counter: {counter}")
elif similarity_ratio < 90 and counter >= 1:
print("Fault is presumably resolved")
html_failure = False
counter = 0
monitor.fault_counter = counter
# Print the similarity ratio between the two URLs
monitor.save(update_fields=['html_compare', 'fault_counter'])
return html_failure
def send_notifications():
for monitor in Monitor.objects.all():
multiple_failures, last_result = has_multiple_failures(monitor)
result = check_html(monitor)
no_notification_timeout = (not monitor.last_notified) or \
monitor.last_notified < timezone.now() - timedelta(hours=1)
if multiple_failures and no_notification_timeout and monitor.is_active:
_send_notification(monitor, last_result)
if result:
_send_notification(monitor, last_result)
I already tried to put a for loop around the 'check_html' function that iterates over every object in monitor but that just returns that monitors can't be iterated over. it was a long shot but still didn't work
You have passed the queryset to the check_html() function. Using a filter we get one or more items that are iterable. You can use the for loop in check_html() function or password only one object to the function.
I found the issue. I had added the check_html function to run on a certain command. Which at the end of the script tried to give the whole queryset to the check_html function itself.
So I just had to remove the check_html function from run_monitor.
Thank you for your help guys.

Understanding how a try except logic works within two while loops

I'm having trouble understanding the mechanism of my code. I want to retrieve the realtime price data of a crypto from an exchange and print it every second, while creating a retry mechanism that catches an exception when it occurs.
while True:
try_cnt = 10
while try_cnt > 0:
try:
price = get_current_price(symbol) # function imported from elsewhere
print(price)
time.sleep(1)
try_cnt = 0
break
except Exception as e:
print(e)
try_cnt -= 1
This is how I have it set up but I'm having trouble follow the logic. This is how I see it as of now. After the code has been run:
Price is retrieved
try_cnt is set to 0 and the second while loop is broken
The next second, try_cnt is reset to 10 and price is retrieved
try_cnt is set to 0 again and the second while loop is broken
repeat of this process
Am I understanding this correctly?
EDIT: the reason why I have a while loop outside is that I have other things that are going on at specific times besides the part I mentioned here. The part that I mentioned is supposed to be running all the time every second. For example,
while True:
if now.hour == 0 and now.minute == 0 and now.second == 0:
A = functionA(x,y)
try_cnt = 10
while try_cnt > 0:
try:
price = get_current_price(symbol) # function imported from elsewhere
print(price)
time.sleep(1)
try_cnt = 0
break
except Exception as e:
print(e)
try_cnt -= 1
I think that you are understanding everything just fine, but the code itself is a little silly and redundant. For example, setting try_cnt to 0 and then following that with break is pointless - both lines do the same thing. And unless there is some significance to the variable try_cnt that is not present within the code you posted, its existence seems pretty much useless to me. Here's code that does the exact same thing as the code above, but without the redundancies:
while True:
try:
price = get_current_price(symbol)
print(price)
time.sleep(1)
except Exception as e:
print(e)
In response to your edit, the while True loop encompassing everything makes a lot more sense. However, this means that your code isn't doing what you want it to do. The outer while loop ensures that even if try_cnt reaches 0, try_cnt will just be reset to 10 on the next pass and it will be as if no Exceptions ever occurred (besides the ten error messages in the console). If you want that inner while loop to stop executing when ten Exceptions occur, you need to place the try_cnt = 10 statement outside of both while loops like so:
try_cnt = 10
while True:
if now.hour == 0 and now.minute == 0 and now.second == 0:
A = functionA(x,y)
while try_cnt > 0:
try:
price = get_current_price(symbol)
print(price)
time.sleep(1)
try_cnt = 10
break
except Exception as e:
print(e)
try_cnt -= 1

CCXT solving connection errors

I'm getting the following error when I run my code:
I'm guessing the root of this error is when I execute the fetch_ticker() function.
I have two python scripts: main.py and functions.py (includes all the functions)
I've read somewhere else that strategically adding a time.sleep(0.01) can solve the issue.
Would it be wise to add time.sleep(0.01) before any line that requests something through BinanceUS API?
For example,
if now.hour == 0 and now.minute == 0 and (0 <= now.second < 10):
time.sleep(0.01)
target = calc_target(binanceUS, symbol)
time.sleep(0.01)
balance = binanceUS.fetch_balance()
time.sleep(0.01)
usd = balance['total']['USD']
op_mode = True
time.sleep(10)
Though I'm not sure if this will solve the issue...or I also thought about creating an error-handling code for all the functions that I use, but I'm not sure how I would go about it when the function includes one or more if-statements. For example,
def enter_position(exchange, symbol, cur_price, target, amount, position):
try_cnt = 3
while try_cnt > 0:
if cur_price > target:
try:
position['type'] = 'long'
position['amount'] = amount
exchange.create_market_buy_order(symbol=symbol, amount=amount)
except Exception as e:
print("Connection error, trying again...")
try_cnt -= 1
time.sleep(1)
else:
exchange.create_market_buy_order(symbol=symbol, amount=amount)
Not sure if this will work, though.
I'm very confused about how to go around this error. In fact, I'm not even sure what the error means. Would any of the two solutions look plausible?

How to repeat a step in a loop if an action fails?

In the case I am looping into a list, where for value msg_list = 0 it will execute action(0), usr). This action can fail for a determined user, I should choose aother user if it happens, perform all the actions related to the user
How can I do to repeat action[0] if it fails?
for msg in range(len(msg_list)):
# in this case msg = 0
usr = select_random_user()
multiple_actions_for(usr) # This are lots of code lines I don't want to repeat!!
try:
action(msg, usr)
more_actions(usr.related_stuff)
except Exception as e:
go_back_to(msg =0 instead of looping into msg=1) # this is what I want to do
How can I do to get that? Repeat the loop for msg = i instead of passing to msg = i + 1?
Put your code into the endless while-loop with exiting from it if try was successful:
for msg in range(len(msg_list)):
while True:
usr = select_random_user()
multiple_actions_for(usr)
try:
action(msg, usr)
more_actions(usr.related_stuff)
except Exception as e:
continue
else:
break
It really depends. Is it okay to go back to the beginning of the loop? If so, you can call "continue", which stops the current iteration, and restarts the loop. Otherwise, I don't think there's something similar to a goto in Python, no. It's a very structured language.
Try using while loop instead of for loop. The idea is as shown bellow:
bool still_looping = True
msg = 0
while still_looping:
usr = select_random_user()
multiple_actions_for(usr)
try:
action(msg, usr)
more_actions(usr.related_stuff)
if (msg < len (msg_list)):
msg += 1
except Exception as e:
# Do whatever you want here. msg was not incremented
if (msg == len(msg_list))
still_looping = False #verify if this was the last execution

Handling rate limit exceptions with Cursor in tweepy

I'm trying to find the correct way to handle rate limits when iterating through a list of followers using the Cursor object. Here is what I'm trying:
while True:
try:
for follower in tweepy.Cursor(api.followers, id=root_usr).items():
print(follower.id)
except tweepy.TweepError:
# hit rate limit, sleep for 15 minutes
print('Rate limited. Sleeping for 15 minutes.')
time.sleep(15 * 60 + 15)
continue
except StopIteration:
break
This is probably incorrect, since an exception will make the for loop start from the beginning again. What's the right way to iterate through all of root_usr's followers while also handling the rate limit problems?
You can construct the Cursor instance on a variable and call next(cursor) inside a try-catch. You can handle StopIteration and TweepError's that way.
Like
cursor = tweepy.Cursor(api.followers, id=root_usr).items()
# or tweepy.Cursor(api.followers, id=root_usr).items().iter()
while True:
try:
follower = next(cursor)
print(follower.id)
except StopIteration:
break
except tweepy.TweepError:
# Handle this, sleep, log, etc.
pass
There's an example under the code snippets section of the documentation that suggests making a wrapper function to deal with error handling.
def limit_handled(cursor):
while True:
try:
yield next(cursor)
except tweepy.RateLimitError:
time.sleep(15 * 60)
except StopIteration:
print("Done")
break
for follower in limit_handled(tweepy.Cursor(api.followers, id=root_usr).items()):
print(follower.id)
Also, I suggest setting count to the max, tweepy.Cursor(api.followers, id=root_usr, count=200).items(), to make the most of each API call.

Categories

Resources