Как игнорировать определенную точку останова в интерактивном режиме?

Рассмотрим этот сценарий:

print("before loop")
for i in range(100):
    breakpoint()
print("after loop")
breakpoint()
print("exit")

Если не нажать «c» сто раз, как можно пройти точку останова в цикле на уровне L3 и перейти к L5?

Я попробовал команду игнорировать, но не смог:

$ python3 example.py
before loop
> /tmp/example.py(2)<module>()
-> for i in range(100):
(Pdb) ignore 0
*** Breakpoint 0 already deleted
(Pdb) c
> /tmp/example.py(2)<module>()
-> for i in range(100):
(Pdb) ignore 0
*** Breakpoint 0 already deleted
(Pdb) c
> /tmp/example.py(2)<module>()
-> for i in range(100):

Я хочу выполнить оставшуюся часть цикла, не отключая снова точку останова на L3, затем напечатать «после цикла» и прерваться перед печатью «выхода», оставаясь в отладчике. Ответ не должен требовать выхода из отладчика и повторного входа в среду выполнения или изменения исходного кода.

Может быть, это тривиально, но я обычно добавляю дополнительный оператор if, чтобы поставить точки останова, когда они мне нужны условно (т. е. if i == 0: breakpoint())

Sayse 09.07.2024 18:05

@Sayse Ответ не должен требовать... изменения исходного кода.

Woodford 09.07.2024 18:08
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
4
2
76
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

breakpoint — это обычная функция Python, поэтому в качестве некрасивого, но функционального решения вы можете временно перезаписать breakpoint новой функцией, которая выборочно пропускает точки останова.

Предположим, foo.py содержит следующее:

for i in range(100):
    breakpoint()

breakpoint()
print("hi")

Запустите foo.py, и он сразу же сломается на первом breakpoint, где continuing неоднократно продолжает цикл:

> /code/foo.py(1)<module>()
-> for i in range(100):
(Pdb) c
> /code/foo.py(1)<module>()
-> for i in range(100):
(Pdb) c
> /code/foo.py(1)<module>()
-> for i in range(100):

Поместите следующее во временный модуль (например, tmp_break_3zfh.py):

import inspect
import builtins

def breakpoint():
    caller = inspect.stack()[1]
    if caller.filename.endswith("/foo.py") and caller.lineno == 2:
        return
    builtins.breakpoint()

Затем импортируйте ее из консоли pdb, чтобы перезаписать реальную функцию точки останова только для этого модуля. Когда мы continue, затем step (чтобы выйти из оболочки breakpoint и войти в вызывающую программу), мы видим, что мы остановлены после цикла:

(Pdb) from tmp_break_3zfh import breakpoint
(Pdb) c
--Return--
> /code/tmp_break_3zfh.py(8)breakpoint()->None
-> builtins.breakpoint()
(Pdb) s
--Return--
> /code/foo.py(5)<module>()
-> print("hi")
Ответ принят как подходящий

Вы можете использовать переменную среды PYTHONBREAKPOINT для вызова пользовательского обработчика точки останова, который вы определяете в отдельном файле.

Например:

$ export PYTHONBREAKPOINT=mybreak.mybreak
# mybreak/__init__.py

import pdb

_counter = 0

def mybreak(*args, **kwargs):
    global _counter
    if _counter >= 100:
        # default behavior
        pdb.set_trace(*args, **kwargs)
    else:
        # skip dropping into pdb while inside the range(100) loop
        pass
    _counter += 1

Возможно, вам придется немного повозиться, если в файле есть другие вызовы breakpoint(), но это будет просто вопрос отслеживания того, каким будет значение счетчика, когда вы захотите пропустить точку останова.

Альтернативная реализация пользовательского обработчика, как предложено в ответе nneonneo:

# mybreak/__init__.py

import inspect
import pdb

def mybreak():
    caller = inspect.stack()[1]
    if caller.filename.endswith("/foo.py") and caller.lineno == 2:
        # skip this breakpoint
        return
    pdb.set_trace(*args, **kwargs)

Интересный. Я бы подумал, что PYTHONBREAKPOINT нужно будет установить вне среды выполнения, чтобы иметь какой-либо эффект, но интерактивное использование os.environ["PYTHONBREAKPOINT"] = "__main__.mybreak" действительно тоже работает.

wim 09.07.2024 19:21

Я бы подумал то же самое. Это полезно знать!

Woodford 09.07.2024 19:38

Чтобы уточнить ответ nneonneo и четко удовлетворить требование «без перезапуска интерпретатора или изменения исходного кода», вам не обязательно предоставлять хаки внутри точки останова.

Вы можете временно отключить встроенную функцию breakpoint и восстановить ее позже. Чтобы восстановить, просто добавьте точку останова pdb сразу после цикла и переназначьте ее обратно.

Вместо хранения исходного breakpoint в переменной вы можете использовать __import__('builtins').breakpoint - это также будет относиться к исходной функции (и лучше, когда ваш цикл фактически не находится в теле скрипта, см. ниже).

# python /tmp/q.py
before loop
> /tmp/q.py(2)<module>()
-> for i in range(100):
(Pdb) _my_old_breakpoint = breakpoint
(Pdb) breakpoint = lambda: None
(Pdb) break 4
Breakpoint 1 at /tmp/q.py:4
(Pdb) c
> /tmp/q.py(4)<module>()
-> print("after loop")
(Pdb) breakpoint = _my_old_breakpoint
(Pdb) c
after loop
> /tmp/q.py(6)<module>()
-> print("exit")
(Pdb) c
exit

Насколько я знаю, нет способа включить/отключить вызовы breakpoint через pdb, он управляет только «пользовательскими» точками останова, установленными в сеансе pdb.

Если этот цикл не находится в глобальной области видимости, вместо этого вам нужно будет изменить builtins:

cat > /tmp/q.py <<EOF
def main():
    print("before loop")
    for i in range(100):
        breakpoint()
    print("after loop")
    breakpoint()
    print("exit")

main()
EOF

# python /tmp/q.py
before loop
> /tmp/q.py(3)main()
-> for i in range(100):
(Pdb) import builtins
(Pdb) builtins._my_old_breakpoint=breakpoint
(Pdb) builtins.breakpoint = lambda: None
(Pdb) break 5
Breakpoint 1 at /tmp/q.py:5
(Pdb) c
> /tmp/q.py(5)main()
-> print("after loop")
(Pdb) builtins.breakpoint = builtins._my_old_breakpoint
(Pdb) c
after loop
> /tmp/q.py(7)main()
-> print("exit")
(Pdb) c
exit

Другие вопросы по теме