ট্রেসব্যাক পড়ার উপায়

ডিবাগ করার জন্য Python ট্রেসব্যাক কীভাবে পড়বেন, তার একটি সংক্ষিপ্ত ধারণা।


ফ্রেম অবজেক্ট

কোনো ফাংশন কল করা হলে, ওই ফাংশনে পাঠানো লোকাল ভ্যারিয়েবল ও আর্গুমেন্ট ধরে রাখার জন্য একটি ফ্রেম অবজেক্ট তৈরি হয়। ফাংশনটি রিটার্ন করলে ফ্রেম অবজেক্টটি ধ্বংস হয়ে যায়। ফাংশন A-এর ভেতরে ফাংশন B কল করা হলে, ফাংশন B-এর মানগুলো একটি ফ্রেম অবজেক্টে রাখা হয়, যা পরে কল স্ট্যাকে ফাংশন A-এর ফ্রেম অবজেক্টের উপরে বসানো হয়।

কল স্ট্যাক

কল স্ট্যাক হলো বর্তমানে সক্রিয় ফাংশনগুলোর ফ্রেম অবজেক্টের সমাহার। ফাংশন A যদি ফাংশন B-কে কল করে এবং ফাংশন B যদি ফাংশন C-কে কল করে, তাহলে তিনটি ফাংশনেরই ফ্রেম অবজেক্ট কল স্ট্যাকে থাকবে। ফাংশন C একবার রিটার্ন করলে তার ফ্রেম অবজেক্টটি স্ট্যাক থেকে পপ হয়ে যাবে এবং কল স্ট্যাকে শুধু ফাংশন A ও B-এর ফ্রেম অবজেক্ট অবশিষ্ট থাকবে।

ট্রেসব্যাক

ট্রেসব্যাক হলো নির্দিষ্ট কোনো এক সময়ে স্ট্যাকে থাকা সব ফ্রেম অবজেক্টের একটি রিপোর্ট। কোনো Python প্রোগ্রাম যখন আনহ্যান্ডেলড এক্সেপশনের মুখোমুখি হয়, তখন সে এক্সেপশনের মেসেজ ও একটি ট্রেসব্যাক প্রিন্ট করে। ট্রেসব্যাক দেখায় কোথায় এক্সেপশনটি রেইজ হয়েছে এবং সেখানে পৌঁছাতে কোন কোন ফাংশন কল করা হয়েছিল।

ট্রেসব্যাক কীভাবে পড়বেন

ValueError একটি কমন এক্সেপশন।

ডান দিকের মাত্র একটি মান থেকে বাম দিকের দুটি ভ্যারিয়েবলে অ্যাসাইন করার চেষ্টা করলে যে ValueError দেখা দেয়, তার একটি উদাহরণ নিচে দেওয়া হলো:

>>> first, second = [1]
Traceback (most recent call last):
File <stdin>, line 1, in <module>
    first, second = [1]
ValueError: not enough values to unpack (expected 2, got 1)

ট্রেসব্যাকে সবচেয়ে সাম্প্রতিক কলটি সবার শেষে থাকে, তাই ট্রেসব্যাক পড়া শুরু করতে হয় একেবারে নিচের এক্সেপশনটি দিয়ে। সেখান থেকে উপরের দিকে পড়তে পড়তে বোঝা যায় কীভাবে ওই স্টেটমেন্ট পর্যন্ত পৌঁছানো হয়েছে। যদি সমস্যা সৃষ্টিকারী লাইনটি একটি ফাংশনের ভেতরে রাখি এবং তারপর ফাংশনটি কল করি, তাহলে আরও লম্বা একটি ট্রেস দেখতে পাই:

>>> def my_func():
...     first, second = [1]
...
>>> my_func()
Traceback (most recent call last):
  File <stdin>, line 5, in <module>
    my_func()
  File <stdin>, line 2, in my_func
    first, second = [1]
ValueError: not enough values to unpack (expected 2, got 1)

নিচ থেকে পিছনের দিকে এগোলে দেখা যায়, যে কলে এক্সেপশনটি ঘটেছে সেটি my_func-এর ২ নম্বর লাইনে। আর সেখানে পৌঁছেছি ৫ নম্বর লাইনে my_func কল করার মাধ্যমে।

কমন এক্সেপশন

