Skip to content

Python pdb Modülü ile Debug Nasıl Yapılır?

Bu yazımızda python'da debugging(hata ayıklama) nasıl yapılır, temel komutlar nelerdir öğrenmeye çalışacağız. Debugging önemlidir çünkü kodunuzun beklendiği şekilde çalışmamasına neden olan hatanın tespit edilmesi ve çözüme kavuşturulması gereklidir. Python'daki dahili(built-in) modüllerden olan pdb modülü, debugging için gayet kullanışlı bir araçtır.

import pdb diyerek pdb'yi import ettikten sonra pdb.set_trace() fonksiyonunu çağırarak veya python3.7'den sonra gelen breakpoint() fonksiyonunu da kullanarak kodunuzun çalışmasını nerede duraklatmak ve hata ayıklamak istiyorsanız oraya ekleme yapabilirsiniz.

hello.py adında aşağıdaki gibi bir python dosyamız olsun. pdb'yi import edelim ve set_trace() fonksyionunu çağıralım

import pdb
pdb.set_trace()
print("Hello")
print("Adnan")
print("is writing Python code.")
- Bu kodu python3 hello.py diyerek çalıştırıyorum ve terminalde/console'da neler olduğunu beraber görelim.

dev@developers-MacBook-Pro pdb % python3 hello.py
> /Users/dev/webdev/snippets/pdb/hello.py(3)<module>()
-> print("Hello")
(Pdb)
- Kodumuz python3 yorumlayıcı tarafından çalıştırıldı ve print("Hello") satırında durdu. -> ile gösterilen satırın çalıştırılmaya hazır olduğu belirtilmektedir.

n harfine basıp Enter diyerek (n)ext diyebiliriz. Böylece çalıştırılmaya hazır olan satır çalışacak ve bir sonraki satıra geçilecektir.

dev@developers-MacBook-Pro pdb % python3 hello.py
> /Users/dev/webdev/snippets/pdb/hello.py(3)<module>()
-> print("Hello")
(Pdb) n
Hello
> /Users/dev/webdev/snippets/pdb/hello.py(4)<module>()
-> print("Adnan")
- Dikkat ettiyseniz ilk kez n harfine basıp Enter dedikten sonra Hello terminale bastırıldı/print edildi.

dev@developers-MacBook-Pro pdb % python3 hello.py
> /Users/dev/webdev/snippets/pdb/hello.py(3)<module>()
-> print("Hello")
(Pdb) n
Hello
> /Users/dev/webdev/snippets/pdb/hello.py(4)<module>()
-> print("Adnan")
(Pdb) c
Adnan
is writing Python code.
dev@developers-MacBook-Pro pdb % 
- c harfine basarak ©ontinue / çalışmaya devam et diyebiliriz. Böylece python dosyamız tamamiyle çalıştırılır ve programdan çıkılır.

sample_main.py ve sample_helper.py adında 2 adet python dosyamız olsun. Bu dosyaların içeriği aşağıdaki gibidir.

sample_helper.py

def mysum(p1, p2):
    print("computing start")
    result = p1 * p2
    print("Sum: ", result)
    print("computing finish")
- sample_main.py
from simple_helper import mysum

if __name__ == "__main__":

    print("main scope start")

    mysum(1400, 53) # expected result 1453

    print("main scope finish")
- Koda baktığınızda hatayı fark etmişsinizdir. simple_main.py çalıştırıldığı zaman beklenen sonucu print ediyor mu görelim:
dev@developers-MacBook-Pro pdb % python3.11 simple_main.py
main scope start
computing start
Sum:  74200
computing finish
main scope finish
- Görüldüğü üzere beklenen sonuç 1453 olması gerekirken 74200 çıkmış. Program sorunsuz olarak çalışıp sonlandırıldı ancak istediğimiz sonuç yazılmadı.

Gerçek hayatta büyük projelerin içerisinde geliştirme yaparken burda gördüğünüz gibi hatayı hemen fark edemeyebilirsiniz. Bu yanlış hesaplama yapan kodun büyük bir projeye ait olduğunu varsayalım ve hata ayıklama(debugging) yapalım.

simple_main.py içerisine breakpoint() ekliyorum (Not: import pdb; pdb.set_trace() diyerek de ekleme yapabilrdiniz.).

from simple_helper import mysum

if __name__ == "__main__":
    breakpoint()
    print("main scope start")

    mysum(1400, 53) # expected result 1453

    print("main scope finish")
