diff --git a/.gitignore b/.gitignore index 72364f9..6769e21 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ __pycache__/ # Distribution / packaging .Python -env/ build/ develop-eggs/ dist/ @@ -20,9 +19,12 @@ lib64/ parts/ sdist/ var/ +wheels/ +share/python-wheels/ *.egg-info/ .installed.cfg *.egg +MANIFEST # PyInstaller # Usually these files are written by a python script from a template @@ -37,13 +39,17 @@ pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ +.nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml -*,cover +*.cover +*.py,cover .hypothesis/ +.pytest_cache/ +cover/ # Translations *.mo @@ -52,6 +58,8 @@ coverage.xml # Django stuff: *.log local_settings.py +db.sqlite3 +db.sqlite3-journal # Flask stuff: instance/ @@ -64,26 +72,89 @@ instance/ docs/_build/ # PyBuilder +.pybuilder/ target/ -# IPython Notebook +# Jupyter Notebook .ipynb_checkpoints -# pyenv -.python-version +# IPython +profile_default/ +ipython_config.py -# celery beat schedule file +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff celerybeat-schedule +celerybeat.pid -# dotenv -.env +# SageMath parsed files +*.sage.py -# virtualenv +# Environments +.env +.venv +env/ venv/ ENV/ +env.bak/ +venv.bak/ # Spyder project settings .spyderproject +.spyproject # Rope project settings .ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ \ No newline at end of file diff --git a/pytest_pudb.py b/pytest_pudb.py index 1f7a592..4c936ef 100644 --- a/pytest_pudb.py +++ b/pytest_pudb.py @@ -1,9 +1,13 @@ """ interactive debugging with PuDB, the Python Debugger. """ from __future__ import absolute_import -import pudb + import sys import warnings +import pudb + +ENTER_MESSAGE = "entering PuDB (IO-capturing turned off)" + def pytest_addoption(parser): group = parser.getgroup("general") @@ -51,7 +55,7 @@ def disable_io_capture(self): sys.stdout.write(err) tw = self.pluginmanager.getplugin("terminalreporter")._tw tw.line() - tw.sep(">", "entering PuDB (IO-capturing turned off)") + tw.sep(">", ENTER_MESSAGE) self.pluginmanager.hook.pytest_enter_pdb(config=self.config) def _get_debugger(self, **kwargs): @@ -63,7 +67,6 @@ def pytest_exception_interact(self, node, call, report): Pytest plugin interface for exception handling https://docs.pytest.org/en/latest/reference.html#_pytest.hookspec.pytest_exception_interact """ - self.disable_io_capture() _enter_pudb(node, call.excinfo, report) def pytest_internalerror(self, excrepr, excinfo): diff --git a/test_pytest_pudb.py b/test_pytest_pudb.py index 6f7b77a..341244a 100644 --- a/test_pytest_pudb.py +++ b/test_pytest_pudb.py @@ -1,9 +1,39 @@ +import re +import textwrap + +import pexpect +import pytest + +from pytest_pudb import ENTER_MESSAGE + pytest_plugins = "pytester" HELP_MESSAGE = "\\?\\:help" VARIABLES_TABLE = "V\x1b\\[0;30;47mariables:" +@pytest.fixture(autouse=True) +def pudb_xdg_home(tmp_path_factory): + import os + tmp_path = tmp_path_factory.mktemp("pudb") + os.environ["XDG_CONFIG_HOME"] = str(tmp_path) + + tmp_path = tmp_path / "pudb" + tmp_path.mkdir(parents=True, exist_ok=True) + (tmp_path / "pudb.cfg").write_text( + textwrap.dedent( + """ + [pudb] + prompt_on_quit = False + # pudb/debugger.py:DebuggerUI#event_loop#WELCOME_LEVEL:2453 + seen_welcome = e999 + """ + ) + ) + + yield + + def test_pudb_interaction(testdir): p1 = testdir.makepyfile(""" def test_1(): @@ -57,3 +87,31 @@ def test_1(): child.expect(HELP_MESSAGE) child.expect(VARIABLES_TABLE) child.sendeof() + + +def test_pudb_avoid_double_prologue(testdir): + p1 = testdir.makepyfile( + """ + def test_1(): + test = [] + assert test[0] + """ + ) + + re_escape = re.escape(ENTER_MESSAGE).encode('utf-8') + re_enter = re.compile(rb"\r\n>+ %s >+\r\n" % re_escape) # \r\n instead of ^,$ + + child = testdir.spawn_pytest("--pudb %s" % p1) + child.expect(re_enter) + child.expect("PuDB") + child.expect(HELP_MESSAGE) + # Check that traceback postmortem handled + child.expect("PROCESSING EXCEPTION") + child.expect(VARIABLES_TABLE) + + # Exit pudb + child.write("q") + ret = child.expect([re_enter, pexpect.EOF]) + + if ret == 0: + raise RuntimeError(f"pexpect found {re_enter!r} again!")