Backend Eksekusi Kode Python โ€“ FLOWER

Dokumen ini menjelaskan cara kerja backend eksekusi interaktif untuk latihan koding Python di platform FLOWER. Backend ini berjalan dalam container Docker tersendiri, dan dapat dipanggil oleh frontend melalui API POST ke endpoint /run.


๐Ÿ”ง Struktur Sistem

  • Bahasa yang didukung: Python 3.x
  • Format komunikasi: JSON
  • Server: Flask + pexpect
  • Port default: 5010
  • Container name: eksekutor

๐Ÿ“ฆ File Utama: api_eksekusi.py

from flask import Flask, request, jsonify
import tempfile
import os
import pexpect
import shutil

app = Flask(__name__)

@app.route("/run", methods=["POST"])
def run_code():
    kode = request.json.get("kode", "")
    stdin = request.json.get("stdin", "")

    with tempfile.NamedTemporaryFile("w+", suffix=".py", delete=False) as f:
        f.write(kode)
        f.flush()
        file_path = f.name

    try:
        python_path = shutil.which("python3") or "python3"
        child = pexpect.spawn(f"{python_path} {file_path}", encoding="utf-8", timeout=5)

        output = ""
        stdin_lines = stdin.strip().splitlines()
        input_index = 0

        while True:
            try:
                idx = child.expect([pexpect.EOF, r":\s*$", r"\?\s*$", r">+\s*$", r".+?:\s*"])
                output += child.before

                if idx == 0:
                    break

                if input_index < len(stdin_lines):
                    child.sendline(stdin_lines[input_index])
                    input_index += 1
                else:
                    child.sendline("")
            except pexpect.exceptions.TIMEOUT:
                output += "\nโš ๏ธ Timeout: Program terlalu lama atau loop?"
                break

        output += child.read()

        return jsonify({
            "output": output.strip(),
            "error": ""
        })

    except Exception as e:
        return jsonify({
            "output": "",
            "error": str(e)
        }), 500

    finally:
        if os.path.exists(file_path):
            os.remove(file_path)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5010, debug=False)

๐Ÿณ Dockerfile

FROM python:3.10-slim

WORKDIR /app
COPY . /app

RUN pip install --no-cache-dir flask pexpect

CMD ["python", "api_eksekusi.py"]

๐Ÿš€ Deployment via Docker

Build image:

docker build -t python-api .

Jalankan container:

docker run -d --name eksekutor -p 5010:5010 python-api

๐Ÿ“ค Endpoint API

URL:

POST /run
Content-Type: application/json

Body (JSON):

{
  "kode": "print('Hello')",
  "stdin": ""
}

Contoh dengan input:

{
  "kode": "nama = input('Siapa nama kamu? ')\nprint('Halo', nama)",
  "stdin": "Alya"
}

Response (JSON):

{
  "output": "Siapa nama kamu? \nHalo Alya",
  "error": ""
}

๐Ÿ›ก๏ธ Keunggulan pexpect dibanding subprocess

Fitur subprocess.run pexpect
Dukungan input() interaktif โš ๏ธ Terbatas โœ… Ya
Prompt tampil natural โŒ Tidak โœ… Ya
Simulasi terminal asli โŒ Tidak โœ… Ya
Menangani looping dan timeout โš ๏ธ Sulit โœ… Ya

๐Ÿ“Œ Catatan Keamanan

  • Backend ini hanya digunakan secara internal dari aplikasi FLOWER.
  • Tidak disarankan untuk menerima kode dari publik tanpa sandbox tambahan.
  • Waktu eksekusi dibatasi (timeout=5 detik) agar tidak infinite loop.

๐Ÿงช Tes Lokal (Opsional)

Gunakan curl untuk mengetes langsung:

curl -X POST http://localhost:5010/run \
  -H "Content-Type: application/json" \
  -d '{"kode": "a = input(\"Nama?\")\nprint(\"Hai\", a)", "stdin": "Reza"}'

โœ… Status: Aktif

Pastikan container berjalan:

docker ps