2025, Oct 20 04:31

pdb में where के बजाय traceback से आसपास के N फ्रेम देखें

गहरी recursion में pdb का where शोर बढ़ाता है। जानें कैसे traceback.print_stack और format_stack से मौजूदा फ्रेम के आसपास सीमित स्टैक विंडो दिखाएँ, डिबगिंग तेज करें.

Python के pdb के साथ गहरी recursion को डिबग करना अक्सर एक सरल सवाल को शोर की दीवार में बदल देता है। where कमांड पूरा कॉल स्टैक उंडेल देता है—पूर्णता के लिए अच्छा, लेकिन फोकस के लिए नहीं। जब असली समस्या मौजूदा फ्रेम के आसपास ही होती है, तो आपको उसके आसपास का छोटा-सा विंडो चाहिए होता है, न कि ऊपर‑नीचे सैकड़ों फ्रेम। pdb में where के आउटपुट को आसपास के N फ्रेम तक सीमित करने का कोई बिल्ट‑इन तरीका नहीं है, लेकिन डिबगर के भीतर traceback मॉड्यूल से बिल्कुल वही हासिल किया जा सकता है।

समस्या को दोहराने वाला न्यूनतम उदाहरण

नीचे दिया स्निपेट एक छोटा कॉल‑चेन बनाता है जो एक recursive फ़ंक्शन पर खत्म होता है। recursion शून्य पर पहुँचते ही नियंत्रण डिबगर में चला जाता है। वहाँ से where पूरा स्टैक दिखाएगा।

import pdb
def f1():
    f2()
def f2():
    f3()
def f3():
    f4()
def f4():
    go_deep(5)
def go_deep(k):
    if k == 0:
        breakpoint()
    else:
        go_deep(k - 1)
f1()

(Pdb) प्रॉम्प्ट पर where पूरा स्टैक दिखा देगा, और breakpoint() इस्तेमाल करने पर pdb के आंतरिक फ्रेम भी शामिल होंगे।

असल में हो क्या रहा है

pdb फिलहाल ऐसा फ़ंक्शन नहीं देता जो मौजूदा फ्रेम के आस‑पास की स्टैक का सिर्फ़ सीमित हिस्सा दिखाए। इसी वजह से अधिकतम recursion के हालात में आउटपुट बेहिसाब बढ़ जाता है। कारगर उपाय है कि (Pdb) प्रॉम्प्ट से ही स्टैंडर्ड लाइब्रेरी के traceback टूल्स का इस्तेमाल करें। pdb के भीतर आप traceback इम्पोर्ट कर सकते हैं और फ्रेम्स के किसी subset को प्रिंट या फ़ॉर्मैट करने के लिए उपयोग कर सकते हैं। आप एक positive limit दे सकते हैं जो मौजूदा स्थिति के सापेक्ष उतने फ्रेम दिखाए, या negative limit जिससे स्टैक के ऊपर या नीचे से गिनती हो। breakpoint() होने पर लिस्टिंग में pdb के आंतरिक फ्रेम “घुस” जाते हैं; व्यवहार में इसका मतलब है कि अपने अपेक्षित नंबर में करीब दस का इज़ाफा कर लें ताकि उसकी भरपाई हो सके।

समाधान: pdb के भीतर से traceback का उपयोग करें

सबसे पहले, डिबगर सेशन के भीतर मॉड्यूल इम्पोर्ट करें।

(Pdb) import traceback

सीमा के साथ स्टैक प्रिंट करने के लिए traceback.print_stack का उपयोग करें। Positive limit देने पर उतने फ्रेम प्रिंट होंगे, और negative limit देने पर गिनती स्टैक के शीर्ष या तल से होगी। फ्रेम नहीं देने पर वर्तमान फ्रेम माना जाता है।

(Pdb) traceback.print_stack()
(Pdb) traceback.print_stack(None, 15)
(Pdb) traceback.print_stack(None, -15)

यदि स्क्रिप्ट को python -m pdb script.py से चलाया गया है, तो आप वर्तमान फ्रेम को स्पष्ट रूप से लक्षित कर सकते हैं।

(Pdb) traceback.print_stack(self.curframe, -10)

और सूक्ष्म नियंत्रण के लिए, तथा डिबगर के आंतरिक फ्रेम प्रिंट होने से बचने के लिए, स्टैक को सूची के रूप में फ़ॉर्मैट करें और जरूरत के मुताबिक़ स्लाइस लें। फिर केवल उसी हिस्से को प्रिंट करें ताकि आउटपुट साफ़ रहे।

(Pdb) {print(chunk, end='') for chunk in traceback.format_stack()[-30:-10]}

इससे breakpoint() द्वारा जोड़े गए अतिरिक्त आंतरिक कॉल बिना आए, फ्रेम्स की एक विंडो छपती है। स्लाइसिंग आपको मौजूदा स्थिति के आस‑पास के “N स्तरों” पर ध्यान केंद्रित करने देती है।

यह क्यों उपयोगी है

जब आप अधिकतम recursion की दिक्कत ढूँढ रहे होते हैं, तो काम की जानकारी सैकड़ों फ्रेम्स के नीचे दब जाती है। दृश्य को एक छोटे लक्षित विंडो तक सीमित करने से फीडबैक चक्र छोटा हो जाता है और बेमानी शोर में स्क्रॉल करने से बचते हैं। यह तरीका pdb के भीतर से ही सहज रूप से काम करता है, इसलिए आपको उस इंटरैक्टिव संदर्भ से बाहर नहीं जाना पड़ता जहाँ स्टेट पहले से मौजूद है।

निष्कर्ष

यदि where भारी पड़ रहा हो, तो (Pdb) प्रॉम्प्ट से सीधे traceback मॉड्यूल पर स्विच करें। त्वरित झलक के लिए positive या negative limits के साथ traceback.print_stack का उपयोग करें, या फिर सटीक नियंत्रण चाहिए हो तो slicing के साथ traceback.format_stack अपनाएँ। ध्यान रहे कि breakpoint() आंतरिक फ्रेम जोड़ता है; तदनुसार अपनी limits समायोजित करें। अगर आपने python -m pdb script.py से डिबगिंग शुरू की है, तो वर्तमान फ्रेम पर आउटपुट स्थिर करने के लिए traceback.print_stack में self.curframe पास करें। इस तरीके से आपका स्टैक‑व्यू उन्हीं फ्रेम्स पर केंद्रित रहता है जो मायने रखते हैं और recursion डिबगिंग काबू में आती है।

यह लेख StackOverflow के प्रश्न पर आधारित है, जिसे quazgar ने पूछा था, और bruno के उत्तर से लिया गया है।