pythonlinuxstdoutinteractiveread-eval-print-loop

How can I execute a Python script in the REPL interpreter mode and get the exactly same output as if it was manually typed in? (Ubuntu, Python 3.12)


The Python interpreter can be run either in script or interactive/REPL mode. I do have a Python script as text file but want to run it as if it was manually typed in in the interactive/REPL mode. I want to get the output (stdout) exactly as if this was done. To give an example, assume that I have the following text stored in a file called srcipt.py

a = 5
a + 8
if a < 4:
    print("123")
else:
    print("xyz")

exit()

I want to execute this script and get the following stdout:

Python 3.12.3 (main, Jun 18 2025, 17:59:45) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 5
>>> a + 8
13
>>> if a < 4:
...     print("123")
... else:
...     print("xyz")
... 
xyz
>>> exit()

As far as I can see, this is not possible using the python3 command with any of the standard options. So I am looking either for some Bash script or maybe a Python program that would take the path to my script as input and then produce the stdout as required.

If need be, I could also change my input script to look like this:

>>> a = 5
>>> a + 8
>>> if a < 4:
...     print("123")
... else:
...     print("xyz")
>>> exit()

I feel that what I want should be possible, because I think that doctests doing something not very far from this.

Many thanks for any suggestions or pointers.

Cheers, Thomas.


Solution

  • Have you tried the code module?
    I would simply supply each line as input to code module and then process it later for whatever type of output I want.

    Example

    import code
    import sys
    import io
    from contextlib import redirect_stdout
    
    def simulate_repl(script_path):
        # Read the script
        with open(script_path, 'r') as file:
            lines = file.readlines()
        
        # Remove trailing newlines and handle empty lines
        lines = [line.rstrip('\n') for line in lines]
        
        # If you really want the python header or skip it
        print(f"Python {sys.version} on {sys.platform}")
        print('Type "help", "copyright", "credits" or "license" for more information.')
        
        # Create an InteractiveConsole instance (best option for you)
        console = code.InteractiveConsole()
        
        # Buffer for multi-line statements
        source = []
        is_multiline = False
        
        for line in lines:
            # Skip empty lines
            if not line.strip():
                continue
                
            # Check if the line is a continuation (starts with ...)
            if line.strip().startswith('...'):
                source.append(line.replace('...', '', 1).lstrip())
                continue
            
            # If we're in a multiline block, complete it
            if is_multiline:
                # Execute the collected multiline block
                block = '\n'.join(source)
                print(f'>>> {source[0]}')
                for s in source[1:]:
                    print(f'... {s}')
                # Capture output
                with io.StringIO() as buf, redirect_stdout(buf):
                    more = console.push(block)
                    output = buf.getvalue()
                if output:
                    print(output, end='')
                source = []
                is_multiline = more
                if line.strip() == 'exit()':
                    break
                if not is_multiline:
                    continue
            
            # Handle single-line statements
            if line.strip() == 'exit()':
                print('>>> exit()')
                break
            
            # Print the prompt and the line
            print(f'>>> {line}')
            
            # Check if the line starts a multiline block
            if line.rstrip().endswith(':'):
                source.append(line)
                is_multiline = True
                continue
            
            # Execute single-line statement and capture output
            with io.StringIO() as buf, redirect_stdout(buf):
                more = console.push(line)
                output = buf.getvalue()
            if output:
                print(output, end='')
            is_multiline = more
    
    if __name__ == '__main__':
        if len(sys.argv) != 2:
            print("Usage: python repl_simulator.py <script_path>")
            sys.exit(1)
        simulate_repl(sys.argv[1])