Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Ipython StartUp Script for pudb python shell. #642

Closed
maltezc opened this issue Mar 22, 2024 · 3 comments
Closed

Ipython StartUp Script for pudb python shell. #642

maltezc opened this issue Mar 22, 2024 · 3 comments

Comments

@maltezc
Copy link

maltezc commented Mar 22, 2024

Is your feature request related to a problem? Please describe.
Is there a way to add a hook(not sure if this is the correct usage of the term 'hook') for a PYTHONSTARTUP variable to point to a startup.py file that can be ran whenever pudb starts up?

  • for example instead of having to type import pprint or import os with every time i startup pudb for the python console, it would already run automatically and i would only need to typepprint.pprint(variable_to_pretty_print)

I'm able to get it to work with my ipython shell from terminal however I can't get it to work in pudb's ipython. I figure if i can get it to work for my terminal's ipython, its possible for pudb?

Describe the solution you'd like
I would like for pudb to search my .zshrc file to look for a PYTHONSTARTUP or similar variable which would then point to a /my/pudb/startup.py script

Describe alternatives you've considered
not sure of any alternatives yet.

Additional context
Add any other context or screenshots about the feature request here.

What I tried
I gave it an attempt and modified the run.py. I tried in the iterm2 console python3 debug_me.py but no dice. if there is somewhere else I should be modifying, please let me know.

COMMAND = {"zsh": "{_command_names -e}"}
PREAMBLE = {
    "zsh": """\
_script_args() {
  # pudb -m <TAB>
  if (($words[(I)-m] == $#words - 1)); then
    _python_modules
  # pudb -m XXX <TAB>
  elif (($words[(I)-m])); then
    _files
  # pudb <TAB>
  else
    _arguments -S -s '(-)1:script_args:_files -g "*.py"' '*: :_files'
  fi
}
""",
}
SCRIPT_ARGS = {"zsh": "_script_args"}


def get_argparse_parser():
    import os
    import sys
    import argparse
    try:
        import shtab
    except ImportError:
        from . import _shtab as shtab

    from pudb import VERSION

    version_info = "%(prog)s v" + VERSION

    if sys.argv[1:] == ["-v"]:
        print(version_info % {"prog": "pudb"})
        sys.exit(os.EX_OK)

    parser = argparse.ArgumentParser(
        "pudb",
        usage="%(prog)s [options] [-m] SCRIPT-OR-MODULE-TO-RUN [SCRIPT_ARGS]",
        epilog=version_info
    )
    shtab.add_argument_to(parser, preamble=PREAMBLE)
    # dest="_continue_at_start" needed as "continue" is a python keyword
    parser.add_argument(
        "-c", "--continue",
        action="store_true",
        dest="_continue_at_start",
        help="Let the script run until an exception occurs or a breakpoint is hit",
    )
    parser.add_argument("-s", "--steal-output", action="store_true")

    # note: we're implementing -m as a boolean flag, mimicking pdb's behavior,
    # and makes it possible without much fuss to support cases like:
    #    python -m pudb -m http.server -h
    # where the -h will be passed to the http.server module
    parser.add_argument("-m", "--module", action="store_true",
                        help="Debug as module or package instead of as a script")

    parser.add_argument("-le", "--log-errors", nargs=1, metavar="FILE",
                        help="Log internal errors to the given file"
                        ).complete = shtab.FILE
    parser.add_argument("--pre-run", metavar="COMMAND",
                        help="Run command before each program run",
                        default="").complete = COMMAND
    parser.add_argument("--version", action="version", version=version_info)
    parser.add_argument("script_args", nargs=argparse.REMAINDER,
                        help="Arguments to pass to script or module"
                        ).complete = SCRIPT_ARGS
    return parser

def get_python_startup_script():
    import os

    # Look for PYTHONSTARTUP variable in .zshrc file
    zshrc_path = os.path.expanduser("~/.zshrc")
    python_startup_script = None

    if os.path.isfile(zshrc_path):
        with open(zshrc_path, "r") as f:
            for line in f:
                if line.startswith("export PYTHONSTARTUP="):
                    _, python_startup_script = line.strip().split("=")
                    python_startup_script = python_startup_script.strip('"\'')
                    break

    return python_startup_script


def main(**kwargs):
    import sys
    import os

    # Execute the Python startup script specified in .zshrc
    startup_script = get_python_startup_script()
    if startup_script and os.path.isfile(startup_script):
        with open(startup_script, "r") as f:
            exec(f.read())

    parser = get_argparse_parser()

    options = parser.parse_args()
    args = options.script_args

    if options.log_errors:
        from pudb.lowlevel import setlogfile
        setlogfile(options.log_errors[0])

    options_kwargs = {
        "pre_run": options.pre_run,
        "steal_output": options.steal_output,
        "_continue_at_start": options._continue_at_start,
    }

    if len(args) < 1:
        parser.print_help()
        sys.exit(2)

    mainpyfile = args[0]
    sys.argv = args

    if options.module:
        from pudb import runmodule
        runmodule(mainpyfile, **options_kwargs)
    else:
        from os.path import exists
        if not exists(mainpyfile):
            print("Error: %s does not exist" % mainpyfile, file=sys.stderr)
            sys.exit(1)

        from pudb import runscript
        runscript(mainpyfile, **options_kwargs)


