Explain Codes LogoExplain Codes Logo

When I catch an exception, how do I get the type, file, and line number?

python
exception-handling
logging
traceback
Anton ShumikhinbyAnton ShumikhinΒ·Nov 15, 2024
⚑TLDR

To extract the type, file, and line number of an exception, we utilize Python's built-in traceback module with sys.exc_info():

import traceback import sys try: # Here be dragons... πŸ‰ raise ValueError('An Ill-Valued Example') except Exception: # Something's fishy... Let's investigate! πŸ•΅οΈβ€β™‚οΈ exc_type, _, exc_tb = sys.exc_info() fname = traceback.extract_tb(exc_tb)[-1].filename line = traceback.extract_tb(exc_tb)[-1].lineno print(f"Exception: {exc_type.__name__}") print(f"File: {fname}") print(f"Line: {line}")

From within the exception catching block, directly access exception type as exc_type.__name__, file name as fname, and line number as line.

Deep-Dive: Dealing with the Unknown

Tackling Complex Code Structures

For more complex code bases where exceptions may be nested or have traceback chains, a detailed traceback inspection is required.

import traceback # Function that plays with fire πŸ”₯ def cause_error(): try: {}['non_existent_key'] # This will cause a KeyError except KeyError as e: raise ValueError('Repackaged the angry KeyError into a chilled ValueError.') from e try: cause_error() except ValueError as e: tb = e.__traceback__ # Iterate through the traceback while tb is not None: print(f"Filename {tb.tb_frame.f_code.co_filename}, line {tb.tb_lineno}, in method {tb.tb_frame.f_code.co_name}") tb = tb.tb_next

This script gives us a deep-dive into the traceback chain, revealing the cause behind our exasperating exception. "Aha!" moment waiting to happen.

Streamlining the Process

Logging is essential when maintaining a professional application. Python’s logging module equips you to record exceptions, dodging the pesky post-mortem debugging bullet.

import logging logger = logging.getLogger(__name__) logging.basicConfig(level=logging.ERROR) # Let's get serious! try: # Our code-beast that might throw a tantrum except Exception: logger.exception('Guess what! We found a bug!') # Logs the stack trace

This allows us to keep an eye on the complete stack trace without needlessly tying ourselves in formatting knots.

Advancing with Traceback Formatting

traceback.format_exc() and traceback.format_exception() are wonders when you prefer to dance to your own formatting tunes.

import traceback import sys try: # Code prone to occasional meltdowns πŸŒ‹ except Exception: formatted_traceback = ''.join(traceback.format_exception(*sys.exc_info())) # Store or log the neatly done traceback for your tea-time reading πŸ‘“

Talk about personalized! Align the formatting to your project's standards or your aesthetics. Because, why not!

Visualization

Detective Work (πŸ•΅οΈ : Me unravelling the Exception Mystery πŸ•΅οΈ)

Keep this bingo card handy πŸ”:

| Clue (An alias for...) | Toolkit (AKA these brilliant Python features) | | ------------------------ | --------------------------------------------- | | Exception Type | type(exc) | | Crime Scene (File, duh!) | exc.__traceback__.tb_frame.f_code.co_filename | | Incident on Line Number | exc.__traceback__.tb_lineno |

Onwards with this, make your way through the labyrinth of errant code!

Translation: πŸ•΅οΈβ†’πŸ”Ž(Type) β†’πŸ“(File) β†’πŸš©(Line)

Watch Out for the Traps!

Common pitfalls and their antidotes

Mistake Alert! Offset these bloopers while handling exceptions:

  • Ignoring exception causes: Might mask the initial error cause. Uncover the mysteries with the __cause__ attribute on exceptions.
  • Eternal loop of circular references: Arises when exceptions hold tracebacks, including local variables that reference the exception. Use del to wipe the slate clean of local references.
  • Swallowing exceptions: Makes debugging as fun as finding a needle in a haystack. Document exceptions even if intentionally ignored.

Wisdom Nuggets

Pro tips for the Exception Handling Aficionado:

  • os.path.basename(): Want just the file name, not its life story (full path)?
    fname = os.path.basename(exc_tb.tb_frame.f_code.co_filename)
  • Exception hooks: Drag every uncaught exception into the spotlight with sys.excepthook.
  • Context managers: Use contextlib to create context managers that help you befriend exceptions.