2.41. Hata Ayıklama

Kamerada başarısız olan betiklerin çoğu üç yoldan biriyle başarısız olur: bir istisna fırlatırlar, yanlış bir değer üretirler veya askıda kalırlar. Her birinin farklı bir araç seti vardır.

2.41.1. Bir traceback okuma

Bir betik istisna fırlattığında ve istisnayı hiçbir şey ele almadığında, REPL veya IDE bir traceback yazdırır – en dıştaki betikten istisnayı fırlatan satıra kadar olan çağrı zincirinin bir kaydı.

Bir traceback aşağıdan yukarıya okunur:

  • En alt satır istisna sınıfını ve mesajını adlandırır (ValueError: invalid literal for int()...).

  • Onun üzerindeki her File "...", line N, in <name> bloğu bir çerçevedir (frame) – yukarı çıktıkça bir çağrı daha derine inilir.

  • En üstteki çerçeve betiğin başladığı yerdir; en alttaki çerçeve ise hatanın tetiklendiği yerdir.

Neyin yanlış gittiğini öğrenmek için önce en altı okuyun, ardından betiğin oraya nasıl geldiğini görmek için yukarı doğru ilerleyin. Satır numaraları betikteki tam kaynak konumlarını gösterir.

2.41.3. Bir nesneyi keşfetme

İki yerleşik fonksiyon “bu şeyle ne yapabilirim” sorusunu yanıtlar:

  • dir() – bir nesnede tanımlı her ismin listesini döndürür: metotlar, öznitelikler, dunder’lar, hepsi.

  • help() – bir fonksiyonun, metodun veya sınıfın docstring’ini (ve CPython’da imzasını) yazdırır.

Bunları birlikte kullanın: dir ismi bulur, help ne yaptığını açıklar.

2.41.3.1. dir ile bir isim bulma

>>> dir([1, 2, 3])
['__add__', '__class__', '__contains__', '__delitem__',
 '__eq__', '__ge__', ..., 'append', 'clear', 'copy',
 'count', 'extend', 'index', 'insert', 'pop', 'remove',
 'reverse', 'sort']

Listenin ilk kısmı her nesneden miras alınan dunder metotlarıdır; aranmaya değer isimler genellikle bunlardan sonra gelir. dir her şeyde çalışır – bir sınıf, bir örnek, bir modül, bir yerleşik tip:

>>> import json
>>> dir(json)
['__name__', 'dump', 'dumps', 'load', 'loads']

Bu ikinci biçim, REPL’den çıkmadan bir modülün gerçekte hangi üst düzey isimleri açığa çıkardığını öğrenmenin yoludur.

2.41.3.2. help ile araştırma

dir bir aday ortaya çıkardıktan sonra, help onu açıklar:

>>> help(str.split)
split(sep=None, maxsplit=-1)
    Return a list of the words in the string, ...

MicroPython’da help CPython’dakinden daha sadedir – bazen yalnızca imza, bazen tek satırlık bir docstring, yerleşik C fonksiyonları için bazen hiçbir şey. Yine de IDE ipucu yakında olmadığında hızlı bir hatırlatıcıdır.

2.41.4. Bir şey askıda kaldığında

Geri dönmeyen bir betiğin teşhisi, istisna fırlatan bir betikten daha zordur. Yaygın suçlular:

  • Koşulu asla yanlış olmayan bir while döngüsü. Her yinelemede döngü değişkeninin bir print’ini ekleyin; değer değişmiyorsa, döngü gövdesinde bir hata vardır.

  • Asla gelmeyen bir girdiyi bekleyen bloklayan bir çağrı – boş bir kuyruktan okuma, sonu olmayan bir uyku. Betiğin hangi satırda takıldığını görmek için çağrıyı print’lerle çevreleyin.

  • Sonsuz bir özyineleme. Sonunda tetiklendiğinde (RecursionError ile) ortaya çıkan traceback genellikle doğrudan onu işaret eder.

Askıda kalmış bir betik için en etkili kurtarma, betiğe USB üzerinden bir KeyboardInterrupt gönderen IDE’nin stop düğmesidir. Kesme, o anda çalışan satırda bir traceback olarak ortaya çıkar – çoğunlukla tam olarak geri dönmeyen satır.

Not

Bir askıda kalma her teşhise direniyorsa – betik doğru görünüyorsa, kesme traceback’i betiğinize değil bir yerleşik fonksiyona veya aygıt yazılımı (firmware) koduna işaret ediyorsa ya da aynı kod önceki bir aygıt yazılımı (firmware) derlemesinde çalışıyorsa – sebep bir betik hatası değil bir aygıt yazılımı (firmware) hatası olabilir. Betiği hâlâ askıda kalan en küçük yeniden üreticiye indirgeyin ve OpenMV forumunda bir rapor açın. Aygıt yazılımı (firmware) sürümünü, çalıştığı kartı ve indirgenmiş betiği ekleyin.

2.41.5. Teslimattan önce teşhisleri kaldırın

Geliştirme sırasında stratejik print’ler harikadır; bir üretim betiğinde bırakılan yüzlerce print çağrısı çıktıyı dağıtır ve gerçek işin kullanabileceği yığını (heap) tüketir. Bir hata düzeltildiğinde print’leri kaldırın (veya kapatabileceğiniz bir hata ayıklama bayrağının arkasına koyun).

Uzun vadeli olarak kod yolunda kalması gereken teşhisler için, print() yerine logging modülüne geçin. Her mesaja bir seviye iliştirir (debug, info, warning, error) ve tek bir ayarın üretimde sessiz olanları susturmasına izin verir:

import logging

log = logging.getLogger("main")
log.info("starting up")
log.debug("loaded config: %s", config)
log.warning("falling back to defaults")

Logger’ın seviyesini logging.WARNING olarak ayarlamak, satırları yorum satırı yapmadan info ve debug çağrılarının esasen hiçbir maliyeti olmamasını sağlar (mesaj dizgesi hiçbir zaman oluşturulmaz). Bu da logging modülünü kalıcı teşhisler için doğru araç yapar; ham print ise bir kerelik olanlar için uygundur.