0

I'm working on a pet project, I'm trying to make a command-line version of Jupyter (I totally understand how stupid that sounds, "why not just use the Python shell", this is just for fun). I've been trying to think of a way to start a Python instance in the background, allowing me to plug the user's input into that shell. But I just can't find any way to do so. Is there any reasonable way to do this?

Thank you!

EDIT: I'm considering something like Jython, but I'd prefer to do this completely in Python if at all possible.

1 Answer 1

1

There are two ways I can think off of the top of my head to do this. The first method is using exec to execute user input code.

while True:
    user_input = input("Python command to execute: ")

    try:
        exec(user_input)
    except Exception as e:
        print("Error thrown.")

This however, comes with its limitations. You have to write some custom code to catch errors, throw errors appropriately, and etc. The second method is a bit more involved, but also more generalized. You use the everything is a file method, and you treat the user input (whether it's through a shell, a website, or anything) as a file. With that file, then, you execute it. You can leave a shell open at all times checking if a file has updated before executing:

import hashlib
import runpy
import time

FILE = "./file.py"

def get_file_md5(file_name):
    with open(file_name, "rb") as f:
        return hashlib.md5(f.read()).hexdigest()

md5 = get_file_md5(FILE)
first_run = True

while True:
    current_md5 = get_file_md5(FILE)
    if md5 != current_md5 or first_run:
        first_run = False
        md5 = current_md5

        try:
            runpy.run_path(FILE)
        except Exception as e:
            print("Error", e)
    else:
        time.sleep(1)

You might find this answer that I gave to another (vaguely related) question interesting and of use.


Regarding below. Notice exec(object[, globals[, locals]]) documentation:

In all cases, if the optional parts are omitted, the code is executed in the current scope. If only globals is provided, it must be a dictionary (and not a subclass of dictionary), which will be used for both the global and the local variables.

So you can do:

exec_globals = {}
exec('a = 10; print(a)', exec_globals)

print("\na in exec_globals: ", 'a' in exec_globals)
print("exec_globals['a'] =", exec_globals['a'])

print("\na in globals(): ", 'a' in globals())
print(a)

And the above will output:

10

a in exec_globals:  True
exec_globals['a'] = 10

a in globals():  False

Traceback (most recent call last):
  File "test.py", line 7, in <module>
    print(a)
NameError: name 'a' is not defined
Sign up to request clarification or add additional context in comments.

3 Comments

I was considering exec() because it's so close to what I need! The only issue is that because it's run in the same python instance, if the user uses variables with the same names as the variables I use in the interpreter, everything will get messed up. Possible solutions are to use very weird variable names, but I'm not sure if that's effective. If there's a way to execute the exec() argument on a different python instance than the one that runs the exec() command, that'll be exactly what I need.
Made edit to the post. Docs for exec specifies how to deal with that. :)
Thank you so much! That's exactly what I needed

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.