deliberately Raise and throw an exception in python - python-3.x

I have a use case where I need to raise an exception and again throw the exception. For this I'm using raise keyword in both try and catch. The code is as below
try:
username = input ("username:")
If Len(username) != 6:
raise Exception
except Exception:
raise Exception ("username is not valid")
Is this the right way to do like this? Is it compliant to coding standards to raise Exception in both try and except blocks?

I'm guessing this is a simplified version of your actual use case, in which case it is generally correct. A couple of notes:
You can use raise without anything after it to re-raise the same error.
try:
raise ValueError('message')
except ValueError:
run_something()
raise # will raise ValueError ('message')
Don't raise a general Exception and don't catch them either, be specific, otherwise this code will obscure other errors and will be difficult to debug. If nothing else suits you, make an exception of your own:
class MyException(Exception):
pass
Then you can use it:
raise MyException('my message')
In your use case, if I understood it correctly, all of this together would be:
class InvalidUsername(Exception):
pass
try:
username = input('username: ')
if len(username) > 6:
raise InvalidUsername('Username is too long')
if '!' in username:
raise InvalidUsername('Invalid character in username')
except InvalidUsername:
handle_invalid_user()
raise
Example in the console:
>>> try:
... username = input('username: ')
... if len(username) > 6:
... raise InvalidUsername('Username is too long')
... if '!' in username:
... raise InvalidUsername('Invalid character in username')
... except InvalidUsername:
... handle_invalid_user()
... raise
...
username: test1234
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
__main__.InvalidUsername: Username is too long
Or with an invalid character:
username: ofer!
Traceback (most recent call last):
File "<stdin>", line 6, in <module>
__main__.InvalidUsername: Invalid character in username

I was also looking to raise an exception when there is an error within the business logic from an external server and due to database connection.
I came across this arcticle to basically raise the exception especially when you are follwing an Controller-> Service -> Repository pattern.
https://flask.palletsprojects.com/en/2.1.x/errorhandling/
from flask import jsonify, request
class InvalidAPIUsage(Exception):
status_code = 400
def __init__(self, message, status_code=None, payload=None):
super().__init__()
self.message = message
if status_code is not None:
self.status_code = status_code
self.payload = payload
def to_dict(self):
rv = dict(self.payload or ())
rv['message'] = self.message
return rv
#app.errorhandler(InvalidAPIUsage)
def invalid_api_usage(e):
return jsonify(e.to_dict()), e.status_code
# an API app route for getting user information
# a correct request might be /api/user?user_id=420
#app.route("/api/user")
def user_api(user_id):
user_id = request.arg.get("user_id")
if not user_id:
raise InvalidAPIUsage("No user id provided!")
user = get_user(user_id=user_id)
if not user:
raise InvalidAPIUsage("No such user!", status_code=404)
return jsonify(user.to_dict())
[1]: https://flask.palletsprojects.com/en/2.1.x/errorhandling/
I hope this helps to developers who are developing enterprise level apps.

Related

logging usability in python

I am implementing a simple logger in python.Find my code below, Sometimes it runs fine,at times I get ValueError at self.log('INFO','Append operation successful.')
Where am I doing wrong? I am new to python and have also referred to the doc. Unable to understand the inconsistency, also I am using python 3.9, tried using force parameter too. It is also not logging the level, like Critical, Info or Error.
import logging as logger
class data:
def __init__(self, filename,filetype,filesize,date):
self.filename=filename
self.filetype=filetype
self.filesize=filesize
self.date=date
def log(self,logtype,msg):
logger.basicConfig(filename="datalog.log",level=logger.INFO,force=True,format='%(asctime)s %(message)s')
if logtype == "INFO":
logger.info(msg)
elif logtype == "ERROR":
logger.ERROR(msg)
elif logtype == "CRITICAL":
logger.CRITICAL(msg)
else:
logger.info(msg)
def openFile(self):
try:
filename=self.filename+self.filetype
f=open(filename,'w+')
f.write("Hello, Lets learn OOPs in Python")
f.close()
self.log('INFO','Write operation successful.')
except Exception as e:
self.log('ERROR','Writing operation failed.')
def appendFile(self):
try:
f=open(self.filename+self.filetype,'a')
f.write("Appending some extra content")
f.close()
self.log('INFO','Append operation successful.')
except Exception as e:
self.log('ERROR','Append operation failed.')
sample = data('sample','.txt','1Mb','2021-08-20')
sample.appendFile()
sample.openFile()
Error:
ValueError: Unrecognised argument(s): force
During handling of the above exception, another exception occurred:
TypeError Traceback (most recent call last)
<ipython-input-1-e095529812fd> in log(self, logtype, msg)
12 logger.info(msg)
13 elif logtype == "ERROR":
---> 14 logger.ERROR(msg)
15 elif logtype == "CRITICAL":
16 logger.CRITICAL(msg)
TypeError: 'int' object is not callable
The force option to basicConfig is only available in Python 3.8+. Check your Python version.
Also, it's probably not a good idea to force reconfigure the logging system on each and every log event. Just configure it once, at the main entry point of your application.

