Visit Pythonology.org for more information about Python

파이썬 디버깅

Home
갱신: 2005-09-01
작성 스티픈 퍼그(Stephen Ferg) — steve(at)ferg.org
한글판 johnsonj 2006.11.13


Creative Commons Licence 이 글은 창조적 공공재 2.0 라이센스 아래에 있다. 마음대로 복사, 배포, 진열해도 좋으며, (번역을 포함하여) 파생 작업을 허용한다. 그렇다면, 원저자를 명시해야 한다. 저자는 특별히 선생님들이 게시하고, 개작하며, 그리고 이 작품 일부 또는 전부를 교실에서 사용하거나 학생들에게 가르치기 위하여 배포하는 것을 허용한다 (권장한다).


프로그래머라면 심각한 프로그램 개발에 제일 필요한 것이 디버거이다.

파이썬에는 디버거가 있다. pdb라 불리우는 모듈로 사용이 가능하다 (당연히 "파이썬 디버거(Python DeBugger)"의 약자이다!). 불행하게도, pdb에 대한 대부분의 연구는 파이썬 초보자에게는 별로 유용하지 않다 -- 대부분은 아주 거칠고 그냥 파이썬 라이브러리 참조 매뉴얼의 pdb 설명을 재언급한 것에 불과하다. 내가 발견한 좋은 연구는 파이썬 2.1 바이블 제 27장의 네 페이지에 있다.

그래서 여기에 개인적으로 부드럽게 pdb 사용법을 소개한다. IDE를 사용하고 있지 않다고 간주하겠다 -- 파이썬을 텍스트 편집기로 코딩하고 명령어 줄에서 파이썬 프로그램을 실행한다고 간주한다.

REXX 배경에서 파이썬으로 온 사람의 관점에서 설명하겠다. 나의 연구가 특별히 REXX에서 파이썬으로 이주하는 다른 사람들에게 유용하기를 바란다. 그러나 파이썬을 배우려는 사람 누구에게나 쓸모가 있도록 설명할 수 있기를 바란다.

이 글은 아직 초벌 문서이며, 여전히 미완성임을 주의하자. 잘못된 것이 있다면, 전자우편으로 steve@ferg.org에 알려주시기를 바란다.

기타 다른 디버거 자원


시작하기 -- pdb.set_trace()

아주 쉬운 방식으로 파이썬 디버거를 사용하는 법을 보여주겠다. (이것은 REXX의 "trace ?" 특징으로 작업하는 것과 비슷하다.)

  1. epdb1.py라는 간단한 프로그램으로 시작하자.
        # epdb1.py --  pdb 파이썬 디버거 실험
        a = "aaa"
        b = "bbb"
        c = "ccc"
        final = a + b + c
        print final
        
  2. 다음 서술문을 파이썬 프로그램의 서두에 끼워넣자. 이 서술문은 파이썬 디버거 모듈 pdb를 반입한다.
    import pdb
  3. 이제 추적을 시작할 지점을 찾아, 다음 코드를 삽입하자:
    pdb.set_trace()
    이 프로그램은 다음과 같이 보일 것이다 (소스 코드를 보려면 여기를 클릭할 것.)
            # epdb1.py -- pdb 파이썬 디버거 실험
            import pdb
            a = "aaa"
            pdb.set_trace()
            b = "bbb"
            c = "ccc"
            final = a + b + c
            print final
            
  4. 이제 프로그램을 평소처럼 명령어 줄에서 실행하자. 아마도 다음과 같이 보일 것이다:
            PROMPT> python epdb1.py

프로그램이 pdb.set_trace()란 줄을 만나면 추적을 시작할 것이다. 다시 말해, (1) 멈추어서, (2) "현재 상태"를 보여주고 (즉, 다음 실행할 줄) 그리고 (3) 입력을 기다린다. pdb 프롬프트가 보일 것이다. 다음과 같이 말이다:

           (Pdb)

다음 서술문 실행하기... "n" (next)

