Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add ipython-profile support #266

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
86 changes: 86 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,72 @@ 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_profile_setup(self):
"""
Setup: pyproject.toml has no [tool.ipdb]-section.
"""
os.unlink(self.env_filename)
os.unlink(self.default_filename)
os.unlink(self.setup_filename)
write_lines_to_file(
self.pyproject_filename,
""
)

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: pyproject.toml has a [tool.ipdb]-section
with the ipython_profile explicitly set to 'default'.

"""
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'",
],
)

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

def test_non_default_profile_setup(self):
"""
Setup: pyproject.toml has a [tool.ipdb]-section
with the ipython_profile explicitly set to a non-default.
"""
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'",
],
)

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