Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions HISTORY.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ Changelog
0.13.14 (unreleased)
--------------------

- Add ipython_profile support. We can now run ipdb with a non-default
ipython-profile by setting an environment-variable ``IPDB_IPYTHON_PROFILE``
or by setting ``ipython_profile`` in the config. [WouterVH]

- Run ``black`` on ipdb-codebase with line-length 88. [WouterVH]


Expand Down
21 changes: 21 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,27 @@ Or you can use ``iex`` as a function decorator to launch ipdb if an exception is
Using ``from future import print_function`` for Python 3 compat implies dropping Python 2.5 support.
Use ``ipdb<=0.8`` with 2.5.


Using a non-default ipython-profile
-----------------------------------
By default ``ipdb`` will instantiate an ipython-session loaded with the default profile called ``default``.
You can set a non-default profile by setting the environment variable ``IPDB_IPYTON_PROFILE``:

.. code-block:: bash

export IPDB_IPYTON_PROFILE="ipdb"

Or by setting in ``pyproject.toml``:

.. code-block:: toml

[tool.ipdb]
ipython_profile = "ipdb"

This should correspond with a profile-directory ``profile_ipdb```in your ``IPYTHON_HOME``.
If this profile-directory does not exist, we fall back to the default profile.


Issues with ``stdout``
----------------------

Expand Down
38 changes: 33 additions & 5 deletions ipdb/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
__version__ = "0.13.14.dev0"

from IPython import get_ipython
from IPython.core.application import ProfileDir
from IPython.core.debugger import BdbQuit_excepthook
from IPython.core.profiledir import ProfileDirError
from IPython.paths import get_ipython_dir
from IPython.terminal.ipapp import TerminalIPythonApp
from IPython.terminal.embed import InteractiveShellEmbed

Expand All @@ -23,20 +26,30 @@
import ConfigParser as configparser


def _get_debugger_cls():
def _get_debugger_cls(ipython_profile="default"):
shell = get_ipython()
if shell is None:
# Not inside IPython
# Build a terminal app in order to force ipython to load the
# configuration
ipapp = TerminalIPythonApp()
ipython_dir = get_ipython_dir()
try:
profile_dir = ProfileDir.find_profile_dir_by_name(
ipython_dir=ipython_dir,
name=ipython_profile,
)
except ProfileDirError: # fallback to default-profile
profile_dir = ProfileDir.find_profile_dir_by_name(
ipython_dir=ipython_dir,
)
ipapp = TerminalIPythonApp(profile_dir=profile_dir)

# Avoid output (banner, prints)
ipapp.interact = False
ipapp.initialize(["--no-term-title"])
shell = ipapp.shell
else:
# Running inside IPython

# Detect if embed shell or not and display a message
if isinstance(shell, InteractiveShellEmbed):
sys.stderr.write(
Expand All @@ -49,10 +62,17 @@ def _get_debugger_cls():
return shell.debugger_cls


def _init_pdb(context=None, commands=[]):
def _init_pdb(context=None, ipython_profile=None, commands=[]):
if context is None:
context = os.getenv("IPDB_CONTEXT_SIZE", get_context_from_config())
debugger_cls = _get_debugger_cls()

if ipython_profile is None:
ipython_profile = os.getenv(
"IPDB_IPYTHON_PROFILE", get_ipython_profile_from_config()
)

debugger_cls = _get_debugger_cls(ipython_profile=ipython_profile)

try:
p = debugger_cls(context=context)
except TypeError:
Expand Down Expand Up @@ -94,6 +114,14 @@ def get_context_from_config():
)


def get_ipython_profile_from_config():
parser = get_config()
try:
return parser.get("ipdb", "ipython_profile")
except (configparser.NoSectionError, configparser.NoOptionError):
return "default"


class ConfigFile(object):
"""
Filehandle wrapper that adds a "[ipdb]" section to the start of a config
Expand Down
113 changes: 113 additions & 0 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from ipdb.__main__ import (
get_config,
get_context_from_config,
get_ipython_profile_from_config,
)


Expand Down Expand Up @@ -137,6 +138,22 @@ def test_noenv_nodef_nosetup_pyproject(self):
self.assertEqual(self.pyproject_context, cfg.getint("ipdb", "context"))
self.assertRaises(configparser.NoOptionError, cfg.get, "ipdb", "version")

def test_noenv_nodef_nosetup_pyproject(self):
"""
Setup: $IPDB_CONFIG unset, $HOME/.ipdb does not exist,
setup.cfg does not exist, pyproject.toml exists
Result: load pyproject.toml
"""
os.unlink(self.env_filename)
os.unlink(self.default_filename)
os.remove(self.setup_filename)
with ModifiedEnvironment(IPDB_CONFIG=None, HOME=self.tmpd):
cfg = get_config()
# breakpoint()
self.assertEqual(["ipdb"], cfg.sections())
self.assertEqual(self.pyproject_context, cfg.getint("ipdb", "context"))
self.assertRaises(configparser.NoOptionError, cfg.get, "ipdb", "version")

def test_env_nodef_setup_pyproject(self):
"""
Setup: $IPDB_CONFIG is set, $HOME/.ipdb does not exist,
Expand Down Expand Up @@ -382,3 +399,99 @@ def test_noenv_nodef_invalid_setup(self):
pass
else:
self.fail("Expected TomlDecodeError from invalid config file")


class get_ipython_profile_from_config_TestCase(unittest.TestCase):
"""
Test cases for function `get_ipython_profile_from_config`.
"""

def setUp(self):
"""
Set fixtures for this test case.
"""
set_config_files_fixture(self)

def test_missing_key_setup(self):
"""
Setup: $IPDB_CONFIG unset, $HOME/.ipdb does not exist,
setup.cfg does not exist, pyproject.toml content is invalid.
Result: Propagate exception from `get_config`.
"""
os.unlink(self.env_filename)
os.unlink(self.default_filename)
os.unlink(self.setup_filename)
write_lines_to_file(
self.pyproject_filename,
["[tool.ipdb]"],
)

try:
from tomllib import TOMLDecodeError
except ImportError:
try:
from tomli import TOMLDecodeError
except ImportError:
from toml.decoder import TomlDecodeError as TOMLDecodeError

with ModifiedEnvironment(IPDB_CONFIG=None, HOME=self.tmpd):
profile_name = get_ipython_profile_from_config()
assert profile_name == "default"

def test_default_profile_setup(self):
"""
Setup: $IPDB_CONFIG unset, $HOME/.ipdb does not exist,
setup.cfg does not exist, pyproject.toml content is invalid.
Result: Propagate exception from `get_config`.
"""
os.unlink(self.env_filename)
os.unlink(self.default_filename)
os.unlink(self.setup_filename)
write_lines_to_file(
self.pyproject_filename,
[
"[tool.ipdb]",
"ipython_profile = 'default'",
],
)

try:
from tomllib import TOMLDecodeError
except ImportError:
try:
from tomli import TOMLDecodeError
except ImportError:
from toml.decoder import TomlDecodeError as TOMLDecodeError

with ModifiedEnvironment(IPDB_CONFIG=None, HOME=self.tmpd):
profile_name = get_ipython_profile_from_config()
assert profile_name == "default"

def test_non_existing_profile_setup(self):
"""
Setup: $IPDB_CONFIG unset, $HOME/.ipdb does not exist,
setup.cfg does not exist, pyproject.toml content is invalid.
Result: Propagate exception from `get_config`.
"""
os.unlink(self.env_filename)
os.unlink(self.default_filename)
os.unlink(self.setup_filename)
write_lines_to_file(
self.pyproject_filename,
[
"[tool.ipdb]",
"ipython_profile = 'foo'",
],
)

try:
from tomllib import TOMLDecodeError
except ImportError:
try:
from tomli import TOMLDecodeError
except ImportError:
from toml.decoder import TomlDecodeError as TOMLDecodeError

with ModifiedEnvironment(IPDB_CONFIG=None, HOME=self.tmpd):
profile_name = get_ipython_profile_from_config()
assert profile_name == "foo"