(Pdb) 프롬프트에서, 키보드 소문자 "n" ("next")을 누른 다음, ENTER 키를 누르자. 이렇게 하면 pdb는 현재 서술문을 실행한다. 이것을 기억하자 -- "n"을 누르고, ENTER.

결국 프로그램의 끝에 다다른다. 그리고 프로그램은 종료한 다음 평상시의 명령어 프롬프트를 돌려준다.

축하한다! 방금 첫 디버깅을 마쳤다!

마지막 디버깅 명령어를 반복하기... ENTER

이번에는, 앞서 한 일을 똑 같이 해보자. 프로그램을 실행하자. (Pdb) 프롬프트에서, 키보드 소문자 "n" ("next"라는 뜻)을 누른 다음, ENTER 키를 누르자.

그러나 이번에는 "n"다음 ENTER를 누르고서 아무 것도 하지말자. 대신에 (Pdb) 프롬프트가 나오면, 그냥 ENTER를 눌러보자. pdb가 진행되는 것을 보실 수 있을 것이다. 마치 "n"을 누른 것처럼 말이다.   그래서 이것은 간편한 팁이다 #1:

아무것도 없이 그냥 ENTER만 누르면, pdb는 가장 마지막 명령어를 재실행한다.

이 경우, 명령어는 "n"이었고, ENTER만 눌러서 프로그램을 계속 진행시킬 수 있었다.

마지막 줄을 넘길 때 ("print" 서술문이 있는 줄), 그 줄이 실행되고 인쇄 결과가 ("aaabbbccc") 화면에 표시되는 것을 보았다.

완전히 끝내기... "q" (quit)

디버거는 모든 종류의 일을 할 수 있다. 그 중에는 완전히 신비로운 것도 있다. 그래서 지금 배워야 할 가장 중요한 일은 -- 다른 무엇보다도 -- 디버깅을 끝내는(quit) 법이다!

끝내기는 쉽다. (Pdb) 프롬프트가 보이면, 그냥 "q" ("quit")를 누르고 ENTER 키를 누르자. Pdb는 종료하고 다시 명령어 프롬프트로 돌아올 것이다. 시도해 보고, 어떻게 작동하는지 살펴보자.

변수의 값을 인쇄하기... "p" (print)

(Pdb) 프롬프트에서 할 수 있는 가장 유용한 일은 변수의 값을 인쇄하는 것이다.

(Pdb) 프롬프트에서, "p" ("print")를 누른 다음 인쇄하고 싶은 변수의 이름을 입력하자. 물론, ENTER 키로 끝내야 한다.

여러 변수들을 인쇄할 수 있다는 것을 주목하자. 이름을 쉼표로 가르면 된다 (보통의 파이썬 "print" 서술문과 똑같이 말이다). 예를 들어, a, b, 그리고 변수의 값을 이렇게 인쇄할 수 있다:

          p a, b, c

pdb가 한 줄을 보여줄 때?

프로그램을 진행시키다고 다음과 같은 줄을 보았다고 해보자

          final = a + b + c

그리고 pdb에게 다음과 같은 명령을 준다고 해보자.

          p final

NameError 예외를 맞이할 것이다. 이것은 줄이 보이기는 하지만 아직 실행되지 않았기 때문이다. 그래서 final 변수는 아직 생성되지 않았다.

이제 "n"과 ENTER를 눌러 다음 줄을 실행하자. 다음 "p final" 명령을 다시 시도하자. 이 번에는 pdb가 final의 값을 보여 주는데, 그 값은 "aaabbbccc"이다.

REXX에서 파이썬으로 온 분들에게, 이것은 파이썬과 REXX의 차이를 보여준다. REXX에서는 "trace ?i"를 사용하여 추적할 때, REXX는 그 줄을 실행한 후에 보여준다 (그리고 매개 값들은 실행될 때 생산된다). 파이썬에서 pdb는 실행하기 전에 그 줄을 보여준다.

(Pdb) 프롬프트 끄기... "c" (continue)

아마도 "q" 명령어를 사용하면 아주 거칠게 pdb를 빠져 나오는 것을 볼 수 있을 것이다 -- 기본적으로, 프로그램이 충돌한다.

