Python 3 exec() from user input

Question:
I am trying to have it so that a user can run a python file from within a program being run. I am essentially making an operating system for an advanced calculator, and want users to be able to open a python file from the internet. I am getting errors with the exec() builtin function. What am I doing wrong?

Repl link:
https://replit.com/@DaveBoyo/PsuedOS#main.py

#the function that would run the "run program" function

def runEx():
  global passWord
  runExParam = input('run param:')
  passVer = input(userName + '@main @runEX: enter password: ')
  if (passVer == passWord):
    os.system('clear')
    exec(runExParam)
    main()
  else:
    os.system('clear')
    print('INVALID PASSWORD...')
    print('')
    time.sleep(2)
    main()

I think I get your question, you mean when you entered an error’d code when doing ‘runEx’ you will get an error and quit right?

If so, you can just make a try/except statement
Here is an example

def runEx():
  global passWord
  runExParam = input('run param:')
  passVer = input(userName + '@main @runEX: enter password: ')
  if (passVer == passWord):
    os.system('clear')
    try:
        exec(runExParam)
    except Exception as e:
        print(f'{e.__class__.__name__}: {e}')
    main()
  else:
    os.system('clear')
    print('INVALID PASSWORD...')
    print('')
    time.sleep(2)
    main()

Here is one of my old project which do somewhat equal to your runEx function for your reference, you may copy some code if you want: https://replit.com/@s3D27ZHOU/python-interactive-console-in-codes

By the way
I don’t actually recommend directly allowing user to use exec in python without checking the code or hiding your codes’ variables and things
People could literally ruin your entire running process by something like

for everything in dir():
  del everything

or even your entire file by

with open(__file__, 'w') as f:
  pass

So, I personally do NOT recommend using exec directly from prompt

TaokyleYT,
I think in this case it is fine to execute raw user input because, well, it’s supposed to be an OS. If the user bricks the OS, that’s on them.

The first code snippet, with dir(), has no effect.

Also, I checked out your project and… why does it have so many random dunder names?

Hello, there looks to be nothing seriously wrong with your code, but you need to wrap the exec() in a try-except block just in case the code gives an error (including syntax errors).
In addition to this, specify globals as an empty dict, or else the user code will have access to the globals and locals in the current scope.

import traceback

try:
  exec(runExParam, {})
except:
  print(traceback.format_exc())

The rest of your code looks relatively fine, improvements could be made but nothing that affects the program too much.

I should point out quickly that the user chooses a new username and password for each session – this is why I am totally cool with users having access to global variables.

@TaokyleYT I appreciate the info. The program does error out when a user tries running runEx, but to be more clear, runEx runs, but usually the source has a generic syntax error. I am asking the user for an http:// requested python program is sort of the end goal here. Which seems to be the main problem.

1 Like

Hey @DaveBoyo, could you please send the Repl link?

he did:
https://replit.com/@DaveBoyo/PsuedOS

2 Likes

Oh, try this then:

def runEx():
    global passWord
    runExParam = input('Enter the URL of the Python file to run:')
    passVer = input(userName + '@main @runEX: enter password: ')
    
    if passVer == passWord:
        try:
            if not runExParam.startswith(('http://', 'https://')):
                raise ValueError('URL must start with http:// or https://')

            response = requests.get(runExParam)
            response.raise_for_status()  
            python_code = response.text

            exec(python_code, {'__name__': '__main__'})
        except requests.RequestException as e:
            print(f"Error fetching file: {e}")
        except ValueError as e:
            print(e)
        except Exception as e:
            print(f"Error executing file: {e}")
        finally:
            main()
    else:
        print('INVALID PASSWORD...')
        time.sleep(2)
        main()
1 Like

Nice. I refactored the code to better handle exceptions:

import traceback

...

def clear():
    # Note: this is shell-specific, though you are making an entire os
    os.system('clear')

def runEx():
    # This function can be refactored in smaller ones
    runExParam = input('Enter the URL of the Python file to run:')
    passVer = input(userName + '@main @runEX: enter password: ')
    clear()
    if passVer != passWord:
        print('INVALID PASSWORD...')
        print()
        time.sleep(2)
        main()
        return
    if not runExParam.startswith(('http://', 'https://')):
        # \/ consider having custom shell errors for your os
        print('error: URL must start with http:// or https://')
    try:
        response = requests.get(runExParam)
        response.raise_for_status()  
        python_code = response.text
    except requests.RequestException as e:
        print(f'Error fetching file: {e}')
    else:
        try:
            exec(python_code, {'__name__': '__main__'})
        except SystemExit:
            pass  # you can print exit code
        except BaseException:
            print(traceback.format_exc())
        else:
            ...  # you can print nothing or a message
    main()

Also Dave, I would highly recommend to refactor your code to not recursively call main().

2 Likes

Perfect, this doesn’t seem to fully solve it the way I want, but it does “do it.”

Also, @NuclearPasta0 I understand many don’t like the recursion I am using, I will consider it lol

2 Likes

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.