Python IMAP4 append fails silently - python-3.x

When appending a message to a folder that doesn't exist, there is no error thrown. It's hard to imagine that this is intended, what am I doing wrong?
mailbox = imaplib.IMAP4_SSL(host="foo")
mailbox.login("foo", "bar")
try:
mailbox.append("DOES_NOT_EXIST", '', imaplib.Time2Internaldate(time.time()), str(mail).encode("utf-8"))
except:
# Expecting to fail here, but it doesn't
# Message doesn't show up in any other folder either (expectedly)

As rightfully stated in the comments, this is actually intended behaviour indeed and documented.
Each command returns a tuple: (type, [data, ...]) where type is usually 'OK' or 'NO', and data is either the text from the command response, or mandated results from the command.
One way to catch the error therefore is:
status, data = mailbox.append("DOES_NOT_EXIST", '', imaplib.Time2Internaldate(time.time()), str(mail).encode("utf-8"))
if status == "NO":
# Catch it here

Related

How to manipulate message.content in an #client.command() section without MissingRequiredArgument Error

I created a ticket command in which a person sends ">ticket" followed by a question. However, when I have ctx and msg in the same function I get the error:
discord.ext.commands.errors.MissingRequiredArgument: msg is a required argument that is missing.
and when I switch the orders of ctx and msg in the function the error occurs for ctx.
#client.command()
async def ticket(ctx, msg):
if msg.content.startswith('>ticket'):
print('ticket was made')
I tried the method with the * in between the 2 and that gives me the same error. I also tried putting this if statement in the on_message function but that makes it so the command doesn't run when I type in ">ticket" and only does the code in the on_message function.
The "rest of the message" will be in the msg variable, so you don't have to cut it out. In case you only use it as >ticket, there will be no extra parameters passed into the function, so it will be missing, as your error suggests.
A way to stop the error from getting raised is to make it optional by giving it a default value, and then check if anything was passed or not.
#client.command()
async def ticket(ctx, *, msg=None):
if msg is None:
print("No arguments were passed!")
else:
print(f"The rest of the message is: {msg}")
>ticket
>>> No arguments were passed!
>ticket something something
>>> The rest of the message is: something something
You can use ctx.message, but the thing is, you're using a command. it's already named ticket (you can't invoke it any other way than >ticket) and you're checking again if it's the right command? Doesn't make any sense

Python 3 try-except: which solution is better and why?

folks.
I'm trying to configure logging from an external yaml configuration file which may or may not have the necessary options forcing me to check and fail over in several different ways. I wrote two solutions doing same thing, but in different styles:
More traditional "C-like":
try:
if config['log']['stream'].lower() == 'console':
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(fmt='scheduler: (%(levelname).1s) %(message)s'))
elif config['log']['stream'].lower() == 'syslog':
raise ValueError
else:
print('scheduler: (E) Failed to set log stream: Unknown stream: \'' + config['log']['stream'] + '\'. Failing over to syslog.', file=sys.stderr)
raise ValueError
except (KeyError, ValueError) as e:
if type(e) == KeyError:
print('scheduler: (E) Failed to set log stream: Stream is undefined. Failing over to syslog.', file=sys.stderr)
handler = logging.handlers.SysLogHandler(facility=logging.handlers.SysLogHandler.LOG_DAEMON, address = '/dev/log')
handler.setFormatter(logging.Formatter(fmt='scheduler[%(process)d]: (%(levelname).1s) %(message)s'))
finally:
log.addHandler(handler)
And "pythonic" with internal procedure:
def setlogstream(stream):
if stream == 'console':
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(fmt='scheduler: (%(levelname).1s) %(message)s'))
elif stream == 'syslog':
handler = logging.handlers.SysLogHandler(facility=logging.handlers.SysLogHandler.LOG_DAEMON, address = '/dev/log')
handler.setFormatter(logging.Formatter(fmt='scheduler[%(process)d]: (%(levelname).1s) %(message)s'))
else:
raise ValueError
log.addHandler(handler)
try:
setlogstream(config['log']['stream'].lower())
except KeyError:
print('scheduler: (E) Failed to set log stream: Stream is undefined. Failing over to syslog.', file=sys.stderr)
setlogstream('syslog')
except ValueError:
print('scheduler: (E) Failed to set log stream: Unknown stream: \'' + config['log']['stream'] + '\'. Failing over to syslog.', file=sys.stderr)
setlogstream('syslog')
They both do what I need, both short, both extendible in case I need more streams, but now I wonder which one is better and why?
Saying one is "better" is mostly a matter of personal preference; if it accomplishes the task it needs to, then pick whichever way you prefer. That said, I think the second one should be used, and here's why:
defining setlogstream() both makes it clear what that section of your code does, and allows you to use it again later, if you need to.
using separate except cases makes your code more readable and easier to follow. this could be especially useful if somehow another error occurred in the handling of the first.
Overall, the second one is far more readable, and your future self will thank you for writing it that way.

Can You Retry/Loop inside a Try/Except?