- Kodu çalıştıralım

dev@developers-MacBook-Pro pdb % python3.11 simple_main.py
> /Users/dev/webdev/snippets/pdb/simple_main.py(5)<module>()
-> print("main scope start")
(Pdb) 
- Beklenildiği gibi ilk print ifadesinde program çalışması duraklatıldı. (n)ext diyerek devam edelim

dev@developers-MacBook-Pro pdb % python3.11 simple_main.py
> /Users/dev/webdev/snippets/pdb/simple_main.py(5)<module>()
-> print("main scope start")
(Pdb) n
main scope start
> /Users/dev/webdev/snippets/pdb/simple_main.py(7)<module>()
-> mysum(1400, 53) # expected result 1453
(Pdb) 
- mysum(1400, 53) satırı çalıştırılmak üzere.

Bu satırda (n)ext ile devam edildiğinde fonksiyon çalıştırılacak ve böylece fonksiyondan çıkılacak. Bir sonraki satıra geçilecek. Bu satırda (s)tep ile devam edildiğinde fonksiyon --Call-- yani çağırılma işlemi başlayacak. Yani fonksiyonun içerisine gidilecek. Böylece fonksiyon çalıştırılıp çıkılmadan fonksiyon içerisindeki satırları tek tek çalıştırabileceğiz. (s)tep ile devam edelim

dev@developers-MacBook-Pro pdb % python3.11 simple_main.py
> /Users/dev/webdev/snippets/pdb/simple_main.py(5)<module>()
-> print("main scope start")
(Pdb) n
main scope start
> /Users/dev/webdev/snippets/pdb/simple_main.py(7)<module>()
-> mysum(1400, 53) # expected result 1453
(Pdb) s
--Call--
> /Users/dev/webdev/snippets/pdb/simple_helper.py(2)mysum()
-> def mysum(p1, p2):
- Görüldüğü gibi debugger pointer(->) def mysum üzerinde durmaktadır. Burada fonksiyonun parametre değerlerini görme imkanımız var.

(a)rguments ile fonksiyon parametrelerini(argüman) ve değerlerini görebilirsiniz veya direkt argüman isimlerini yazıp enter'a basarak değerlerini görebilirsiniz.

dev@developers-MacBook-Pro pdb % python3.11 simple_main.py
> /Users/dev/webdev/snippets/pdb/simple_main.py(5)<module>()
-> print("main scope start")
(Pdb) n
main scope start
> /Users/dev/webdev/snippets/pdb/simple_main.py(7)<module>()
-> mysum(1400, 53) # expected result 1453
(Pdb) s
--Call--
> /Users/dev/webdev/snippets/pdb/simple_helper.py(2)mysum()
-> def mysum(p1, p2):
(Pdb) a
p1 = 1400
p2 = 53
(Pdb) p1
1400
(Pdb) p2
53
- (l)ist source code komutu ile 11 satırlık bir kod bloğunu görüntüleyebiliriz. Böylece kodda bulunduğumuz yeri çalışacak sonraki satırları görebiliriz.

--Call--
> /Users/dev/webdev/snippets/pdb/simple_helper.py(2)mysum()
-> def mysum(p1, p2):
(Pdb) a
p1 = 1400
p2 = 53
(Pdb) p1
1400
(Pdb) p2
53
(Pdb) l
  1  
  2  -> def mysum(p1, p2):
  3         print("computing start")
  4         result = p1 * p2
  5         print("Sum: ", result)
  6         print("computing finish")
  7  
[EOF]
(Pdb) 
- (ll)longlist komutu ile bulunulan mevcut fonksiyon veya frame'in tüm kaynak kodunu gösterir.

(Pdb) ll
  2  -> def mysum(p1, p2):
  3         print("computing start")
  4         result = p1 * p2
  5         print("Sum: ", result)
  6         print("computing finish")
(Pdb) n
> /Users/dev/webdev/snippets/pdb/simple_helper.py(3)mysum()
-> print("computing start")
(Pdb) 
computing start
> /Users/dev/webdev/snippets/pdb/simple_helper.py(4)mysum()
-> result = p1 * p2
- (n)ext dedikten sonra hiçbir şey yazmadan tekrar Enter'a basarsak en son (n)ext komutu çalıştırıldığı için yine (n)ext komutu çalıştırılır.

Kodda bulunduğunuz yeri tekrar hatırlamak için (w)here komutunu kullanabilirsiniz.