그냥 단순히 디버깅을 끝내고 싶지만, 프로그램은 계속해서 실행되도록 놓아두고 싶다면, (Pdb) 프롬프트에서 "c" ("continue") 명령어를 사용하자. 이렇게 하면 프로그램은 정상적으로 계속 실행될 것이다. 디버깅 때문에 멈출 필요없이 말이다. 계속해서 끝까지 실행할 수 있다. 아니면, pdb.set_trace() 서술문이 회돌이 안에 있다면, 또다시 맞이할 것이다. (Pdb) 디버깅 프롬프트가 다시 한 번 나타날 것이다.

(REXX 프로그래머에게 -- "c"를 pdb 프롬프트에서 입력하면 REXX 상호대화 추적 프롬프트에서 "trace off"을 입력한 것과 효과가 똑 같다.)

어디에 있는지 보려면... "l" (list)

디버깅하다 보면, 많은 것들이 화면에 출력된다. 그리고 실제로 프로그램에서 어디쯤에 있는지 알기 어렵게 된다. 그런 상황에 "l" ("list") 명령어가 필요하다. (소문자 "L"임에 주목, 숫자 "일" 또는 대문자 "I"가 아님.)

"l"는 소스 코드에서 실행중인 구역을 보여준다. 기본으로 11 줄을 보여준다. 다음 실행할 코드 줄은 ("현재 줄") 정중앙에 있고 작은 화살표 "-->"가 있어서 그 위치를 지시한다.

그래서 pdb와의 전형적인 상호작용은 다음과 같다

서브루틴으로 들어가기... "s" (step into)

결국, 더 프로그램을 디버그할 필요가 있을 것이다. 서브루틴을 사용하는 프로그램을 말이다. 그리고 종종, 발견하고자 하는 문제가 서브루틴에 숨어 있는 경우가 있다. 다음 프로그램을 살펴보자. (소스 코드는 여기를 클릭하자.)

        # epdb2.py -- pdb 파이썬 디버거 실험
        import pdb

        def combine(s1,s2):      # combine이라는 서브루틴을 정의...
            s3 = s1 + s2 + s1    # s2를 s1 사이에 삽입하고 ...
            s3 = '"' + s3 +'"'   # 겹따옴표로 둘러싸서...
            return s3            # 돌려준다.

        a = "aaa"
        pdb.set_trace()
        b = "bbb"
        c = "ccc"
        final = combine(a,b)
        print final
        

(Pdb) 프롬프트에서 "n" 명령어를 사용하여 프로그램을 돌아다닐 때, 서브루틴을 호출하는 서술문을 만나면 -- 예를 들어final = combine(a,b) 서술문 -- pdb는 그것을 다른 서술문과 똑같이 취급한다. 즉, 그 서술문은 실행되고 다음 서술문으로 이동한다 -- 이 경우, print final로 간다.

그러나 서브루틴에 문제가 있다고 가정해 보자. 우리의 경우, combine 서브루틴에 문제가 있다고 가정해 보자. -- final = combine(a,b) 서술문을 만나면 -- combine 서브루틴으로 들어가(step into), 그 안에서 디버깅을 계속하고 싶을 것이다.

자, 역시 그렇게 할 수 있다. "s" ("step into") 명령어를 사용하면 된다.

함수 호출이 관련되지 않은 서술문을 호출하면, "n"과 "s"는 똑 같은 일을 한다 -- 다음 서술문으로 이동한다. 그러나 함수를 호출하는 서술문을 실행하면, "s"는 "n"과 다르게 그 서브루틴으로 들어간다(step into). 우리의 경우, final = combine(a,b) 서술문을 "s"를 사용하여 실행한다면, pdb가 보여줄 다음 서술문은 combine 서브루틴의 첫 서술문이 될 것이다:

               def combine(s1,s2): 

그리고 거기에서 계속 디버깅을 할 것이다.

계속하되... 현재 서브루틴의 끝가지만 계속한다... "r" (return)