I'm trying to understand if it's possible to set a loop inside of a Try/Except call, or if I'd need to restructure to use functions. Long story short, after spending a few hours learning Python and BeautifulSoup, I managed to frankenstein some code together to scrape a list of URLs, pull that data out to CSV (and now update it to a MySQL db). The code is now working as planned, except that I occasionally run into a 10054, either because my VPN hiccups, or possibly the source host server is occasionally bouncing me (I have a 30 second delay in my loop but it still kicks me on occasion).
I get the general idea of Try/Except structure, but I'm not quite sure how I would (or if I could) loop inside it to try again. My base code to grab the URL, clean it and parse the table I need looks like this:
for url in contents:
print('Processing record', (num+1), 'of', len(contents))
if url:
print('Retrieving data from ', url[0])
html = requests.get(url[0]).text
soup = BeautifulSoup(html, 'html.parser')
for span in soup('span'):
span.decompose()
trs = soup.select('div#collapseOne tr')
if trs:
print('Processing')
for t in trs:
for header, value in zip(t.select('td')[0], t.select('td:nth-child(2)')):
if num == 0:
headers.append(' '.join(header.split()))
values.append(re.sub(' +', ' ', value.get_text(' ', strip=True)))
After that is just processing the data to CSV and running an update sql statement.
What I'd like to do is if the HTML request call fails is wait 30 seconds, try the request again, then process, or if the retry fails X number of times, go ahead and exit the script (assuming at that point I have a full connection failure).
Is it possible to do something like that in line, or would I need to make the request statement into a function and set up a loop to call it? Have to admit I'm not familiar with how Python works with function returns yet.
You can add an inner loop for the retries and put your try/except block in that. Here is a sketch of what it would look like. You could put all of this into a function and put that function call in its own try/except block to catch other errors that cause the loop to exit.
Looking at requests exception hierarchy, Timeout covers multiple recoverable exceptions and is a good start for everything you may want to catch. Other things like SSLError aren't going to get better just because you retry, so skip them. You can go through the list to see what is reasonable for you.
import itertools
# requests exceptions at
# https://requests.readthedocs.io/en/master/_modules/requests/exceptions/
for url in contents:
print('Processing record', (num+1), 'of', len(contents))
if url:
print('Retrieving data from ', url[0])
retry_count = itertools.count()
# loop for retries
while True:
try:
# get with timeout and convert http errors to exceptions
resp = requests.get(url[0], timeout=10)
resp.raise_for_status()
# the things you want to recover from
except requests.Timeout as e:
if next(retry_count) <= 5:
print("timeout, wait and retry:", e)
time.sleep(30)
continue
else:
print("timeout, exiting")
raise # reraise exception to exit
except Exception as e:
print("unrecoverable error", e)
raise
break
html = resp.text
etc…
I've done a little example by myself to graphic this, and yes, you can put loops inside try/except blocks.
from sys import exit
def example_func():
try:
while True:
num = input("> ")
try:
int(num)
if num == "10":
print("Let's go!")
else:
print("Not 10")
except ValueError:
exit(0)
except:
exit(0)
example_func()
This is a fairly simple program that takes input and if it's 10, then it says "Let's go!", otherwise it tells you it's not 10 (if it's not a valid value, it just kicks you out).
Notice that inside the while loop I put a try/except block, taking into account the necessary indentations. You can take this program as a model and use it on your favor.

Write a program which gives an input from the user by asking 'Give me an input:'

Write a program which gets an input from the user by asking 'Give me an input:',and passes this string to 'silly_function' if the function returns without an error.simply print whatever it returned. However: if the function produces a ValueError,the program should instead print'I cannot use this value'; if the function produces a TypeError,the program should instead print 'Invalid input'
def silly_function(a):
a = input('Give me an input')
try:
sily_function(a)
except ValueError:
print('I cannot see this value')[enter image description here][1]
Let's try this together.
Code Analysis
def silly_function(a):
a = input('Give me an input')
this is fine, it will prompt the user for an input**
try:
silly_function(a)
Why do you need to call silly_function again ? I don't think that was the intended behavior ?
Also silly_function doesn't do anything that will terminate the recursion or generate an error , so this is bound to break.
except ValueError:
print('I cannot see this value')[enter image description here][1]
Suppose this is a typo, but see != useand you are only handling one error and not the other one.
Suggestion
Let's right in pseudocode a bit of help
def error_handling_function(a):
a = input('Give me an input')
try:
silly_function(a)
except #input error here:
#handle error here
except #input error here:
#handle error here
def silly_function(a):
#write code that can break and generate the two error you are looking for

Message not deleting with m.expunge

I have studied numerous questions and answers here and from what I can see my code is correct. For some reason though, the mail I have selected is not being deleted.
My code is:
m = imaplib.IMAP4_SSL("imap-mail.outlook.com")
m.login("MY_EMAIL","MY_PWORD")
m.select("Sent")
resp, data = m.uid("search", None, "ALL")
uid = data[0].split()[-1]
#Can also get message content with lines
# resp,data = m.uid('fetch',uid,"(BODY[HEADER])")
# print(data)
m.store(uid, "+FLAGS", "\\Deleted") #Works fine to here
m.expunge() #This doesn't delete message
m.close()
m.logout()
If I change the m.expunge() line to print(m.expunge()) I get the tuple
('OK', [None])
The message is still in the mailbox even with the "OK" response.
I'm unsure why this happens
You are using UIDs to identify the messages, so you also need to use UID commands to change the \Deleted flag:
m.uid('STORE' uid, "+FLAGS", "\\Deleted")
You are currently trying to set the deleted flag on Message Sequence Number with the same UID, which probably doesn't exist, or refers to a completely different message.

Resources