Search code examples
phppythonpython-3.xselenium-chromedrivershell-exec

Python Script Hangs When Called via PHP shell_exec()


I have a Python script that is called from PHP using shell_exec, all was working fine and the script came back to the PHP function after the Python script completed and everything was working as exepcted.

Now I have changed the Python script to include a class one level up from it that creates a web driver and returns the driver to the script that calls it via an object and then another function closes the driver that is passed in as a parameter.

Since setting up this new class and adding in an object to create/close a driver for webdriver using chrome the Python script hangs after completion and it never hands back control to the PHP script to finish processing.

I've had a look at php system() shell_exec() hangs the browser and also at http://php.net/manual/it/function.shell-exec.php#106250 with no luck.

I need the PHP script to run and then pass to the Python script, wait for the Python script to complete and hand back to the PHP script to finish processing.

All this process is kicked off from Run.php with:

$reponse = shell_exec('python36 "script1.py" "https://www.example.com/" "23"');

Directory Structure

root_dir
    python_dir
        scripts_dir
            Script1.py
            Script2.py
            Script3.py
        Global.py
    run.php

Global.py

import re
from selenium import webdriver

class Global:

    def getDriver(self):
        try:
            chrome_options = webdriver.ChromeOptions()
            chrome_options.add_argument('headless')
            chrome_options.add_argument('no-sandbox')

            # Windows - Dev Environment
            driver = webdriver.Chrome(executable_path='C:\chromedriver.exe', chrome_options=chrome_options)
            # Linux - Prod Environment
            #driver = webdriver.Chrome(chrome_options=chrome_options)

            return driver
        except Exception as e:
            print('Error: ' + str(e.args[0]))

    def closeDriver(self, driver):
        try:
            driver.close()
            return None
        except Exception as e:
            print('Error: ' + str(e.args[0]))

All the scripts in the scripts_dir have different code in them depeneding on the task they are doing, but the new Global object stuff is the same for all scripts, the new Global object is the only change made to all the scripts since they were working fine till now where the Python scripts hang.

Script1.py

import os, sys
ROOT_DIR = os.path.normpath(os.path.join(os.path.abspath(__file__), '../..'))
sys.path.insert(0, ROOT_DIR)
from Global import Global

globalObj = Global()

try:
    # Load all items on page
    driver = globalObj.getDriver()
    driver.get(PARAMS[1])

    #... code to create a JSON file and populate it.

    # Close webdriver
    globalObj.closeDriver(driver)

    print('Completed Ref: ' + str(PARAMS[2]))

except Exception as e:
    print('Error: ' + str(e.args[0]))

There are no errors or warnings in Python or PHP or any other logs available to me, the Python scripts run and create the expected JSON files they should and all looks to be working normally, the line print('Completed Ref: ' + str(PARAMS[2])) doesn't seem to print out when the Python script hangs.

Running Script1.py on the command line takes about 35 seconds to complete successfully.

Update - 27 Feb 2018

If I remove the calls to the Global class to getDriver and closeDriver then the script runs and completes and hands back to PHP to continue processing.

Script1.py - Update

import os, sys
from selenium import webdriver

try:
    # Load all items on page
    chrome_options = webdriver.ChromeOptions()
    chrome_options.add_argument('headless')
    chrome_options.add_argument('no-sandbox')

    driver = webdriver.Chrome(executable_path='C:\chromedriver.exe', chrome_options=chrome_options)
    driver.get(PARAMS[1])

    #... code to create a JSON file and populate it.

    # Close webdriver
    driver.close()

    print('Completed Ref: ' + str(PARAMS[2]))

except Exception as e:
    print('Error: ' + str(e.args[0]))

The script runs fine in both command line and when executed via PHP using shell_exec using the above example, so there must be something I'm missing when it comes to creating an object and call the getDriver or closeDriver classes.

Update 2 - 27 Feb 2018

After further testing, I can confirm that the issue is being caused by the script1.py script calling the class Global to create the webdriver and close it, once I remove these calls in script1.py the script runs fine.

I've tried putting in sys.exit() after the globalObj.closeDriver(), I've also tried del globalObj at the end of the script with no luck.

Update 3 - 27 Feb 2018

I've changed out the webdriver from Chrome to Firefox and PhantomJS, Other drivers are working fine with the Global class, looking like it might be related to the Chrome driver at this stage.

I'm using Chrome 64.0.3282.186 (Official Build) (64-bit) and Chromedriver 2.35.


Solution

  • It looks like driver.close() is the cause of the problem. You need to use .quit() with Chromedriver to end the chromedriver process otherwise the process will not terminate which is why the Python script looked like it was hanging.

    From:

    driver.close()
    

    To:

    driver.quit()