if __name__ == "__main__":
    main()
@inducer
Copy link
Owner

inducer commented Mar 24, 2024

It seems as though you're wanting this to affect the shell in the debugger. Have you considered using pudb's custom shell feature?

@maltezc
Copy link
Author

maltezc commented Mar 25, 2024

I have not yet. If that is another way to go, then i will investigate it. Thank you!

@maltezc maltezc closed this as completed Mar 25, 2024
@maltezc maltezc reopened this Mar 29, 2024
@maltezc
Copy link
Author

maltezc commented Mar 29, 2024

hello again!

so i've hooked up the custom_shell and set up my file pudb_custom_shell.py and started messing with it.

i've found that i need to use cons.push() to "push" code to my custom shell. for example, if i have

cons.push("from pprint from pprint")

To use pprint in my console, i can just do `pprint("hello") and it comes out correctly.

is there a way i can run an entire .py file instead of just doing line by line in my pudb_custom_shell.py file?

i've tried the following below and it will output a print statement but it won't import pprint.

See below for 2 versions of my pudb_custom_shell.py file. one with cons.push('from pprint import pprint') and then the one trying import a ipython_startup_script.py file and running everything in that.

This version below works:

here is what it looks like:

"""
This file shows how you can define a custom shell for PuDB. This is the
shell used when pressing the ! key in the debugger (it does not affect the
Ctrl-x shell that is built into PuDB).

To create a custom shell, create a file like this one with a function called
pudb_shell(_globals, _locals) defined at the module level. Note
that the file will be execfile'd.

Then, go to the PuDB preferences window (type Ctrl-p inside of PuDB) and add
the path to the file in the "Custom" field under the "Shell" heading.

The example in this file

"""

Define this a function with this name and signature at the module level.

def pudb_shell(_globals, _locals):
"""
This example shell runs a classic Python shell. It is based on
run_classic_shell in pudb.shell.

"""
# Many shells only let you pass in a single locals dictionary, rather than
# separate globals and locals dictionaries. In this case, you can use
# pudb.shell.SetPropagatingDict to automatically merge the two into a
# single dictionary. It does this in such a way that assignments propogate
# to _locals, so that when the debugger is at the module level, variables
# can be reassigned in the shell.
from pudb.shell import SetPropagatingDict
ns = SetPropagatingDict([_locals, _globals], _locals)

try:
    import readline
    import rlcompleter
    HAVE_READLINE = True
except ImportError:
    HAVE_READLINE = False

if HAVE_READLINE:
    readline.set_completer(
        rlcompleter.Completer(ns).complete)
    readline.parse_and_bind("tab: complete")
    readline.clear_history()

from code import InteractiveConsole
cons = InteractiveConsole(ns)

cons.push("from pprint import pprint")  # this works.

other version trying to import and run an entire file.

"""
This file shows how you can define a custom shell for PuDB. This is the
shell used when pressing the ! key in the debugger (it does not affect the
Ctrl-x shell that is built into PuDB).

To create a custom shell, create a file like this one with a function called
pudb_shell(_globals, _locals) defined at the module level. Note
that the file will be execfile'd.

Then, go to the PuDB preferences window (type Ctrl-p inside of PuDB) and add
the path to the file in the "Custom" field under the "Shell" heading.

The example in this file

"""

Define this a function with this name and signature at the module level.

def pudb_shell(_globals, _locals):
"""
This example shell runs a classic Python shell. It is based on
run_classic_shell in pudb.shell.

"""
# Many shells only let you pass in a single locals dictionary, rather than
# separate globals and locals dictionaries. In this case, you can use
# pudb.shell.SetPropagatingDict to automatically merge the two into a
# single dictionary. It does this in such a way that assignments propogate
# to _locals, so that when the debugger is at the module level, variables
# can be reassigned in the shell.
from pudb.shell import SetPropagatingDict
ns = SetPropagatingDict([_locals, _globals], _locals)

try:
    import readline
    import rlcompleter
    HAVE_READLINE = True
except ImportError:
    HAVE_READLINE = False

if HAVE_READLINE:
    readline.set_completer(
        rlcompleter.Completer(ns).complete)
    readline.parse_and_bind("tab: complete")
    readline.clear_history()

from code import InteractiveConsole
cons = InteractiveConsole(ns)

# import code
import os

# Get the path to the .py file in your home directory
home_dir = os.path.expanduser("~")
module_path = os.path.join(home_dir, "ipython_startup_script.py")

# Import and execute the script
with open(module_path, 'r') as file:
    script_code = file.read()
    exec(script_code) # commenting out this or
    cons.push(script_code) # commenting out this one.

cons.interact("Press Ctrl-D to return to the debugger")
# When the function returns, control will be returned to the debugger.

my ipython_startup_script file looks like this:

print("running ipython_startup_script.py") # <-- this will output to the console
from pprint import pprint # <-- this does not get imported.

i get this error if i dont put them in a function
SyntaxError: multiple statements found while compiling a single statement

I've also tried button both of those lines in a function called Load() and then called Load() at the end.

Any ideas on where to go from here or is cons.push() line by line in the shell file the way to go?

Repository owner locked and limited conversation to collaborators Mar 29, 2024
@inducer inducer converted this issue into discussion #643 Mar 29, 2024

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Projects
None yet
Development

No branches or pull requests

2 participants