How do I catch only a specific error in Python3?

Say I have a function that can produce a vast variety of errors.
I have a ValueError that I need to catch, a specific AttributeError, and then I also need to handle any other type of error.
try:
func()
except AttributeError as e:
if "specific error text" in str(e):
print("The specific AttributeError occurred")
else:
raise
except ValueError:
print("A value error occurred")
except Exception as e:
print("Another error occurred: {}".format(str(e)))
Problem: If func() bubbles an AttributeError that's not the specific one I'm looking for, in this case, it'll be re-raised and not handled how I want it to be handled (via the general Exception handler).
How do I force non-specific errors to be handled further down in the chain, without duplicating code from the Exception section into the AttributeError section?
As an option you can process AttributeError and ValueError in one try-except block and all other Exceptions on the top level like
try:
try:
func()
except AttributeError as e:
if "specific error text" in str(e):
print("The specific AttributeError occurred")
else:
raise
except ValueError:
print("A value error occurred")
except Exception as e:
print("Another error occurred: {}".format(str(e)))
this may look a bit ugly though, so we can extract inner try-except block in a separate function like
def func_with_expected_exceptions_handling():
try:
func()
except AttributeError as e:
if "specific error text" in str(e):
print("The specific AttributeError occurred")
else:
raise
except ValueError:
print("A value error occurred")
and after that
try:
func_with_expected_exceptions_handling()
except Exception as e:
print("Another error occurred: {}".format(str(e)))
this doesn't save us from an actual nested structure, but it may come in handy if this kind of func processing arises in other places.
BTW, I don't think checking for a specific error message in exception is a good idea, we need a little bit more context to see if it can be done easier.
EDIT
If I understood correctly your func looks like
def func(...):
getattr(COMMANDS, cmd.command).command(irc_c, msg, cmd)
and you want to handle error from getattr call.
I can see next options here:
Wrap getattr call in try-except and process AttributeError in-place
def func(...):
try:
commander = getattr(COMMANDS, cmd.command)
except AttributeError:
print('Command {} not found'.format(cmd.command))
else:
commander.command(irc_c, msg, cmd)
Wrap getattr call in try-except, re-raise a custom exception (or ValueError) and process it afterwards in OP try-except
class CommandNotFound(Exception): pass
def func(...):
try:
commander = getattr(COMMANDS, cmd.command)
except AttributeError:
raise CommandNotFound() # or we can use `ValueError` instead
else:
commander.command(irc_c, msg, cmd)
Use default parameter of getattr function and make some kind of logging there like
class DefaultCommand:
def command(self, irc_c, msg, cmd):
print("Command {} is not found".format(cmd.command))
and after that used like
getattr(COMMANDS, cmd.command, DefaultCommand()).command(irc_c, msg, cmd)
Basically you need to handle the specific error first. From more general to more specific, i.e Exception => AttributeError => YourError
>>> try:
... raise MyCustomAttrErr("Hey, this failed!")
... except MyCustomAttrErr as e:
... print(e)
... except AttributteError as e:
... print("Attribute error raised")
... except Exception as e:
... print("Base exception raised")
...
Hey, this failed!
Python handled the except blocks in order from top to bottom and stops in the first block that captures it.

Suppressing nested exception

In the except clause of a try...except block, is there any way to mark the current exception as handled, so if another exception is raised in the except block, it doesn't link the new exception to the old one?
Example:
from time import sleep
def connect(server):
print("Attempting to connecting to", server)
sleep(2)
raise ConnectionRefusedError("Couldn't connect")
print("Connected to", server)
try:
conn = connect('main-server')
except ConnectionRefusedError as connection_refused:
# connection_refused.handled = True
conn = connect('backup-server')
This produces:
Traceback (most recent call last):
File "<pyshell#37>", line 2, in <module>
conn = connect('main-server')
File "<pyshell#26>", line 2, in connect
raise ConnectionRefusedError("Couldn't connect")
ConnectionRefusedError: Couldn't connect
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<pyshell#37>", line 4, in <module>
conn = connect('backup-server')
File "<pyshell#26>", line 2, in connect
raise ConnectionRefusedError("Couldn't connect")
ConnectionRefusedError: Couldn't connect
I'd like to mark the first exception as handled, so that the second exception doesn't link to the first with the "During handling of the above exception, another exception occurred" ceremony.
I realize in this simple example, I can do this by creating a flag condition, exiting the except clause, testing the flag, and resuming the recovery code.
try:
conn = connect('main-server')
except ConnectionRefusedError:
conn = None
if not conn:
conn = connect('backup-server')
Or using a for...else construct:
for server in ('main-server', 'backup-server'):
try:
conn = connect(server)
break
except ConnectionRefusedError:
continue
else:
raise ConnectionRefusedError("Couldn't connect to any server")
Finally, I know I can wrap my exception handler in a try...except block, and use raise...from None to hide the first exception.
try:
conn = connect('main-server')
except ConnectionRefusedError:
try:
conn = connect('backup-server')
except ConnectionRefusedError as nested:
raise nested from None
But I'm not looking for any "restructure the code" solutions, and I'd rather not use the try ... except ... try ... except ... raise ... from None, catching exception simply to raise it again. I'm curious if there is anything I can place at this point:
except ConnectionRefusedError as connection_refused:
# connection_refused.handled = True
which can flag the exception as being completely handled.

