Я создал программу для проверки электронных писем и получения вложений от указанных c отправителей в Outlook. Существует главное меню и подменю, два меню представляют собой уникальные функции, заключенные в отдельные блоки try-кроме, чтобы справиться с неверным вводом. Когда я намеренно ввожу неправильный ввод в подменю, он отображает исключение из главного меню. Это то, как должна работать обработка исключений, или есть способ решить эту проблему?
Я ПРЕДЫДУЩАЯ использовал два цикла стиля While True:
для каждого меню. Я подумал, что это может означать, что верхнее меню l oop все еще работает после того, как я получил доступ к подменю, в результате чего оно использует обработку исключений из верхнего меню l oop со входом подменю. Я изменил его, чтобы при переходе в другие меню текущее меню l oop сменилось на False. К сожалению, в подменю все еще отображаются исключения из верхнего меню, в то время как в верхнем меню никогда не отображаются исключения из подменю (насколько я встречал).
import win32com.client, os, datetime, time, sys
def main():
#Opens Microsoft Outlook as outlook
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
######These variables are in a try except clause because itcontents will throw an error when
###### a sender uses something like emojis in their subject line.
try:
itinbox = outlook.Folders("IT Department").Folders('Inbox')#Look at inbox of IT Dept folder
itcontents = itinbox.Items #messages in the inbox
newMail = itcontents.GetLast() #GetLast is the most RECENT email.
nextMail = itcontents.GetPrevious() #The next most recent email after .GetLast()
mailSubj = newMail.Subject #The subject header
totalEmails = itcontents.Count
#print("Most recent mail:", newMail) #subject header of most recent email
#print("sent from:", newMail.Sender)
except UnicodeEncodeError: #unsupported characters
print("Most recent message had unsupported characters.")
#Shows the 'class' value of the most recent inbox item.
for msg_class in range(1):
print("Message class value of most recent inbox item is:", itcontents.GetLast().Class)
emailUser = os.getlogin()
print("Current user is", emailUser)
todayDate = datetime.date.today()
#############
#A dictionary of sender addresses and associated sender names
#Some addresses are sent on behalf of/bounced from another address
#Use str(msg.SentOnBehalfOfName) in this case because the bounce address will change
senderDict = {"sender name" : "sender email"} #redacted details, there are about 10 unique emails
listSender = list(senderDict.values())
listSender.insert(0, "Search Any")
############Top level menu
def topMenu():
topMenuItems = ['Collect Attachments', 'View Senders', 'Edit Senders', 'Quit']
topMenuDisplay = True
while topMenuDisplay == True:
try:
for count, item in enumerate(topMenuItems,1):
print(count, item)
mainChoice = int(input('\nChoose an option: ')) #subtract one to match enumerate to index
mainChoice -= 1
print("Chose", topMenuItems[mainChoice], "\n")
###Collect Attachments
if mainChoice == 0:
topMenuDisplay = False #Intended to stop the topMenu loop
mainChoice0() #Go to sub menu
elif mainChoice == 1:
print(senderDict, "\n")
input("Press any key")
continue
elif mainChoice == 2:
print("coming soon\n")
continue
elif mainChoice == 3:
input("Press any key to exit.")
sys.exit("Goodbye")
#avoid negative number index access, still displays a menu choice but prints error
elif mainChoice < 0:
print("Your choice is not listed.")
continue
#For any other input I haven't considered
else:
print("Your choice is not listed.")
continue
except ValueError: #Not int
print("You did not enter a valid number at topMenu() (ValueError)")
continue
except AttributeError:
print("You did not enter a valid number at topMenu() (AttributeError)")
continue
except IndexError: #outside range
print("Selection was not in the list at topMenu() (IndexError)")
continue
############# Sub menu to collect email attachments from sender dictionary
def mainChoice0():
check = 0 #Keep count of checked messages
mainChoiceDisplay0 = True
#Make a folder on user desktop if it doesn't exist
if not os.path.exists("C:\\Users\\"+emailUser+"\\Desktop\\Invoices from Outlook\\"):
os.makedirs("C:\\Users\\"+emailUser+"\\Desktop\\Invoices from Outlook\\")
print("Created folder 'Invoices from Outlook' on user desktop.")
while mainChoiceDisplay0 == True:
try:
####Determine search range by date
startDate = input("Enter a date dd-mm-yyyy (blank to exit): ")
if startDate == "":
mainChoiceDisplay0 = False
topMenu()
#strptime will convert startDate from a string to a usable date
startDate = datetime.datetime.strptime(startDate, "%d-%m-%Y") #class and module are called datetime hence datetime.datetime.strptime
searchRange = int(input("Enter how many days previous to search through: "))
searchDate = startDate.date() - datetime.timedelta(days = searchRange)#Subtract number of days from user input date
print("Your search starts from", startDate, "and ends on", searchDate, "\n")
#display list of senders for mainChoice == 0
for count, item in enumerate(listSender,0):
print(count, item)
print("There are", len(listSender), "options.\n")
#Choose sender from menu
senderNum = input("Choose a sender (blank to exit): ")
if senderNum == "":
mainChoiceDisplay0 = False
topMenu()
else:
senderNum = int(senderNum)
senderChoice = listSender[senderNum]
#Search all
if senderNum == 0:
print("Searching for any sender")
for msg in reversed(itcontents): #reversed() will go from most recent to oldest email based on date
if msg.Class == 43:
if ( (str(msg.SenderEmailAddress) or str(msg.Subject) or str(msg.Sender) or str(msg.SentOnBehalfOfName)) in senderDict and (msg.SentOn.date() >= searchDate and msg.SentOn.date() <= startDate.date())):
check += 1
print(check, "messages from", msg.SenderEmailAddress, "on", msg.SentOn.date())
###############################
#print("Looking at", msg, msg.SentOn.date()) #uncomment this to see details about every message
#Check attachment file format string, invoices are usually PDFs.
#x refers to the attachment. Not every message from a listed sender has an attachment, those messages still add to count check.
for x in msg.Attachments:
if str(".pdf").casefold() in str(x): #casfold() checks possible upper or lower case combinations e.g PdF or pDf
x.SaveAsFile("C:\\Users\\"+emailUser+"\\Desktop\\Invoices from Outlook\\" + str(msg.SentOn.date()) + str(msg.SenderEmailAddress) + x.FileName)
print("Saved attachment", x, "from", str(msg.Sender()), "on", str(msg.SentOn.date()))
#break
#Stop searching inbox earlier than searchDate
if msg.SentOn.date() < searchDate:
break
elif senderNum <= len(listSender) and senderNum > 0:
print("you chose", senderChoice)
#msg refers to an inbox object, msg.Class == 43 is a mail item object
for msg in reversed(itcontents): #reversed() will go from most recent to oldest email based on date
if msg.Class == 43: #Don't call msg.Class as a function with (), just need integer value
#senderNum != 0 for all other valid options.
#https://www.programiz.com/python-programming/methods/built-in/any
#list comprehension [x for x in [items] if y in x] https://python-3-patterns-idioms-test.readthedocs.io/en/latest/Comprehensions.html
if ( (str(msg.SenderEmailAddress) or str(msg.Subject) or str(msg.Sender) or str(msg.SentOnBehalfOfName)) in senderDict
and
(msg.SentOn.date() >= searchDate and msg.SentOn.date() <= startDate.date())
and
any([field for field in [str(msg.SenderEmailAddress), str(msg.Subject), str(msg.Sender), str(msg.SentOnBehalfOfName)] if str(senderChoice) in field])
):
check += 1
print(check, "messages from", msg.SenderEmailAddress, "on", msg.SentOn.date()) #keep count of checked messages
#Check attachment file format, invoices are usually PDFs
#x refers to the attachment. Not every message from a listed sender has an attachment, those messages still add to count check.
for x in msg.Attachments:
if str(".pdf").casefold() in str(x): #casfold() cheks upper or lower case format
x.SaveAsFile("C:\\Users\\"+emailUser+"\\Desktop\\Invoices from Outlook\\" + str(msg.SentOn.date()) + str(msg.SenderEmailAddress) + x.FileName)
print("Saved attachment", x, "from", str(msg.Sender()), "on", str(msg.SentOn.date()))
#Stop searching inbox earlier than searchDate
if msg.SentOn.date() < searchDate:
break
except UnicodeEncodeError: #unsupported characters
print("Subject line could not be parsed.")
#continue
except OverflowError: #caused by invalid or very old date e.g 01-01-0000
print("Date calculation caused an overflow error, possibly because you entered a very old date e.g 01-01-0000.")
#break
except AttributeError: #The email was the wrong class for the attribute msg.Sender or you tried to use an attribute that doesn't work for that method
print("Attribute error (not class 43 mail) for item - ", msg, msg.SentOn.date())
continue
topMenu()
main()
Вывод выглядит так:
Message class value of most recent inbox item is: 43
Current user is camerona
1 Collect Attachments
2 View Senders
3 Edit Senders
4 Quit
Choose an option: 1
Chose Collect Attachments
####Sub menu starts here
Enter a date dd-mm-yyyy (blank to exit): 04-02-2020
Enter how many days previous to search through: 1
Your search starts from 2020-02-04 00:00:00 and ends on 2020-02-03
0 Search Any
1 redacted1
2 redacted2
3 redacted3
4 redacted4
5 redacted5
6 redacted6
7 redacted7
8 redacted8
There are 9 options.
Choose a sender (blank to exit):
a
You did not enter a valid number at topMenu() (ValueError)
Итак, как вы можете видеть, когда я набираю 'a'
в подменю, чтобы вызвать исключение, оно обрабатывается topMenu ().