0

I am trying to run code in the python interpreter from a python script (on windows, using the terminal build in to vsc), but I can't make anything work. I have spent a lot of time using the subprocess,and have also tried os module, but the issue with those, is that they cannot run code in the interpreter. So, I can make them start the interpreter, and I can enter code myself, which my script can get the result of (stdout and stderr), but it cannot enter code into the interpreter. I have tried running multiple commands in a row, using \n\r in the commands, and a few other attempts, but it always runs the second command/line after I manually quit() the interpreter. I have tried almost all of the functions from the subprocess module, and have tried numerous configrations for stdin, stdout, and stderr.

So, my qyuestion is: How can I have a script enter code into the interpreter?

It would also be nice to collect the results in real time, so my script does not have to start and quit an instance of the interpreter every time it wants the results, but that is not a priority.

Example of the issue with the OS module (but the issue is more or less the same with subprocess:

My code:

import os
print(os.popen("python").read())
print(os.popen("1 + 1").read())

Result:

Python 3.10.8 (tags/v3.10.8:aaaf517, Oct 11 2022, 16:50:30) [MSC v.1933 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> 1 + 2    #entered by me
>>> quit()   #entered by me
3            #what the print statement returns

'1' is not recognized as an internal or external command,
operable program or batch file.

P.S. I am aware there is another question about this issue, but the only answer it has does not work for me. (When using the module they say, python cannot find the module after I installed it)

EDIT: my code with subprocess:

import subprocess as sp
c = sp.Popen("python", text=True, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE)
c.stdin.write("1 + 1")
c.stdin.close()
print(c.stdout.read())
Finn E
  • 309
  • 2
  • 12
  • 1
    Each call to `os.popen()` starts a new process. You can't use the second call to feed input to the first one. – Barmar Oct 27 '22 at 20:04
  • 1
    Use `subprocess.Popen()` to start a Python child process. Then write to its `stdin` pipe to send input to it, and read from its `stdout` pipe to get the output. – Barmar Oct 27 '22 at 20:05
  • @Barmar `writing()` caused the entire thing to freeze. It brought up a blank line with the cursor as if i had an `input()` function but I cannot type anything and it is not printing anything. `^C` does nothing. I am going to edit the question to show the code I used. EDIT: I tried closing `c.stdin`, now it prints a blank line and then the program is over. – Finn E Oct 27 '22 at 20:11
  • 1
    I don't understand why you would do this. If you're already in a script, just ask for input and use something like `eval` to have it run. You'd need to cleanse it to get rid of dangerous stuff (like `os.system("rm -rf /*")`), but you'd have the same problem with a bare interpreter. – Tim Roberts Oct 27 '22 at 20:17
  • @TimRoberts The reason is even stupider than what Im doing. I am attempting to make a programming language run in python. I am well aware this is a bad idea, and I am not even following the recommended steps for developing a language and how you would normally parse it. I was told that `eval` was quite slow, and that using the interpreter would be faster. If this is incorrect than I will use `eval`. – Finn E Oct 27 '22 at 20:21
  • 2
    `eval` is certainly quicker than launching a new interpreter. Python is essentially calling `eval` for every line you type in. – Tim Roberts Oct 27 '22 at 20:28
  • @TimRoberts Even if I managed to keep one interpreter going, without restarting it every time I wanted output? – Finn E Oct 27 '22 at 20:29
  • 1
    Maybe I'm missing something. But can't you just import the python script? That will make its code run as if you typed it into the interpreter. If you're trying to capture output from the script, then that needs to happen in the script. Any global variables defined in the script (eg a list you append to) will be available to you in the interpreter. – ibonyun Oct 27 '22 at 20:59
  • @FinnE -- You're right, but you're guilty of premature optimization. You shouldn't spend time developing an alternative until you KNOW that the easier solution isn't fast enough. – Tim Roberts Oct 27 '22 at 21:54
  • 1
    Even though I answered for this question, I don't recommend spawning a child process. Just use eval() as Tim Roberts said. Because the child process will do the same work as what will be done by eval() if it have been done by the parent process. – relent95 Oct 28 '22 at 01:30
  • @FinnE: The single interpreter *is* a loop reading text and calling `eval` over and over, but now you need to deal with IPC to feed it data and receive results (and deal with the inevitable deadlocks that occur with that sort of design). `eval` is the way to go here, managing a separate Python process just to do this is a terrible idea. – ShadowRanger Oct 28 '22 at 01:45
  • @ibonyun for my purposes, I would need to write to the file with my script and then import it. That could work, but I think I will use eval as it is much simpler. I know that a compiled script can run faster than eval or interpreted code, but I am going to be running usually only one line of code at a time, so at that point they should be about the same speed, only I also need to time to write to the imported script. – Finn E Oct 28 '22 at 14:53

1 Answers1

0

Use the suprocess library like this.

import sys
import subprocess

p = subprocess.run(sys.executable, text=True, capture_output=True,
    input='print(1+1)')
print(p.stdout)
print(p.stderr)

If you want to reuse a single child process, you have to implement a client and server system. One easy method is to implement a remote call with multiprocessing.Manager. See the example in the documentation.

As a side note, I don't recommend these if you don't have a good reason for spawning a child process, such as sandboxing an execution environment. Just use eval() in the parent process, because the child process will do the same work as what will be done by eval() if it has been done by the parent process.

relent95
  • 3,703
  • 1
  • 14
  • 17
  • I will use eval like you and others have said, but in the interest of completing the thread, I cannot get the result of the command in my code (printing to the console doesnt help my purposes). `(yourcode).stdout` returns `None`. and when I add the `stdout=subprocess.PIPE` argument to `run()` it returns an empty string. – Finn E Oct 28 '22 at 14:59
  • I updated the example to capture stdout. – relent95 Oct 28 '22 at 15:07
  • Both stdout and stderr still print empty strings. – Finn E Oct 28 '22 at 15:09
  • You probably didn't print anything to the stdout in the child process. – relent95 Oct 29 '22 at 00:29
  • I tried `1+1` (which prints 2 when using the interpreter) and I also tried `print(1+1)` – Finn E Oct 29 '22 at 15:06