Pythonリモート実行環境`python-remoteexec`
$ pip install python-remoteexec
何を作ったか
隔離されたサーバーやコンテナ環境でPythonコードを安全に実行するための新しいリモート実行環境です。
Pythonのexec組み込み関数を、別の場所で実行するライブラリ、といった感じです。
何のためにあるのか
ChatGPTのCodeInterapterのように、AIエージェントにソースコードを生成させてそのコードを実行する手法で、セキュリティを担保するために使用する想定です。
その他にも、ループ実行の周波数を指定したり、エラー発生時の例外の扱い方などを細かく設定できるので、AI生成コードのような正体のよく分からないコードを実行環境の側から制御することが出来ます。
またブレークポイントの設定等が出来るので、デバッグ環境の構築にも使えるんじゃないかと思います。
使い方:
- サーバー側
from remoteexec.communicate import *
from remoteexec.inout import *
listen_addr = '1.1.1.1'
listen_port = 9165
sync_frequency = -1
fpS = SocketIO(listen_port=listen_port, listen_addr=listen_addr)
server = Communicator(connection=fpS, sync_frequency=sync_frequency, use_compress=True)
server.host(reciever=SocketReciever())
これでリモート実行環境のサーバーが起動します。
クライアントがサーバーにソースコードを送り付けて、サーバー上での実行結果をリアルタイムに同期する感じです。
クライアント側
- オブジェクトの共有とループ周波数の制御
import time
share = {'hoge':[]}
cond = RunningConditions(shared_objects=share)
code = """\
for i in range(500):
if i%100==0:
hoge.append(f'{i}')
"""
## The loop runs at 50Hz in the server
start = time.time()
runner.exec(code, cond=cond, frequency=50) # take 10 sec to run
print(time.time() - start) # around 10
print(','.join(share['hoge'])) # display '0,100,200,300,400'
上の例では、ループの実行周波数を50Hzに設定しています。
サーバーに送ったソースコードでは、500回のループが走るので、返ってくるまで10秒ほどかかることになります。
指定しないと最大速度での動作になります。サーバーのCPUリソース食い潰し対策などに。
- サーバー → クライアント側の関数呼び出し
import os
@snippet_share
class clz1:
def __init__(self):
self.n = ''
def p(self):
return print(self.n)
share = {'clz':clz1()}
cond = RunningConditions(shared_objects=share)
code = """\
clz.n = 'aaazzz'
clz.p()
"""
runner.exec(code, cond) ## display 'aaazzz'
@snippet_share
を指定してクラスを作成して、共有オブジェクト化すると、サーバーに送ったソースコードの中からそのクラスのメンバーにアクセス出来ます。
関数呼び出しの引数と戻り値はシリアライズ可能な型である必要があります。
- セキュリティ対策
cond = RunningConditions(allow_global_functions=['int'])
code = dedent("""\
a = int(10)
""")
runner.exec(code, cond)
組み込み関数exec
のglobalsに相当します。何も指定しないと、globals=None
相当、つまりPython標準の組み込み関数は何も使えません。
cond = RunningConditions(allow_import_modules=['time'])
code = """\
import time
"""
runner.exec(code, cond)
インポートを許可するパッケージを指定します。何も指定しないと、import
文が動きません。何でもインポート出来るようにするには、dynamic_import=True
を指定します。
cond = RunningConditions(allow_global_functions=COMMON_BUILTINS,
allow_import_modules=COMMON_MODULES)
とりあえず安心して使えるPython標準関数とパッケージのみ使うには、このようにします。open
のようなファイルシステムに影響が出る関数とパッケージは使えなくなります。
リポジトリ
他にもDockerコンテナ内でソースコードを実行するなど、色々な機能があります。
詳しい使い方はリポジトリのドキュメントを参照してください。
ではでは
おまけ
langchainで生成コードをDocker内で実行するようにしてほしかったのでIssue立ててます。だれか実装してPR出してくれると嬉しいです。