4

I am using a piece of software that comes with an embedded (I hope that is the right word) version of IronPython. The IP is there to allow the user to write .py scripts that interact with the program API.

There is no ipy.exe or python.exe file available inside of the program files installation directory. The only thing in there are the various IronPython DLL files. The standard library is fully available.

There is no interactive command line available. I really need to debug my code with something more powerful than output to a text only popup widow, but the program just hangs when I trying to use the debugger:

import pdb;pdb.set_trace()

It would be great if there were a piece of code I could just drop in one of these .py modules that will launch a command line REPL for the current python session. I've tried solutions from SO using the code module, but it doesn't work.

Can it be done?

jeb
  • 78,592
  • 17
  • 171
  • 225
Rick
  • 43,029
  • 15
  • 76
  • 119
  • 1
    Does this help? https://stackoverflow.com/q/1395913/1426065 Check out the 2nd answer. – MattDMo Oct 08 '20 at 23:02
  • @MattDMo I specifically said I tried solutions using the code module and it does not work. A REPL window does not launch. – Rick Oct 08 '20 at 23:25
  • Could you elaborate "doesn't work" pls. Is there an exception? Or it runs without any console input visible? Does it accept keyboard input? – Kroshka Kartoshka Oct 30 '20 at 12:41

2 Answers2

4

For some reason using FIFO did not work properly so instead I implemented this strangeness. It uses files to communicate between a controller instance (just running it from terminal) and the pdb in a different code called from wherever. Also uses monkey patching to get our new set_trace into pdb.

import time
import pdb


IN_FILE = "in.pdb"
OUT_FILE = "out.pdb"


class _Receiver(object):
    def __init__(self, in_file):
        self.in_file = in_file
        with open(in_file, "w") as file:
            file.write("")

    def readline(self):
        x = ""
        while x == "":
            time.sleep(0.1)
            with open(self.in_file, "r") as file:
                x = file.readline()

        with open(self.in_file, "w") as file:
            file.write("")

        return x


class _Sender(object):
    def __init__(self, out_file):
        self.out_file = out_file
        with open(out_file, "w") as file:
            file.write("")

    def write(self, x):
        with open(self.out_file, "a") as file:
            file.write(x)

    def flush(self):
        pass


mypdb = pdb.Pdb(stdin=_Receiver(in_file=IN_FILE),
                stdout=_Sender(out_file=OUT_FILE))

pdb.set_trace = mypdb.set_trace

To use a breakpoint in some other code:

from youcannameit import pdb

pdb.set_trace()

To connect to pdb run the main batch script in a cmd. It works fairly well. Not as good as the bash version so I'll leave it there in case you choose to use that. You can't use CRT-C type q instead. Once you quit I suggest opening a new cmd because running again in the same one is buggy as hell. Maybe someone with more batch knowledge can fix it.

Main batch script:

@echo off

set IN_FILE="in.pdb"
set OUT_FILE="out.pdb"
set LOCK="lock.lock"

echo running > %LOCK%
start /b read.cmd
timeout 1 /NOBREAK >NUL
echo PDB:
goto :write_pdb


:print_file
type %OUT_FILE%
timeout 1 /NOBREAK >NUL
NUL > %OUT_FILE% 2>NUL
goto :read_pdb


:read_pdb
for /f %%i in (%OUT_FILE%) do set size=%%~zi
if %size% gtr 0 goto :print_file
timeout 1 /NOBREAK >NUL
goto :read_pdb


:write_pdb
set /P INPUT=%=%
echo %INPUT% > %IN_FILE%
If /I "%Input%"=="q" goto :end
If /I "%Input%"=="quit" goto :end
If /I "%Input%"=="exit" goto :end
goto :write_pdb


:end
del %LOCK%
echo Exiting..
EXIT /B 0

Secondary batch script so we can print and input at the same time (name it read.cmd and put it in the same folder as the main script):

@echo off

set IN_FILE="in.pdb"
set OUT_FILE="out.pdb"
set LOCK="lock.lock"

goto :read_pdb


:print_file
type %OUT_FILE%
timeout 1 /NOBREAK >NUL
NUL > %OUT_FILE% 2>NUL
goto :read_pdb


:read_pdb
if not exist %LOCK% goto :end
for /f %%i in (%OUT_FILE%) do set size=%%~zi
if %size% gtr 0 goto :print_file
timeout 1 /NOBREAK >NUL
goto :read_pdb

:end

In case you stick with the bash version::

#!/bin/bash

IN_FILE="in.pdb"
OUT_FILE="out.pdb"

echo PDB:

# trap ctrl-c and call ctrl_c()
trap ctrl_c INT

function ctrl_c() {
    kill $READER >/dev/null 2>&1
    exit 1
}

read_pdb(){
    while true
    do
        if [ -s $OUT_FILE ]
        then
            cat $OUT_FILE
            sleep 0.05
            > $OUT_FILE
        fi
    done
}

write_pdb(){
    while true
    do
        sleep 0.1
        read user_inp
        echo $user_inp > $IN_FILE

        if [ "$user_inp" == "q" ];
        then
            kill $READER >/dev/null 2>&1
            exit 1
        fi
    done
}

read_pdb &
READER=$!
write_pdb

To connect to a breakpoint in a terminal just execute the bash script. You do have to make sure that the paths (IN_FILE, OUT_FILE, LOCK) are actual paths for your system.

Hadus
  • 1,551
  • 11
  • 22
  • this looks promising! but since it is ironpython we are on windows, no bash available... i like the concept though. – Rick Oct 25 '20 at 01:12
  • actually on a second look, since all the bash script does is read/write text to/from the file, there's no reason i can't run it from a windows bash session using WSL. so this might work fine. – Rick Oct 25 '20 at 01:18
  • I mean I'll see how hard it is to translate it to a batch script. – Hadus Oct 25 '20 at 10:12
  • 1
    There you go even a batch version. It isn't as good but works well enough I think. – Hadus Oct 25 '20 at 12:18
  • @RicksupportsMonica why didn't you accept my answer? Are you waiting for more answers before you decide or is there some way I can improve it? – Hadus Oct 28 '20 at 11:09
  • 1
    I just haven't had time to try it out. Other projects taking priority. I also usually let my bounties expire fully and then wait until the last minute to award them, just in case there's someone else with additional useful input out there. – Rick Oct 28 '20 at 13:45
  • 1
    May be necroing this answer but +1 for the use of "[Monkey Patching](https://en.wikipedia.org/wiki/Monkey_patch)" first time I've seen that term used. – CaffeineDependent Jan 27 '22 at 20:01
0

Try this and pls report if it worked:

import code
code.InteractiveConsole(locals=globals()).interact()

It runs a usual Python console:

$ python3 ./temp.py
Python 3.6.9 (default, Jul 17 2020, 12:50:27)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>
Kroshka Kartoshka
  • 1,035
  • 5
  • 23
  • As I already explained in the question, this solution will not work. – Rick Oct 30 '20 at 11:45
  • The code runs fine, but no REPL window appears. The main program that executes the script just hangs waiting on the script to exit, and it never does. So I have to force quit the program. – Rick Oct 30 '20 at 16:35