2

In my understanding, subprocess.Popen() should create a new process and does not block the main one.

However, the following scripts do not print until they have finished.

It appears that the print job is added after the button is pressed, but for some reason not directly executed. (At least Ubuntu shows an added print job.)

Why does this behavior occur?

#! /usr/bin/env python3
# -*- coding: utf-8 -*-

import subprocess

lpr =  subprocess.Popen("/usr/bin/lpr",              # on raspbian: /usr/bin/lp
                        stdin=subprocess.PIPE,
                        stdout=subprocess.DEVNULL,   # proposed by user elias
                        close_fds=True)              # proposed by user elias

output = "Username: testuser\n".encode() \
         + "Password: p4ssw0rd\n".encode()

lpr.stdin.write(output)

while True:
    pass

The above script does not print anything, even after it has been quit using ctrl-c. (The print job seems to stay in the queue.)

#! /usr/bin/env python3
# -*- coding: utf-8 -*-

import subprocess
import time

lpr =  subprocess.Popen("/usr/bin/lpr",              # on raspbian: /usr/bin/lp
                        stdin=subprocess.PIPE,
                        stdout=subprocess.DEVNULL,   # proposed by user elias 
                        close_fds=True)              # proposed by user elias

output = "Username: testuser\n".encode() \
         + "Password: p4ssw0rd\n".encode()

lpr.stdin.write(output)

time.sleep(20)

This prints after 20 seconds (when the script ends).


About the execution environment:

  • os: ubuntu 18.04 (does also occur on raspbian)
  • python: 3.6.5
  • printer: a network printer via CUPS (does also occur when connected via USB)

SOLUTION:

As can be seen in the comments of the answer from user elias, the behavior was caused by buffering.

The problem was solved by closing stdin.

lpr.stdin.close()
AFoeee
  • 731
  • 7
  • 24

1 Answers1

1

I believe if you don't specify stdout in the Popen call it will share the same as the parent process, whose output your program is probably owning.

Try adding stdout=subprocess.DEVNULL (or stdout=subprocess.PIPE, in case you want to capture that output).

From the docs:

stdin, stdout and stderr specify the executed program’s standard input, standard output and standard error file handles, respectively. Valid values are PIPE, DEVNULL, an existing file descriptor (a positive integer), an existing file object, and None. PIPE indicates that a new pipe to the child should be created. DEVNULL indicates that the special file os.devnull will be used. With the default settings of None, no redirection will occur; the child’s file handles will be inherited from the parent.

Elias Dorneles
  • 22,556
  • 11
  • 85
  • 107
  • Thanks for your quick reply. I added the parameter, but I am not sure if it solved the problem. It appears that a print job is added right after the button is pressed, but not directly executed. Maybe it is a peculiarity of Ubuntu? – AFoeee Sep 12 '18 at 09:45
  • 1
    Hm, it could be... If you still want to make sure it will run in background, you can also try `close_fds=True` (saw this on: https://stackoverflow.com/a/34459371/149872) – Elias Dorneles Sep 12 '18 at 12:00
  • I've added the parameter, but sadly the behavior still occurs. In the meantime I have posted the problem on [superuser.com](https://superuser.com/questions/1357650/why-is-the-print-job-not-executed-until-the-urwid-application-has-finished) and updated the question. – AFoeee Sep 12 '18 at 20:05
  • 1
    @AFoeee does the same thing happen if you run the command in a shell-script instead of a python script? something like: `echo -e "Username: testuser\nPassword: p4ssw0rd" | /usr/bin/lpr &` – Elias Dorneles Sep 13 '18 at 08:18
  • 1
    If you get the same behavior, then the problem is definitely unrelated to python/urwid. – Elias Dorneles Sep 13 '18 at 08:18
  • The shell command is printed immediately. I also revised the question, because it has turned out that it has nothing to do with urwid, but generally occurs when printing in python. – AFoeee Sep 13 '18 at 22:40
  • 1
    Hmm, interesting! Maybe it's because of buffering? You could try flushing the stdin after writing to it, like `lpr.stdin.flush()` or maybe even closing it `lpr.stdin.flush()` ? – Elias Dorneles Sep 14 '18 at 07:58
  • Good call isolating the problem, btw! We're getting closer :) – Elias Dorneles Sep 14 '18 at 07:59
  • Thank you very much! `lpr.stdin.close()` has solved the problem. Now I feel kind of silly, that I was trying to terminate `lpr`, but not `lpr.stdin`... – AFoeee Sep 14 '18 at 09:26
  • Right, i meant to write `lpr.stdin.close` the second time! Don't feel silly, we just learned something! :) – Elias Dorneles Sep 14 '18 at 09:39