(Pdb) w
  /Users/dev/webdev/snippets/pdb/simple_main.py(7)<module>()
-> mysum(1400, 53) # expected result 1453
> /Users/dev/webdev/snippets/pdb/simple_helper.py(4)mysum()
-> result = p1 * p2
- Debugging yaparken değişkenler oluşturabilir, onlara değerler tayin edebiliriz(assign). Aşağıda debugging yaparken bir dictionary oluşturma örneği ve (pp)prettyprint ile bu dict objesini yazıdrmayı görüyorsunuz.
(Pdb) w
  /Users/dev/webdev/snippets/pdb/simple_main.py(7)<module>()
-> mysum(1400, 53) # expected result 1453
> /Users/dev/webdev/snippets/pdb/simple_helper.py(4)mysum()
-> result = p1 * p2
(Pdb) dict_defined_while_debugging = {"name":"adnan", "surname":"kaya", "hire-status":"open to work on upwork", "role":"Python Web Developer", "skills":"python, django, flask, scrapy, docker, tkinter, linux"}
(Pdb) pp dict_defined_while_debugging                                                                                                                      
{'hire-status': 'open to work on upwork',
 'name': 'adnan',
 'role': 'Python Web Developer',
 'skills': 'python, django, flask, scrapy, docker, tkinter, linux',
 'surname': 'kaya'}
(Pdb) 
- (b)reakpoint satir_no diyerek debugging yaparken ekstra breakpointler ekleyebiliriz. Böylece ©ontinue diyerek devam edersek bile kod, breakpoint eklediğimiz yerde tekrar duraklatılacaktır.
(Pdb) l
  1  
  2     def mysum(p1, p2):
  3         print("computing start")
  4  ->     result = p1 * p2
  5         print("Sum: ", result)
  6         print("computing finish")
  7  
[EOF]
(Pdb) b 6
Breakpoint 1 at /Users/dev/webdev/snippets/pdb/simple_helper.py:6
(Pdb) b
Num Type         Disp Enb   Where
1   breakpoint   keep yes   at /Users/dev/webdev/snippets/pdb/simple_helper.py:6
Pdb) ll
  2     def mysum(p1, p2):
  3         print("computing start")
  4  ->     result = p1 * p2
  5         print("Sum: ", result)
  6 B       print("computing finish")
- (b)reakpoint yazarak eklenen breakpointleri görebiliyoruz. Yanına satır numarası eklediğimizde belirttiğimiz satıra breakpoint eklenir. ll ile kontrolde de görebileceğiniz gibi 6. satırın sağında B yani breakpoint var.

Not: Ayrıca b module:satır_no diyerek istediğimiz bir modülün istediğimiz satırına breakpoint koyabiliriz.

Hata aldığımız satırı ve yanlış matematiksel operatörü( + yerine * ) gördüğümüze göre debugging durumundan çıkıp kodu düzeltebiliriz.

(q)uit diyerek çıkabilirsiniz veya ©ontinue diyerek kodun çalışmasını ve programın sonlandırılmasını sağlayabilirsiniz. Veya geçici süreliğine (n)ext dedikten ve 4. satırı çalıştırdıktan sonra result = p1 + p2 diyerek elle yazıp Enter'a bastıktan sonra bulunduğunuz debugging ortamında result'u manipüle ederek çıktının sonucunu görebilirsiniz. Bunu deneyelim

(Pdb) n
computing start
> /Users/dev/webdev/snippets/pdb/simple_helper.py(4)mysum()
-> result = p1 * p2
(Pdb) n
> /Users/dev/webdev/snippets/pdb/simple_helper.py(5)mysum()
-> print("Sum: ", result)
(Pdb) result = p1 + p2
(Pdb) n
Sum:  1453
> /Users/dev/webdev/snippets/pdb/simple_helper.py(6)mysum()
-> print("computing finish")
(Pdb) c
computing finish
main scope finish
- Gördüğünüz gibi sonucun manipüle edilmesini sağladık ve yaptığımız değişikliğin beklediğimiz sonucu verip vermeyeceğini de görmüş olduk.

Bu şekilde inceleme, manipüle etme gibi operasyonlar gerçekleştirerek hata ayıklama işlemlerini projelerinizde gerçekleştirebilirsiniz.

Daha fazla bilgi edinmek için https://docs.python.org/3/library/pdb.html modülünü okuyabilirsiniz. Ek olarak https://pypi.org/project/ipdb/ python paketini de incelemek ve kullanmak isteyebilirsiniz.