Quark의 개요: 파이썬 트레이스백 객체
출처: Dev.to
Quark’s Outlines: Python Traceback Objects
개요, 연대기, 문제점 및 해결책
Python 프로그램을 실행하다가 오류가 발생하면 Python은 스택 트레이스를 보여줍니다. 스택 트레이스는 오류가 어디서 발생했는지를 알려줍니다. 어떤 함수가 실패했는지, 어떤 줄이 원인인지 알려줍니다. Python traceback 객체는 그 정보를 담고 있습니다.
각 traceback 객체는 오류로 이어진 함수 호출 체인에서 한 단계씩을 나타냅니다. Python은 스택을 풀어내면서 이 체인을 구축합니다. traceback에는 함수의 프레임, 줄 번호, 그리고 실패한 바이트코드 명령이 포함됩니다.
Python은 traceback 객체를 이용해 무엇이 잘못됐는지 검사할 수 있게 해줍니다.
import traceback
try:
1 / 0
except ZeroDivisionError as e:
tb = e.__traceback__
print("Line:", tb.tb_lineno)
위 traceback은 오류가 발생한 줄을 보여줍니다. 이를 따라가면 Python이 어떻게 그 지점에 도달했는지 확인할 수 있습니다.
traceback 객체는 오류가 발생했을 때 활성화돼 있던 프레임을 저장합니다. 또한 그 프레임에서 실행된 마지막 명령과 줄 번호도 저장합니다. 오류 이전에 다른 호출이 있었다면, traceback은 tb_next를 통해 앞선 traceback과 연결됩니다.
traceback을 프로그램이 실패하기 전까지 어떤 함수에서 어떤 함수로 이동했는지를 보여주는 빵 부스러기 흔적이라고 생각하면 됩니다.
Python traceback 객체는 오류까지 이어지는 프레임 체인을 형성합니다.
def a():
b()
def b():
1 / 0
try:
a()
except ZeroDivisionError as e:
tb = e.__traceback__
print("Error in:", tb.tb_frame.f_code.co_name)
위 코드는 오류가 발생한 함수 이름을 출력합니다. tb_next를 사용해 뒤로도 탐색할 수 있습니다.
Python traceback 객체는 어디서 오는가?
Python traceback 객체는 이전 프로그래밍 도구들의 스택 트레이스 개념을 이어받았습니다. 오류가 어디서, 어떻게 발생했는지를 볼 수 있게 해줍니다. 아래 연대기는 Python에서 traceback 지원이 어떻게 발전했는지를 보여줍니다.
- 1960 — 스택 트레이스 메시지가 컴파일 언어용 디버깅 도구에 처음 등장.
- 1970년대 — ALGOL·Pascal 도구에서 구조화된 오류 보고가 보편화.
- 1991 — Python에 예외와 모든 잡히지 않은 오류에 대한 내장 스택 트레이스가 추가.
- 1994 — 사용자가 예외를 직접 탐색할 수 있도록
traceback모듈이 추가. - 2000 — 예외에
__traceback__속성이 도입되어 traceback 객체를 보관. - 2010 —
raise from을 통해 체인 예외가 가능해지면서 traceback 링크가 보존. - 2020 —
traceback.TracebackException등 도구에서 구조화된 오류 분석을 위한 traceback 접근성이 향상.
Python traceback 객체를 올바르게 사용하는 방법
Python traceback 객체는 오류가 시작된 위치, 발생한 위치, 그리고 그때 Python이 무엇을 하고 있었는지를 알려줍니다. 오류가 발생하면 Python은 traceback을 구축합니다. 이 traceback을 통해 실행 흐름을 추적하고 문제를 해결할 수 있습니다. 아래 예시들은 실제 코드에서 traceback 객체를 활용하는 방법을 보여줍니다.
1️⃣ 오류가 발생한 정확한 줄 번호를 프로그램matically 얻고 싶을 때
문제: 오류가 난 정확한 줄 번호를 화면에 표시되는 스택 트레이스를 읽지 않고 얻고 싶다.
해결: tb_lineno 속성을 사용하면 된다.
try:
int("nope")
except ValueError as e:
tb = e.__traceback__
print("Line number:", tb.tb_lineno)
# prints:
# Line number: 2
tb_lineno 속성은 Python이 오류와 함께 멈춘 정확한 줄을 반환합니다.
2️⃣ 전체 traceback을 읽지 않고 오류를 일으킨 함수 이름만 알고 싶을 때
문제: 전체 traceback을 살펴보지 않고 오류를 일으킨 함수 이름을 알고 싶다.
해결: traceback의 프레임에서 코드 객체의 이름을 추출한다.
def crash():
return 1 / 0
try:
crash()
except ZeroDivisionError as e:
tb = e.__traceback__
print("Function:", tb.tb_frame.f_code.co_name)
# prints:
# Function: crash
프레임은 오류가 발생했을 때 실행 중이던 함수를 알려줍니다.
3️⃣ 여러 함수를 거쳐 발생한 오류의 전체 호출 스택을 추적하고 싶을 때
문제: 오류까지 이어진 전체 호출 스택을 모두 확인하고 싶다.
해결: tb_next를 이용해 traceback 체인을 따라간다.
def a():
b()
def b():
c()
def c():
1 / 0
try:
a()
except ZeroDivisionError as e:
tb = e.__traceback__
while tb:
print("Function:", tb.tb_frame.f_code.co_name)
tb = tb.tb_next
# prints:
# Function: c
# Function: b
# Function: a
tb_next 링크를 통해 오류 지점부터 역방향으로 호출 체인을 탐색할 수 있습니다.
4️⃣ 오류를 파일에 기록하는 도구를 만들고 전체 스택 트레이스를 포함하고 싶을 때
문제: 오류 메시지만이 아니라 전체 traceback을 직접 포맷해서 출력하고 싶다.
해결: traceback 모듈을 사용해 객체 기반 traceback을 포맷한다.
import traceback
try:
open("/this/does/not/exist.txt")
except Exception as e:
traceback.print_tb(e.__traceback__)
# prints:
# File "example.py", line 3, in
# open("/this/does/not/exist.txt")
프로그램을 종료하지 않고도 깔끔한 traceback을 얻을 수 있습니다.
5️⃣ 인터랙티브 환경(노트북 등)에서 프로그램이 멈춘 뒤 마지막 traceback에 접근하고 싶을 때
문제: 오류가 발생한 직후에 예외를 저장하지 않았지만, 프로그램이 멈춘 뒤에 마지막 traceback을 읽고 싶다.
해결: 인터랙티브 모드에서는 sys.last_traceback에 마지막 traceback이 보관된다.
import sys
# 인터랙티브 모드에서만 동작:
# sys.last_traceback
프로그램이 이미 중지된 경우에도 traceback 세부 정보를 복구할 수 있습니다. 단, Python이 인터랙티브 모드에서 실행 중이며 오류가 잡히지 않은 경우에만 동작합니다.
이 내용이 도움이 되었나요? 아래 좋아요 버튼을 눌러 주세요. 댓글로 여러분의 생각도 공유해 주세요! 더 많은 콘텐츠를 원하시면 구독을 잊지 마세요. 읽어 주셔서 감사합니다!
Mike Vincent는 미국 캘리포니아주 로스앤젤레스 출신의 소프트웨어 엔지니어이자 앱 개발자입니다. Mike Vincent에 대한 자세한 내용은 아래를 참고하세요.