Python ৬০টিরও বেশি বিল্ট-ইন এক্সেপশন ক্লাস সংজ্ঞায়িত করে। এখানে আরও কমন কিছু এক্সেপশন এবং সেগুলো কী বোঝায় তার একটি সংক্ষিপ্ত পরিচয় দেওয়া হলো।

SyntaxError

সিনট্যাক্স অবৈধ থাকার কারণে Python যখন কোডটি বুঝতে পারে না, তখন সে একটি SyntaxError রেইজ করে। উদাহরণস্বরূপ, একটি খোলা বন্ধনী থাকতে পারে, যার সঙ্গে মেলানো কোনো বন্ধ বন্ধনী নেই।

কোডের উদাহরণের জন্য এখানে ক্লিক করুন।

এই কোডটি চালালে:

def distance(strand_a, strand_b):
    if len(strand_a) != len(strand_b):
        raise ValueError("Strands must be of equal length."  # This is missing the closing parenthesis

নিচের মতো একটি স্ট্যাক ট্রেস দেখতে পাবেন (শেষ লাইনের মেসেজটি লক্ষ্য করুন):

.usr.local.lib.python3.10.site-packages._pytest.python.py:608: in _importtestmodule
    mod = import_path(self.path, mode=importmode, root=self.config.rootpath)
.usr.local.lib.python3.10.site-packages._pytest.pathlib.py:533: in import_path
    importlib.import_module(module_name)
.usr.local.lib.python3.10.importlib.__init__.py:126: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
<frozen importlib._bootstrap>:1050: in _gcd_import ???
<frozen importlib._bootstrap>:1027: in _find_and_load ???
<frozen importlib._bootstrap>:1006: in _find_and_load_unlocked ???
<frozen importlib._bootstrap>:688: in _load_unlocked ???
.usr.local.lib.python3.10.site-packages._pytest.assertion.rewrite.py:168: in exec_module
    exec(co, module.__dict__)
.mnt.exercism-iteration.hamming_test.py:3: in <module>
    from hamming import (  
E     File ".mnt.exercism-iteration.hamming.py", line 10
E       raise ValueError("Strands must be of equal length."
E                       ^
E   SyntaxError: '(' was never closed

AssertionError

কোনো assert স্টেটমেন্ট (নিচে দেখুন) ব্যর্থ হলে Python একটি AssertionError রেইজ করে।

কোডের উদাহরণের জন্য এখানে ক্লিক করুন।

এই কোডটি চালালে:

def distance(strand_a, strand_b):
    assert len(strand_a) == len(strand_b)


distance("ab", "abc")

নিচের মতো একটি স্ট্যাক ট্রেস দেখতে পাবেন (শেষ লাইনের মেসেজটি লক্ষ্য করুন):

hamming_test.py:3: in <module>
    from hamming import (
hamming.py:5: in <module>
    distance("ab", "abc")
hamming.py:2: in distance
    assert len(strand_a) == len(strand_b)
E   AssertionError

AttributeError

কোড (বা কোনো ইউনিট টেস্ট!) যখন কোনো অবজেক্টের অ্যাট্রিবিউট অ্যাক্সেস করার চেষ্টা করে, কিন্তু ওই অবজেক্টে সেই অ্যাট্রিবিউট না থাকে, তখন একটি AttributeError রেইজ হয়। উদাহরণস্বরূপ, একটি ইউনিট টেস্ট আশা করে একটি Robot অবজেক্টের direction অ্যাট্রিবিউট থাকবে, কিন্তু সে যখন robot.direction অ্যাক্সেস করার চেষ্টা করে, তখন তা খুঁজে পায় না।

এটি বানান বা টাইপিংয়ের ভুলও বোঝাতে পারে, যেমন সঠিক সিনট্যাক্স "Hello".lower() হওয়া সত্ত্বেও "Hello".lowercase() ব্যবহার করা। "Hello".lowercase() রেইজ করে AttributeError: 'str' object has no attribute 'lowercase'।

একটি Robot ক্লাসের উদাহরণের জন্য এখানে ক্লিক করুন।

এই কোডটি চালালে:

class Robot:
    def __init__():
        #note that there is no self.direction listed here
        self.position = (0, 0)
        self.orientation = 'SW'

    def forward():
        pass
        

robby = Robot
robby.direction

নিচের মতো একটি স্ট্যাক ট্রেস দেখতে পাবেন (শেষ লাইনের মেসেজটি লক্ষ্য করুন):

robot_simulator_test.py:3: in <module>
    from robot_simulator import (
robot_simulator.py:12: in <module>
    robby.direction
E   AttributeError: type object 'Robot' has no attribute 'direction'
একটি স্ট্রিংয়ের উদাহরণের জন্য এখানে ক্লিক করুন।

এই কোডটি চালালে:

def distance(strand_a, strand_b):
    if strand_a.lowercase() == strand_b:
        return 0


distance("ab", "abc")

নিচের মতো একটি স্ট্যাক ট্রেস দেখতে পাবেন (শেষ লাইনের মেসেজটি লক্ষ্য করুন):

    def distance(strand_a, strand_b):
>       if strand_a.lowercase() == strand_b:
E       AttributeError: 'str' object has no attribute 'lowercase'

ImportError

কোড যখন কিছু ইমপোর্ট করার চেষ্টা করে, কিন্তু Python তা করতে পারে না, তখন একটি ImportError রেইজ হয়। উদাহরণস্বরূপ, Guidos Gorgeous Lasagna-এর একটি ইউনিট টেস্ট from lasagna import bake_time_remaining করে, কিন্তু lasgana.py সলিউশন ফাইলে bake_time_remaining সংজ্ঞায়িত না-ও থাকতে পারে।

কোডের উদাহরণের জন্য এখানে ক্লিক করুন

সংজ্ঞায়িত ফাংশন ছাড়া lasgana.py ফাইলটি চালালে নিচের এররটি দেখা যাবে:

We received the following error when we ran your code:

  ImportError while importing test module '.mnt.exercism-iteration.lasagna_test.py'.
Hint: make sure your test modules.packages have valid Python names.

Traceback:
.mnt.exercism-iteration.lasagna_test.py:6: in <module>
    from lasagna import (EXPECTED_BAKE_TIME,
E   ImportError: cannot import name 'bake_time_remaining' from 'lasagna' (.mnt.exercism-iteration.lasagna.py)

During handling of the above exception, another exception occurred:
.usr.local.lib.python3.10.importlib.__init__.py:126: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
.mnt.exercism-iteration.lasagna_test.py:23: in <module>
    raise ImportError("In your 'lasagna.py' file, we can not find or import the"

E   ImportError: In your 'lasagna.py' file, we can not find or import the function named 'bake_time_remaining()'. Did you mis-name or forget to define it?

### **IndexError**

Python raises an `IndexError` when an invalid index is used to look up a value in a sequence.
This often indicates the index is not computed properly and is often an off-by-one error.


<details>
<summary>Click here for code example</summary>


Consider the following code.

```python
def distance(strand_a, strand_b):
    same = 0
    for i in range(len(strand_a)):
        if strand_a[i] == strand_b[i]:
            same += 1
    return same


distance("abc", "ab")  # Note the first strand is longer than the second strand.

ওই কোডটি চালালে এরকম একটি এরর দেখা যাবে। (শেষ লাইনটি লক্ষ্য করুন।)

hamming_test.py:3: in <module>
    from hamming import (
hamming.py:9: in <module>
    distance("abc", "ab")  # Note the first strand is longer than the second strand.
hamming.py:4: in distance
    if strand_a[i] == strand_b[i]:
E   IndexError: string index out of range

KeyError

IndexError-এর মতোই, ডিকশনারির কোনো মান খুঁজতে কী (key) ব্যবহার করা হলে কিন্তু ডিকশনারিতে সেই কী (key) সেট করা না থাকলে এই এক্সেপশনটি রেইজ হয়।

কোডের উদাহরণের জন্য এখানে ক্লিক করুন

নিচের কোডটি লক্ষ্য করুন।

def to_rna(dna_letter):
    translation = {"G": "C", "C": "G", "A": "U", "T": "A"}
    return translation[dna_letter]


print(to_rna("Q"))  # Note, "Q" is not in the translation.

ওই কোডটি চালালে এরকম একটি এরর দেখা যাবে। (শেষ লাইনটি লক্ষ্য করুন।)

rna_transcription_test.py:3: in <module>
    from rna_transcription import to_rna
rna_transcription.py:6: in <module>
    print(to_rna("Q"))
rna_transcription.py:3: in to_rna
    return translation[dna_letter]
E   KeyError: 'Q'

TypeError

সাধারণত কোনো ফাংশনে ভুল ধরনের ডেটা পাঠানো হলে বা কোনো অপারেশনে ভুল ধরনের ডেটা ব্যবহার করা হলে একটি TypeError রেইজ হয়।

কোডের উদাহরণের জন্য এখানে ক্লিক করুন

নিচের কোডটি লক্ষ্য করুন।

def hello(name):  # This function expects a string.
    return 'Hello, ' + name + '!'


print(hello(100))  # 100 is not a string.

ওই কোডটি চালালে এরকম একটি এরর দেখা যাবে। (শেষ লাইনটি লক্ষ্য করুন।)

hello_world_test.py:3: in <module>
    import hello_world
hello_world.py:5: in <module>
    print(hello(100))
hello_world.py:2: in hello
    return 'Hello, ' + name + '!'
E   TypeError: can only concatenate str (not "int") to str

ValueError

কোনো ফাংশনে অবৈধ মান পাঠানো হলে সাধারণত একটি ValueError রেইজ হয়।

কোডের উদাহরণের জন্য এখানে ক্লিক করুন

লক্ষ্য করুন, বাস্তব বর্গমূল কেবল ধনাত্মক সংখ্যার জন্যই বিদ্যমান। -1 যেহেতু বর্গমূলের জন্য বৈধ মান নয়, তাই math.sqrt(-1) কল করলে ValueError: math domain error রেইজ হয়। (গাণিতিক) পরিভাষায় বললে, -1 বর্গমূলের ডোমেইনে পড়ে না।

import math

math.sqrt(-1)

ওই কোডটি চালালে এরকম একটি এরর দেখা যাবে। (শেষ লাইনটি লক্ষ্য করুন।)

square_root_test.py:3: in <module>
    from square_root import (
square_root.py:3: in <module>
    math.sqrt(-1)
E   ValueError: math domain error

print ফাংশন ব্যবহার করা

কখনও কখনও কোনো এরর রেইজ হয় না, কিন্তু মানটি প্রত্যাশিত মান নয়। মানটি যদি ধারাবাহিক কয়েকটি হিসাবের ফল হয়, তাহলে এটি আরও বিভ্রান্তিকর হতে পারে। এমন পরিস্থিতিতে প্রতিটি ধাপের মান দেখে বোঝা কাজে লাগে, কোন ধাপটি প্রত্যাশামতো আচরণ করছে না। কনসোলে মান প্রিন্ট করতে print ফাংশনটি ব্যবহার করা যায়। নিচে এমন একটি ফাংশনের উদাহরণ দেওয়া হলো, যা প্রত্যাশিত মান রিটার্ন করে না:

# the intent is to pass an integer to this function and get an integer back
def halve_and_quadruple(num):
    return (num / 2) * 4

ফাংশনটিতে 5 পাঠালে প্রত্যাশিত মান 8, কিন্তু এটি রিটার্ন করে 10.0। সমস্যা খুঁজতে হিসাবটিকে ভাগ করে ফেলা হয়, যাতে প্রতিটি ধাপে মানটি পরীক্ষা করা যায়।

# the intent is to pass an integer to this function and get an integer back
def halve_and_quadruple(num):
    # verify the number in is what is expected
    # prints 5
    print(num)
    # we want the int divided by an integer to be an integer
    # but this prints 2.5! We've found our mistake.
    print(num / 2)
    # this makes sense, since 2.5 x 4 = 10.0
    print((num / 2) * 4)
    return (num / 2) * 4

What the `print` calls revealed is that we used `/` when we should have used `//`, the [floor division operator][floor division operator].

## Logging

[Logging][logging] can be used similarly to `print`, but it is more powerful.
What is logged can be configured by the logging severity (e.g., 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'.)
A call to the `logging.error` function can pass `True` to the `exc_info` parameter, which will additionally log the stack trace.
By configuring multiple handlers, logging can write to more than one place with the same logging function.

Following is an example of logging printed to the console:

```python

>>> import logging
>>>
>>> # configures minimum logging level as INFO
>>> logging.basicConfig(level=logging.INFO)
>>>
>>> def halve_and_quadruple(num):
...     # prints INFO:root: num == 5
...     logging.info(f" num == {num}")
...     return (num // 2) * 4
...
>>> print(halve_and_quadruple(5))

ডিফল্ট লেভেল WARNING হওয়ায় লেভেলটি INFO হিসেবে কনফিগার করা হয়েছে। স্থায়ী লগের জন্য লগারকে এভাবে একটি ফাইলে লেখার জন্য কনফিগার করা যায়:

>>> import logging
...
>>> # configures the output file name to example.log, and the minimum logging level as INFO
>>> logging.basicConfig(filename='example.log', level=logging.INFO)
...
... def halve_and_quadruple(num):
...     # prints INFO:root: num == 5 to the example.log file
...     logging.info(f" num == {num}")
...     return (num // 2) * 4
...
>>> print(halve_and_quadruple(5))

assert

assert হলো এমন একটি স্টেটমেন্ট, যা প্রোগ্রামে বাগ না থাকলে সবসময় True হিসেবে গণ্য হওয়া উচিত। কোনো assert যখন False হিসেবে গণ্য হয়, তখন এটি একটি AssertionError রেইজ করে। AssertionError-এর ট্রেসব্যাকে assert স্টেটমেন্টের অংশ হিসেবে একটি ঐচ্ছিক মেসেজ থাকতে পারে। মেসেজ ঐচ্ছিক হলেও, assert-এর সংজ্ঞায় সবসময় একটি রাখা ভালো অভ্যাস।

নিচে assert ব্যবহারের একটি উদাহরণ দেওয়া হলো:

>>> def int_division(dividend, divisor):
...     assert divisor != 0, "divisor must not be 0"
...     return dividend // divisor
...
>>> print(int_division(2, 1))
2
>>> print(int_division(2, 0))
Traceback (most recent call last):
  File <stdin>, line 7, in <module>
    print(int_division(2, 0))
          ^^^^^^^^^^^^^^^^^^
  File <stdin>, line 2, in int_division
    assert divisor != 0, "divisor must not be 0"
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: divisor must not be 0

ট্রেসব্যাক পড়া যদি নিচ থেকে শুরু করি (যা আমাদের করা উচিত), তাহলে দ্রুত বোঝা যায় সমস্যাটি হলো divisor হিসেবে 0 পাঠানো উচিত নয়।

কোনো মান প্রত্যাশিত টাইপের কি না তা পরীক্ষা করতেও assert ব্যবহার করা যায়:

>>> import numbers
...
...
... def int_division(dividend, divisor):
...     assert divisor != 0, "divisor must not be 0"
...     assert isinstance(divisor, numbers.Number), "divisor must be a number"
...     return dividend // divisor
...
>>> print(int_division(2, 1))
2
>>> print(int_division(2, '0'))
Traceback (most recent call last):
  File <stdin>, line 11, in <module>
    print(int_division(2, '0'))
          ^^^^^^^^^^^^^^^^^^^^
  File <stdin>, line 6, in int_division
    assert isinstance(divisor, numbers.Number), "divisor must be a number"
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: divisor must be a number

একবার বাগ চিহ্নিত হয়ে গেলে, assert-এর বদলে এরর হ্যান্ডলিং ব্যবহার করার কথা ভাবুন। কারণ -O বা -OO অপশন দিয়ে Python চালালে, কিংবা PYTHONOPTIMIZE এনভায়রনমেন্ট ভ্যারিয়েবলটি 1 বা 2-তে সেট করলে সব assert স্টেটমেন্ট নিষ্ক্রিয় করা যায়। PYTHONOPTIMIZE-কে 1-এ সেট করা মানে -O অপশন দিয়ে Python চালানোর সমান, যা অ্যাসারশনগুলো নিষ্ক্রিয় করে। PYTHONOPTIMIZE-কে 2-এ সেট করা মানে -OO অপশন দিয়ে Python চালানোর সমান, যা অ্যাসারশন নিষ্ক্রিয় করার পাশাপাশি বাইটকোড থেকে ডকস্ট্রিং সরিয়ে দেয়। বাইটকোড কমানো কোড দ্রুত চালানোর একটি উপায়।

Python ডিবাগার

Python-এ একটি বিল্ট-ইন ডিবাগার আছে, pdb। এটি দিয়ে কোড ধাপে ধাপে এগিয়ে যাওয়া এবং ভ্যারিয়েবল পরীক্ষা করা যায়। এটি দিয়ে ব্রেকপয়েন্টও সেট করা যায়। শুরু করতে প্রথমে import pdb করতে হবে, তারপর যেখানে ডিবাগ করা শুরু করতে চান সেখানে pdb.set_trace() কল করতে হবে:

import pdb

def add(num1, num2):
    return num1 + num2

pdb.set_trace()
sum = add(1,5)
print(sum)

এই কোডটি চালালে একটি pdb প্রম্পট পাবেন, যেখানে কমান্ড টাইপ করতে পারবেন। কমান্ডের তালিকা পেতে help লিখুন। সবচেয়ে কমন কমান্ডটি হলো step, যা ওই লাইনে কল করা ফাংশনের ভেতরে ঢুকে ধাপে ধাপে এগোয়। next ফাংশন কলের উপর দিয়ে পেরিয়ে পরের লাইনে চলে যায়। where আপনাকে বলে দেয় আপনি কোন লাইনে আছেন। আরও কিছু কাজের কমান্ড হলো whatis <variable>, যা একটি ভ্যারিয়েবলের টাইপ জানায়, এবং print(<variable>), যা একটি ভ্যারিয়েবলের মান প্রিন্ট করে। শুধু <variable> লিখেও একটি ভ্যারিয়েবলের মান প্রিন্ট করতে পারেন। আরেকটি কমান্ড হলো jump <line number>, যা নির্দিষ্ট লাইন নম্বরে চলে যায়।

আগের কোডের ভিত্তিতে ডিবাগার ব্যবহারের একটি ছোট উদাহরণ নিচে দেওয়া হলো। লক্ষ্য করুন, এই এবং পরের উদাহরণগুলোতে MacOS বা Linux প্ল্যাটফর্মে ফাইল পাথ ফরওয়ার্ড স্ল্যাশ দিয়ে লেখা হবে:

>>> python pdb.py
... > c:\pdb.py(7)<module>()
... -> sum = add(1,5)
... (Pdb)
>>> step
... > c:\pdb.py(3)add()
... -> def add(num1, num2):
... (Pdb)
>>> whatis num1
... <class 'int'>
>>> print(num2)
... 5
>>> next
... > c:\pdb.py(4)add()
... -> return num1 + num2
... (Pdb)
>>> jump 3
... > c:\pdb.py(3)add()
... -> def add(num1, num2):
... (Pdb)

ব্রেকপয়েন্ট সেট করা হয় break <filename>:<line number> <condition> দিয়ে, যেখানে শর্তটি ঐচ্ছিক এবং ব্রেকপয়েন্টে থামার জন্য তা সত্য হতে হবে। শুধু break লিখলেই আপনার সেট করা ব্রেকপয়েন্টের তালিকা পাবেন। কোনো ব্রেকপয়েন্ট নিষ্ক্রিয় করতে disable <breakpoint number> লিখতে পারেন। কোনো ব্রেকপয়েন্ট সক্রিয় করতে enable <breakpoint number> লিখতে পারেন। কোনো ব্রেকপয়েন্ট মুছে ফেলতে clear <breakpoint number> লিখতে পারেন। চালনা চালু রাখতে continue বা c লিখতে পারেন। ডিবাগার থেকে বেরোতে quit বা q লিখতে পারেন।

আগের কোডের ভিত্তিতে উপরের ডিবাগার কমান্ডগুলো ব্যবহারের একটি উদাহরণ নিচে দেওয়া হলো:

>>> python pdb.py
... > c:\pdb.py(7)<module>()
... -> sum = add(1,5)
... (Pdb)
>>> break
...
>>> break pdb:4
... Breakpoint 1 at c:\pdb.py:4
>>> break
... Num Type         Disp Enb   Where
... 2   breakpoint   keep yes   at c:\pdb.py:4
>>> c # continue
... > c:\pdn.py(4)add()
... -> return num1 + num2
>>> disable break 1
... Disabled breakpoint 1 at c:\pdb.py:4
>>> break
... Num Type         Disp Enb   Where
... 1   breakpoint   keep no    at c:\pdb.py:4
...         breakpoint already hit 1 time
>>> clear break 1
... Deleted breakpoint 1 at c:\pdb.py:4
>>> break
...

Python 3.7+ এ ব্রেকপয়েন্ট তৈরি করার আরও সহজ উপায় আছে। যেখানে দরকার সেখানে শুধু breakpoint() লিখলেই একটি তৈরি হয়ে যাবে।

def add(num1, num2):
    breakpoint()
    return num1 + num2

breakpoint()
sum = add(1,5)
print(sum)
>>> python pdb.py
... > c:\pdb.py(7)<module>()
... -> sum = add(1,5)
... (Pdb)
>>> c # continue
... > c:\pdb.py(5)add()
... -> return num1 + num2