서브루틴을 들어가기 위해 "s"를 사용할 때, 종종 서브루틴에 사로잡힐 것이다. 관심있는 코드 부분은 살폈고, 이제 서브루틴에서 관심없는 부분을 한 번에 실행할 필요가 있다.

이런 경우, 서브루틴의 끝 바로 앞까지 건너뛰고 싶을 것이다. 다시 말해, "c" ("continue") 명령어 같은 것을 원하지만, 그냥 서브루틴의 끝까지만 계속하고 싶고, 거기에서부터 코드 탐사를 재개하고 싶다.

"r" ("return" 또는 "continue until return")로 그렇게 할 수 있다. 서브루틴 안에 있고 (Pdb) 프롬프트에서 "r" 명령어를 누르면, pdb는 서브루틴의 끝까지 실행을 계속한다. 그 시점에서 -- 호출 루틴으로 돌아갈 준비가 된 지점에서 -- 멈추고 다시 (Pdb) 프롬프트가 보인다. 그러면 코드 탐사를 재개할 수 있다.

(Pdb) 프롬프트에서 무엇이든 할 수 있다 ...

가끔 다음과 같은 상황에 처할 것이다 -- 문제를 발견한 것 같다. "aaa"라는 값을 변수 var1에 할당하는 서술문이 문제다. 그리고 그 때문에 프로그램이 망가졌다. "bbb"를 var1에 할당해야 한다.

... 적어도, 무엇이 문제인지는 확실히 안다...

이제 문제를 찾았으므로 "bbb"를 var1에 할당하고, 프로그램이 이제 제대로 실행되는 지 확인한다.

가능하다!

(Pdb) 프롬프트에서 한가지 멋진 일은 무엇이든 할 수 있다는 것이다 -- (Pdb) 프롬프트에서 어떤 명령어도 마음대로 내릴 수 있다. 그래서 예를 들면, 다음 명령어를 (Pdb) 프롬프트에서 입력해 보자.

               (Pdb) var1 = "bbb"

그러면 계속해서 프로그램을 탐사할 수 있다. 아니면, 더 모험을 감행할 수도 있다 -- "c" 로 디버깅을 끄고, 프로그램이 제대로 끝나는지 알아 보자!

... 그러나 조심하자! 1

(Pdb) 프롬프트에서 무엇이든 할 수 있다. 다음과 같이, 변수 b에다 새로운 값, 예를 들어 "BBB"를 설정하기로 결정했다면:

               (Pdb) b = "BBB"

이렇게 하면, pdb는 '= "BBB" '라는 이름의 객체를 찾을 수 없다면서 이상한 에러 메시지를 산출한다. 왜 그럴까???

무슨 일인가 하면 pdb는 정지점을 설정하고 보여주는 b 명령어를 실행하려고 시도한다(아직 언급하지 않은 명령어임). 줄 나머지를 b 명령어에 건네는 인자로 이해하고, 그래서 지시한(그렇게 생각한) 객체를 발견할 수 없다. 그래서 에러 메시지를 산출한다.

어떻게 새로운 값을 b에 할당할 수 있을까? 느낌표와 함께 명령을 시작하면 된다(!).

               (Pdb)!b = "BBB"

느낌표는 pdb에게 다음부터는 pdb 명령어가 아니라 파이썬 서술문이라고 알려준다.

맺는 말

자. 이제 끝났다. 도움말, 익명, 그리고 정지점등과 같이 아직 언급하지 않은 주제가 많다. 더 자세한 정보는 www.python.org/doc/current/lib/debugger-commands.html에 있는 pdb 명령어에 대한 온라인 도움말을 참조하자. 게다가, 제레미 존스(Jeremy Jones)의 글 파이썬 상호대화 디버깅을 추천한다.

모쪼록 pdb에 대한 이 개론에서 빠르고 편안하게 디버거를 기동시켜 실행하실 수 있기를 바란다. 행운을 빈다!

—스티브 퍼그(Steve Ferg)


1 이 섹션 정보에 대하여 딕 모리스(Dick Morris)에게 감사한다.