파이썬의 `breakpoint()`
`breakpoint()` 소개: 현대 파이썬에 내장된 디버깅 초능력
왜 알아둬야 하나
파이썬 3.7부터 소박한 breakpoint() 호출이 언어에 그대로 들어왔다. 여기저기 import pdb; pdb.set_trace()를 흩뿌릴 필요 없이 디버거로 즉시 들어갈 수 있다. 실행을 멈추고, 이것저것 들여다보고, 아무 일 없었다는 듯 재개하는 가장 빠른 방법이다.
시간을 멈추는 한 줄
def weird_math(x):
result = (x ** 2) // 3
breakpoint() # ↓ 프로그램이 여기서 멈춘다
return result + 42
스크립트를 실행하면 파이썬이 그 줄에서 익숙한 pdb 프롬프트로 데려다준다. 이제 지역 변수를 확인하고(p locals()), 변수를 바꾸고(x = 99), 한 줄씩 진행하거나(n / s) 계속 실행할 수 있다(c).
팁: 탐색용 코드에는
breakpoint()를 마음껏 뿌려도 되고, 나중에 import 정리 없이 그냥 지우면 된다.
pdb / ipdb 명령어 치트시트
아래는 간결하지만 빠짐없는 참고표다.
| 명령 | 긴 형태 | 하는 일 | 짧은 예시 |
|---|---|---|---|
n | next | 현재 프레임의 다음 줄 실행 (함수 본문은 건너뜀). | len() 안으로 들어가지 않고 for 루프 진행 |
s | step | 다음 함수 호출 안으로 진입. | calculate()가 왜 None을 주는지 안으로 들어가 보기 |
c | continue | 다음 breakpoint(또는 프로그램 끝)까지 재개. | 변수만 확인하고 디버거 빠져나오기 |
l | list | 현재 줄 주변 소스 표시. 줄 번호를 붙이면 → l 120. | “내가 지금 정확히 어디지?” |
ll | — | 현재 함수 전체 표시. | 50줄짜리 핸들러의 전체 문맥 훑기 |
p expr | print expr | 표현식을 평가하고 출력. | p len(data) |
pp expr | prettyprint expr | pprint.pformat으로 객체를 예쁘게 출력. | pp settings |
! stmt | — | 임의의 파이썬 문장 실행. | ! filtered = [x*2 for x in data] |
b line | break line | 현재 파일에 breakpoint 설정. | b 210 |
b file:line | — | 다른 파일에 breakpoint. | b utils.py:45 |
b func | — | 함수 진입점에 breakpoint. | b DataLoader.load |
b | — | 모든 breakpoint과 번호 나열. | 빠른 개관 |
cl num | clear num | num 번 breakpoint 제거. | cl 3 |
disable num / enable num | — | 삭제하지 않고 breakpoint 토글. | 비싼 중단을 잠시 건너뛰기 |
tbreak line | — | 일회성 breakpoint. 한 번 걸리면 자동 해제. | 루프가 처음 도는 순간만 멈추기 |
u | up | 한 프레임 위로 이동 (호출자). | 호출 스택에서 인자 확인 |
d | down | 한 프레임 아래로 이동 (피호출자). | 방금 들어갔던 프레임으로 복귀 |
args | — | 현재 프레임의 함수 인자 출력. | def foo(a, b=3) 안에서 args |
where / w | — | 전체 스택 트레이스 (현재 줄은 →로 표시). | 호출 사슬 스냅샷 |
bt | backtrace | where의 별칭. | |
retval | — | 스텝 중 마지막 return이 반환한 값 표시. | parse()가 무엇을 만들었는지 확인 |
q | quit | 디버그 세션 중단하고 프로그램 종료. | 비상 탈출 |
jump line | j line | 실행 커서를 line 으로 이동 (같은 파일). 위험하지만 강력하다. | 분기 건너뛰기: jump 180 |
run args | — | 주어진 인자로 프로그램 재시작. | CLI 스크립트에서 run --fast 500 |
debug expr | — | 표현식을 자체 미니 디버거에서 평가. | debug some_generator() |
help | help topic | 내장 도움말 (help break, help list, …). | 미묘한 플래그 발견하기 |
팁: 어떤 명령이든 앞에 반복 횟수를 붙일 수 있다.
10 n은 열 줄을 건너뛰고,2 u는 두 프레임을 올라간다.
내부에서 벌어지는 일
breakpoint()는 그저 sys.breakpointhook()를 호출할 뿐이고, 기본 구현이 pdb를 띄운다. 그래서 이런 게 가능하다:
-
모든 breakpoint을 동적으로 비활성화:
export PYTHONBREAKPOINT=0 # POSIX 셸 set PYTHONBREAKPOINT=0 # 윈도우 cmd모든
breakpoint()가 아무 일도 하지 않게 된다. CI 파이프라인에서 유용하다. -
다른 디버거로 교체 (예:
ipdb,pudb,web-pdb):export PYTHONBREAKPOINT=ipdb.set_trace이제 같은 코드 줄이 소스 파일을 건드리지 않고도 원하는 디버거를 연다.
-
런타임에 커스텀 훅 제공:
import sys, logging def log_only(*args, **kwargs): logging.warning("breakpoint() hit!") sys.breakpointhook = log_only
실제로 쓰게 되는 응용 기법
| 목적 | 빠른 레시피 |
|---|---|
| 조건이 터질 때만 진입하기 | if total < 0: breakpoint() |
| 리스트 컴프리헨션 안에서 디버거 진입 | lambda 사용: [f(x) if x>0 else (lambda: (breakpoint(), None)[1])() for x in data] |
| 컨테이너 안에서 원격 디버깅 | export PYTHONBREAKPOINT=remote_pdb.set_trace + remote-pdb 서버 실행 |
| 비동기 코드를 매끄럽게 진행 | python -m pdb -m your_script와 조합하거나, 코루틴을 더 잘 이해하는 ipdb를 쓴다 |