Logging in as user using discord.py

I am trying to make some simple program, showing received messages through the terminal. Now I am trying to ask the user for their email address and password for the login, but some errors occur, which I do not quite understand. This is what my code looks like:
import discord
class DiscordClient(discord.Client):
def __init__(self, *args, **kwargs):
discord.Client.__init__(self, **kwargs)
async def on_ready(self):
print('Success!')
if __name__ == '__main__':
dc = DiscordClient()
dc.login(input('email : '), input('password : '), bot=False)
dc.run()
and the error is:
Traceback (most recent call last):
File "[...]/Main.py", line 16, in <module>
dc.run()
File "[...]/lib/python3.6/site-packages/discord/client.py", line 519, in run
self.loop.run_until_complete(self.start(*args, **kwargs))
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/base_events.py", line 466, in run_until_complete
return future.result()
File "[...]/lib/python3.6/site-packages/discord/client.py", line 490, in start
yield from self.login(*args, **kwargs)
File "[...]/lib/python3.6/site-packages/discord/client.py", line 418, in login
raise TypeError('login() takes 1 or 2 positional arguments but {} were given'.format(n))
TypeError: login() takes 1 or 2 positional arguments but 0 were given
Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x103881fd0>
So, what am I doing wrong, or what should code look like? All I was doing was write an on_message() and some basic commands like send_message().
client.login is a coroutine so it should be (untested) :
await dc.login(input('email : '), input('password : '), bot=False)
Note that in this case, bot parameter is not needed.
However, to use client.login, you need to use the client loop. To avoid that, you can simply do:
dc.run(email, password)
Which will both login and connect and then start the loop.
After that you can (in the on_ready function) get a desired server from dc.servers and a suitable channel to send there, for example, a 'Hello message' with dc.send_message.
When you are done with the connection, do self.close() from within the DiscordClient class.
Working example for Python 3.4 (replace keywords as necessary for Python 3.6)
import discord
import asyncio
import datetime
class DiscordClient(discord.Client):
def __init__(self, *args, **kwargs):
discord.Client.__init__(self, **kwargs)
#asyncio.coroutine
def on_ready(self):
servers = list(self.servers)
for server in servers:
if server.name == 'My server':
break
for channel in server.channels:
if channel.name == 'general':
break
now = datetime.datetime.now()
yield from self.send_message(channel, 'Api Success! at ' + str(now))
print('Success!')
yield from self.close()
if __name__ == '__main__':
dc = DiscordClient()
email = input('email : ')
password = input('password : ')
dc.run(email, password)

Python 3.5.2: socket.timeout exception causes typeerror

I'm a bit of a Python newbie and this is my first post to stackoverflow so please bear with me. :)
Before posting i have searched google and stackoverflow but cant seem to find anything similar to my issue.
I have a script that polls a website and retrieves the content.
It works fine for hours however if it encounters a socket timeout the script throws a typeerror even though I have an exception for it.
I'm sure I am missing something obvious, but cant put my finger on it.
Code:
timingout = 10
def get_url(url):
try:
sock = urllib.request.urlopen(url, timeout=timingout)
orig_html = sock.read()
html = orig_html.decode("utf-8", errors="ignore").encode('cp1252', errors='ignore')
sock.close()
except KeyboardInterrupt:
# Kill program if Control-C is pressed
sys.exit(0)
except urllib.error.URLError as e:
print("***Error= Page ", e.reason)
return
except timingout:
print("socket timed out - URL: %s", url)
else:
# See if site is Down or errors eg: 404
if html == None:
print ("page contains no content!?!")
return ''
# See if site is complaining
elif html == site_overload:
if _verbose:
print('ERROR: Too many requests - SLEEPING 600 secs')
time.sleep(600)
return ''
# If not, we are good
elif html:
return html
error:
return self._sock.recv_into(b)
**socket.timeout: timed out**
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "test.py", line 201, in <module>
main()
File "test.py", line 140, in main
http_text = get_text(site_id)
File "test.py", line 110, in get_text
return get_url(url)
File "test.py", line 59, in get_url
except timingout:
**TypeError: catching classes that do not inherit from BaseException is not allowed**
Thanks in advance for any suggestions & help!
It's caused by trying to use timingout to catch an exception. timingout is an integer object whereas the except statement only accepts objects inherited from the BaseException class.
Remove that except because it doesn't do anything. Also consider revising your try statement to only include a single operation. It will make troubleshooting easier and prevent you from having to break up your try statements later on when a bug occurs.

Resources