How to safely run unreliable piece of code?

2019-02-06 23:37发布

Suppose you are working with some bodgy piece of code which you can't trust, is there a way to run it safely without losing control of your script?

An example might be a function which only works some of the time and might fail randomly/spectacularly, how could you retry until it works? I tried some hacking with using threading module but had trouble to kill a hung thread neatly.

#!/usr/bin/env python

import os
import sys
import random

def unreliable_code():

  def ok():
    return "it worked!!"

  def fail():
    return "it didn't work"

  def crash():
    1/0

  def hang():
    while True: 
      pass

  def bye():
    os._exit(0)

  return random.choice([ok, fail, crash, hang, bye])()


result = None
while result != "it worked!!":
  # ???

3条回答
Luminary・发光体
2楼-- · 2019-02-07 00:16

You can try running it in a sandbox.

查看更多
神经病院院长
3楼-- · 2019-02-07 00:27

To be safe against exceptions, use try/except (but I guess you know that).

To be safe against hanging code (endless loop) the only way I know is running the code in another process. This child process you can kill from the father process in case it does not terminate soon enough.

To be safe against nasty code (doing things it shall not do), have a look at http://pypi.python.org/pypi/RestrictedPython .

查看更多
可以哭但决不认输i
4楼-- · 2019-02-07 00:30

In your real case application can you switch to multiprocessing? Becasue it seems that what you're asking could be done with multiprocessing + threading.Timer + try/except.

Take a look at this:

class SafeProcess(Process):
    def __init__(self, queue, *args, **kwargs):
        self.queue = queue
        super().__init__(*args, **kwargs)
    def run(self):
        print('Running')
        try:
            result = self._target(*self._args, **self._kwargs)
            self.queue.put_nowait(result)
        except:
            print('Exception')

result = None
while result != 'it worked!!':
    q = Queue()
    p = SafeProcess(q, target=unreliable_code)
    p.start()
    t = Timer(1, p.terminate)   # in case it should hang
    t.start()
    p.join()
    t.cancel()
    try:
        result = q.get_nowait()
    except queues.Empty:
        print('Empty')
    print(result)

That in one (lucky) case gave me:

Running
Empty
None
Running
it worked!!

In your code samples you have 4 out of 5 chances to get an error, so you might also spawn a pool or something to improve your chances of having a correct result.

查看更多
登录 后发表回答