0

I'm working on a Node.js application which accepts submitted Python code and runs test suites on those submissions. I've been using python-shell package to run Python within Node.js. When submissions fail or pass test cases, everything is fine. The problem appears when a Python script contains an endless loop. In this case the python-shell instance never terminates.
I've checked some of stack overflow already asked questions, such as How to set a time limit to run asynchronous function in node.js?, Start and Stop Python Script from NodeJS? and How can I stop PythonShell but nothing seems to deal with endless loops within scripts. I want to terminate my judgeRoutine() if the promise is not resolved within several seconds. I've tried to use timeout and promises to address the problem but so far to no avail. I include the code that I have:

//judge.js
const {PythonShell} = require('python-shell')

const fs = require('fs');

function judgeRoutine(){
    let output=""
    return new Promise(function(resolve, reject){
        PythonShell.run('./test.py', null,function(err, result){
            if(err){
                return err
            }
            output = result[1]
            console.log(output)

            //perform DB operations based on output value

            return resolve(output)
        })
        setTimeout(function(){if (output===""){
            return reject(new Error("Time out"));
        }
        else{
            resolve(output)
        }} ,1000)
    })
    .catch(err => console.log(err))
}

function runJudge(){
    new Promise(function(resolve, reject){
        //copy scripts into the folder
        //solution2.py contains an endless loop
        fs.copyFile('./submissions/solution2.py', './solution.py', (err) => {
            if (err) throw err;
            console.log('File was copied to destination');
        });

        fs.copyFile('./tests/test35345.py', './test.py', (err) => {
            if (err) throw err;
            console.log('File was copied to destination');
        })    
    }).then(judgeRoutine()).then(value => console.log("resolve value", value)).catch(err=> {if (err.name==="Time out"){
        reject("Time out")
    }})
}

module.exports={runJudge}

I would be happy to get any suggestions.

  • A Promise represents an asynchronously derived result, not the asynchronous process itself. Force-rejecting a Promise won't automatically cause the process to be terminated. If you have a hanging process, you will need to do two things - (1) issue a (shell) command to kill the process; (2) force-reject the promise. – Roamer-1888 Apr 24 '20 at 03:27
  • You can't promisify two asynchronous processes with one `new Promise(...)`. Your two `copyFile()` calls will need to be independently promisified and either chained with `.then()` (to serialise them) or the two Promises aggregated with `Promise.all()` (having initiated the two processes to run in parallel). – Roamer-1888 Apr 24 '20 at 03:31
  • `.then(judgeRoutine())` should read `.then(judgeRoutine)`. – Roamer-1888 Apr 24 '20 at 03:32
  • `runJudge()` should return Promise. – Roamer-1888 Apr 24 '20 at 03:33

1 Answers1

1

I included the code snippet that I used to stop an infinite loop in a python script that in running on my Node.js server.

Here I just created a simple promise to run the python script asynchronously to not to block the main thread. But I also added a removeEventlistener function that will stop running the python script after a certain number of time. You can adjust the value as per your need.

Also, you might have noticed that I didn't use any third party library and instead used the one built into Node js. Anyway, hope that it will give you an idea as to how to stop long running process of python scripts in Node.js

const pythonPromise = () => {
  return new Promise((resolve, reject) => {
    const python = spawn("python", ["./script.py"]);

    const print = (data) => {
      console.log(data.length);
      console.log(data);
      resolve(data.toString());
    };
    python.stdout.on("data", print);

    setTimeout(() => {
      python.stdout.off("data", print);
      resolve("Infinite code detected");
    }, 5000);

    python.stderr.on("data", (data) => {
      reject(data.toString());
    });
  });
};

async function runCode() {
  const value = await pythonPromise().catch(console.log);
  console.log(value);
} 

runCode()