gotcha / ipdb

Integration of IPython pdb
BSD 3-Clause "New" or "Revised" License
1.85k stars 146 forks source link

How do we customize the color scheme for ipdb? #185

Open AdrienLemaire opened 4 years ago

AdrienLemaire commented 4 years ago

Related: #50, #144, #57, #1

By searching around, I understand (probably incorrectly) that ipdb should use ipython's color scheme by default. And that ipython use pygments styles.

So I tried the following:

Within the same virtualenv, I get colors within ipython, but not within ipdb:

image

image

The prompt seems to be another matter, and I haven't figured out how to change its color yet.

Therefore, I have 2 related questions:

Thanks for your help in advance!

gotcha commented 4 years ago

I have never tried to customize IPython or ipdb colors myself.

I am sorry I cannot help and hope you'll report here if you succeeds.

AdrienLemaire commented 4 years ago

Investigating the Prompt color issue

I can modify the prompt text in IPython/core/debugger.py

prompt = 'ipdb> '

I can modify the prompt color in IPython/terminal/interactiveshell.py

class TerminalInteractiveShell(InteractiveShell):
    def _make_style_from_name_or_cls(self, name_or_cls):
        if name_or_cls == 'legacy':
        else :
            if isinstance(name_or_cls, str):
                style_cls = get_style_by_name(name_or_cls)
            else:
                style_cls = name_or_cls
            style_overrides = {
                Token.Prompt: '#009900',  # <-- OUR GREEN
                Token.PromptNum: '#ansibrightgreen bold',
                Token.OutPrompt: '#990000',
                Token.OutPromptNum: '#ansibrightred bold',
            }

        style_overrides.update(self.highlighting_style_overrides)
        style = merge_styles([
            style_from_pygments_cls(style_cls),
            style_from_pygments_dict(style_overrides),
        ])

Looks like we can update this from ipython_config.py

from pygments.token import Token
c.TerminalInteractiveShell.highlighting_style_overrides = {
    Token.Prompt: '#cc6666',
}

image

AdrienLemaire commented 4 years ago

Investigating the lack of color for ipdb shell commands

in prompt_toolkit/styles/style.py, I set __import__('pprint').pprint([vars(s) for s in styles]) in merge_styles to record the difference between ipdb and ipython terminal shells:

Looks like in ipdb's case, a style is added twice. It's being called from Application._create_merged_style in prompt_toolkit/application/application.py, itself only called during initialization of Application.

Thus we're re-initializing Application late in the process and nuking all the style setting done previously.

Application seems to get initialized from PromptSession initialization in prompt_toolkit/shortcuts/prompt.py

Calling ipython in a shell will only call TerminalInteractiveShell, but ipdb will call TerminalInteractiveShell followed by TerminalPdb.

IPython/terminal/interactiveshell.py

class TerminalInteractiveShell(InteractiveShell):
    @property
    def debugger_cls(self):
        return Pdb if self.simple_prompt else TerminalPdb

ipdb/main.py

debugger_cls = shell.debugger_cls

The shell.debugger_cls had been called and set up with ipapp.initialize()

TerminalInteractiveShell inherits Interactiveshell

IPython/core/ultratb.py

class VerboseTB(TBTools):
    def __init__(self, color_scheme='Linux', call_pdb=False, ostream=None,
                 tb_offset=0, long_header=False, include_vars=True,
                 check_cache=None, debugger_cls = None,
                 parent=None, config=None):
        self.debugger_cls = debugger_cls or debugger.Pdb

    def debugger(self, force=False):
        if force or self.call_pdb:
            if self.pdb is None:
                self.pdb = self.debugger_cls()

IPython/core/crashhandler.py

class CrashHandler(object):
    def __call__(self, etype, evalue, etb):
        TBhandler = ultratb.VerboseTB(
            color_scheme=color_scheme,
            long_header=1,
            call_pdb=self.call_pdb,
        )

IPython/terminal/ipapp.py

class IPAppCrashHandler(CrashHandler):
    ...

class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
    crash_handler_class = IPAppCrashHandler

Realizing that althought IPython uses TerminalInteractiveShell and calls debugger_cls, the TerminalPdb class is never instantiated!!!

Not sure how to debug this further, I do not understand how ipython colorizes text in the ipython shell image

gotcha commented 4 years ago

ipdb is a lightweight wrapper around IPython debugger. You might want to ask help to the IPython community.

zacharesmer commented 4 years ago

I went down this rabbit hole yesterday, and the solution does lie within IPython. IPython uses two different color schemes: one is Pygments, and the other one is changed through c.InteractiveShell.colors in the config file. For the second one there are very limited options and they only use basic 8-bit colors, presumably for compatibility. Using different ANSI escape codes, you can get a lot of much nicer colors depending on what your terminal supports.

If you compare this to master, you can see all the files that need to be changed to add the theme. It's probably possible to do all of that through the config file, but I'm not sure how.

camAtGitHub commented 1 year ago

Since this is the first hit on the googz for change ipdb colors - In my case the blues were too dark against the black background. Here's how you change the theme:

  1. Run: pip install ipdb
  2. Run: ipython profile create
  3. edit ~/ipython/profile_default/ipython_config.py
  4. Uncomment / change the line c.InteractiveShell.colors = (about line 442) to c.InteractiveShell.colors = 'Linux'
  5. Celebrate!
gotcha commented 1 year ago

@camAtGitHub Thanks a lot for that walkthrough !

woutervh commented 1 year ago

@gotcha Is there currently a way to tell ipdb to start a non-default ipython-profile?

my usecase:
I use startup-files of the default ipython-profile for interactive sessions,
but when debugging the application with pdb, i don't want that startup-code to be run.

as a quickfix, I'm currently wrapping my all code in startup-file in a condition like this:

import os
is_ipython = int(os.getenv("IS_IPYTHON", "0"))

if is_ipython:
    print(f"\nRunning {__file__}")
    .... actual startup-code ....

an ipdb-config setting to start ipdb initialised with an "ipdb-"ipython-profile would nice I think:

[ipdb]
profile=ipdb
woutervh commented 1 year ago

I'll create a PR for this

woutervh commented 1 year ago

See https://github.com/gotcha/ipdb/pull/266