2

The original Problem - MCVE

The following script should use chrome headless, to print to pdf (I am running windows 10, and python 3.6):

import subprocess
from tempfile import NamedTemporaryFile

output = NamedTemporaryFile()
CHROME_PATH=r'"C:\Program Files (x86)\Google\Chrome\Application\chrome"'

chrome_args=[CHROME_PATH,
             '--headless',
             r'--print-to-pdf="{}"'.format(output.name),
             '--disable-gpu',
             'https://www.google.com/',]

subprocess.call(chrome_args,shell=True)

However the generated file, is just empty.

Attempt at debugging

To try and figure out what is going, on I adapted the script to the following:

import subprocess
CHROME_PATH=r'"C:\Program Files (x86)\Google\Chrome\Application\chrome"'

chrome_args=[CHROME_PATH,
             '--headless',
             r'--print-to-pdf="c:\Users\timmc\Documents\output.pdf"',
             '--disable-gpu',
             'https://www.google.com/',]

print(r" ".join(chrome_args))  #For debuging

subprocess.call(chrome_args,shell=True)

In this case, there is just no file generated at the expected location. The result of the print is:

"C:\Program Files (x86)\Google\Chrome\Application\chrome" --headless --print-to-pdf="c:\Users\timmc\Documents\output.pdf" --disable-gpu https://www.google.com/

if I run the following (creating a raw string literal), everything works as expected and the file is produced.

subprocess.call(r'"C:\Program Files (x86)\Google\Chrome\Application\chrome" --headless --print-to-pdf="c:\Users\timmc\Documents\output.pdf" --disable-gpu https://www.google.com/', shell=True)

Having searched around on stack-overflow, and tried a few things, I still can’t get the original script to work. Any ideas?

Part of the problem is that I can't seem to get any meaningful debug from the subprocess call. Any help with that would also be much appreciated.

tim-mccurrach
  • 6,395
  • 4
  • 23
  • 41

2 Answers2

3

I'll try to answer instead of commenting again and again, but obviously I cannot test this.

The issue is mainly the forcing of the double quotes & shell=True. Leaving the quoting to subprocess (also in CHROME_PATH) and splitting arguments properly usually work. I've solved a lot of questions here with this technique.

Since your comments state that it does not, and that you found a workaround, let me suggest an improvement of this workaround: injecting the output filename in the command line that works:

subprocess.call(r'"C:\Program Files (x86)\Google\Chrome\Application\chrome" --headless --print-to-pdf="{}" --disable-gpu https://www.google.com/'.format(output.name), shell=True)

not satisfactory to me but it has a good chance to work.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • I'm still getting empty files, running this script. Is there anyway I can get any kind of error messages that might be returned from subprocess.call()? – tim-mccurrach Nov 30 '17 at 15:01
  • change to ``check_output`. This is becoming "interesting" – Jean-François Fabre Nov 30 '17 at 15:02
  • That gives the following: subprocess.CalledProcessError: Command '['C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome', '--headless', '--print-to-pdf', 'C:\\Users\\timmc\\AppData\\Local\\Temp\\tmp761ozhvy', '--disable-gpu', 'https://www.google.com/']' returned non-zero exit status 1. – tim-mccurrach Nov 30 '17 at 15:05
  • I finally figured out why it wasn't working, solution below if you are interested. – tim-mccurrach Dec 03 '17 at 16:30
1

It turns out that the reason the subprocess wasn't running properly, is that when python creates a NamedTemporaryFile in windows, it does so with a FILE_SHARE_DELETE tag which prevents any other process accessing it unless it also has this tag. There is more discussion of this here.

Fortunately, Django comes with its own NamedTemporaryFile which was made to partially address this problem, and does so well enough for these purposes.

tim-mccurrach
  • 6,395
  • 4
  • 23
  • 41