Gtk.Application.run() versus Gtk.main() Last Chance Exception Handling in Python3 - python-3.x

I created a last chance exception handler for my test Gtk.Application but it is never hit. However if I don't use Gtk.Application but use Gtk.main() to start my test app, the last chance exception handler is hit.
The sample below has 2 functions (test_gtk_main() and test_gtk_application()) which show what I mean. Both functions deliberately cause a NameError: name '_on_window_delete' is not defined. Both functions have a last chance exception handler. Only the exception handler for test_gtk_main() is hit.
My question: How can I modify test_gtk_application so the last chance exception handler is hit?
Sample code:
import logging
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gio
#set up logging
date_fmt = '%Y-%m-%d,%H:%M:%S'
log_format = '%(levelname)s %(asctime)s.%(msecs)03d %(name)s.%(funcName)s %(message)s'
logging.basicConfig(format=log_format, datefmt=date_fmt, level=logging.INFO)
_logger = logging.getLogger(__name__)
_logger.info('Gtk version is %s.%s.%s', Gtk.MAJOR_VERSION, Gtk.MINOR_VERSION, Gtk.MICRO_VERSION)
def test_gtk_main():
try:
window = Gtk.Window(title="Window")
window.connect("delete-event", _on_window_delete)
window.show_all()
Gtk.main()
except Exception as ex:
_logger.exception("\nLast Chance Exception Gtk.main() Caught '%s'", ex)
def test_gtk_application():
try:
app_id = "org.foo.example"
flags = Gio.ApplicationFlags.FLAGS_NONE
app = Gtk.Application.new(app_id, flags)
app.connect("startup", do_startup, app)
app.connect("activate", do_activate, app)
app.connect("shutdown", do_shutdown, app)
app.run()
except Exception as ex: #never hit
_logger.exception("\nLast Chance Exception Gtk.Application() Caught '%s'", ex)
def do_startup(self, app):
_logger.debug("entered do_startup")
def do_activate(self, app):
win = Gtk.ApplicationWindow(title="Test")
win.connect("delete-event", _on_window_delete)
def do_shutdown(self, app):
_logger.debug("entered do_shutdown")
if __name__ == "__main__":
test_gtk_main() #exception handler hit
test_gtk_application() #exception handler ignored
And here is the output (run from terminal on Ubuntu 18.04)
$ python3 test_gtk_exception_handler_3.py
INFO 2020-07-18,12:47:37.849 __main__.<module> Gtk version is 3.22.30
ERROR 2020-07-18,12:47:37.880 __main__.test_gtk_main
Last Chance Exception Gtk.main() Caught 'name '_on_window_delete' is not defined'
Traceback (most recent call last):
File "test_gtk_exception_handler_3.py", line 17, in test_gtk_main
window.connect("delete-event", _on_window_delete)
NameError: name '_on_window_delete' is not defined
Traceback (most recent call last):
File "test_gtk_exception_handler_3.py", line 40, in do_activate
win.connect("delete-event", _on_window_delete)
NameError: name '_on_window_delete' is not defined

The problem is you are trying to catch the exception in the wrong place.
the exception is function named "_on_window_delete" is not defined.
In test_gtk_main function, you are directly trying to connect to "_on_window_delete" that's why you are catching that error.
But in test_gtk_application you are adding "do_activate" as a callback function so the function "_on_window_delete" is not directly executed in the try statement. That's why you are not catching an error. but if you move the try clause to do_activate function it will work properly.
def do_activate(self, app):
try:
win = Gtk.ApplicationWindow(title="Test")
win.connect("delete-event", _on_window_delete)
except Exception as ex: #never hit
_logger.exception("\nLast Chance Exception Gtk.Application() Caught '%s'", ex)
or if you move that exception code to the try clause in function "test_gtk_application"
that will also catch the exception
def test_gtk_application():
try:
app_id = "org.foo.example"
flags = Gio.ApplicationFlags.FLAGS_NONE
app = Gtk.Application.new(app_id, flags)
win = Gtk.ApplicationWindow(title="Test")
app.connect("startup", do_startup, app)
app.connect("activate", do_activate, app)
app.connect("shutdown", do_shutdown, app)
win.connect("delete-event", _on_window_delete)
app.run()
except Exception as ex: #never hit
_logger.exception("\nLast Chance Exception Gtk.Application() Caught '%s'", ex)
then you would get the exception as below
INFO 2020-07-19,13:49:57.773 __main__.<module> Gtk version is 3.24.20
ERROR 2020-07-19,13:49:58.091 __main__.test_gtk_main
Last Chance Exception Gtk.main() Caught 'name '_on_window_delete' is not defined'
Traceback (most recent call last):
File "exception.py", line 17, in test_gtk_main
window.connect("delete-event", _on_window_delete)
NameError: name '_on_window_delete' is not defined
ERROR 2020-07-19,13:49:58.092 __main__.test_gtk_application
Last Chance Exception Gtk.Application() Caught 'name '_on_window_delete' is not defined'
Traceback (most recent call last):
File "exception.py", line 33, in test_gtk_application
win.connect("delete-event", _on_window_delete)
NameError: name '_on_window_delete' is not defined
basically, to catch an exception it has to run in the try clause, or else you won't be able to catch that error. for more details, I would suggest you look into (https://docs.python.org/3/tutorial/errors.html)

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.

deliberately Raise and throw an exception in python

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.

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.

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