You are not logged in.

#1 2009-02-13 15:33:36

u_no_hu
Member
Registered: 2008-06-15
Posts: 453

Cant spawn urxvt in python

As an exercise in learning python, i am trying to write an application launcher like dmenu. Only difference is that it will write everything directly on to screen (no window) using osdcat/pyosd. The problem is that it can launch other programs but launching urxvt fails.

 os.spawnlp(os.P_NOWAIT,"urxvt")

launches urxvt but it is launched as a defunct process and no window appears

4812  0.0  0.0      0     0 pts/1    Z+   20:52   0:00 [urxvt] <defunct>

when called with P_WAIT it returns with -11 which seems to be sigsegv

when called with os.system it works fine, but i guess its not the proper way to do this.

Any help will be appreciated.


PS: The partly baked code is below... It works with basic functionality ie you can launch programs other than urxvt. No editing of typed text, backspace,arrow keys etc currently. And it uses dmenu_path to get the list of programs wink

#!/usr/bin/python
from Xlib.display import Display
from Xlib import X
from Xlib import XK
import pyosd
import re
import commands
import os



a = commands.getoutput("dmenu_path").split("\n")
matches = []


def matchpattern(pattern,lst):
    return filter(lambda x: re.match(pattern.lower(),x) , lst)

def printmatches(matches,myosd):
    map(lambda x: myosd.display(x,line= matches.index(x)) , matches)

def clearlines(myosd):
    map(lambda x: myosd.display("",line=x),range(0,9))



def run_pgm(program):
    print "running"
    print program
    pid = os.fork()
    if not pid:
        os.execlp(program)


def handle_event(aEvent,disp,myosd,input):
    keycode = aEvent.detail
    if aEvent.type == X.KeyPress:
        keysym = disp.keycode_to_keysym(keycode,0)
        if keysym == XK.XK_Escape:
            disp.ungrab_keyboard(X.CurrentTime)
            return None
        elif keysym == XK.XK_Return:
            disp.ungrab_keyboard(X.CurrentTime)
            matches = matchpattern(input,a)
            print matches
            if len(matches) == 0:
                run_pgm(input)
            else:
                run_pgm(matches[0])
            return None
        else:
            input += disp.lookup_string(keysym)
            clearlines(myosd)
            myosd.display(input)
            printmatches(matchpattern(input,a)[0:9],myosd)
            return input
    else:
        return input

def main():
    disp = Display()
    root = disp.screen().root
    input = ""
    myosd=pyosd.osd("-*-courier-bold-r-*-*-24-*-75-*-*-*-*-*",colour="#00FF00",timeout=0,pos=0,align=1,lines=10)
    myosd.display("Run: ")
    root.change_attributes(event_mask = X.KeyPressMask)
    root.grab_keyboard(1,X.GrabModeAsync, X.GrabModeAsync,X.CurrentTime)

    while input != None:
        event = root.display.next_event()
        input = handle_event(event,disp,myosd,input)
        print 

if __name__ == '__main__':
    main()

Don't be a HELP VAMPIRE. Please search before you ask.

Subscribe to The Arch Daily News.

Offline

#2 2009-02-13 16:28:41

Procyon
Member
Registered: 2008-05-07
Posts: 1,819

Re: Cant spawn urxvt in python

Very interesting, I wonder about this too. Could you try some of these:

def run_pgm(program):
    print "running"
    print program
    pid = os.fork()
    if not pid:
        os.setsid()
        os.close(0)
        os.close(1)
        os.close(2)
        os.execlp(program,program) #it needs arg0 right?

And...

import subprocess

def run_pgm(program):
    print "running"
    print program
    pid = os.fork()
    if not pid:
        os.setsid()
        os.close(0)
        os.close(1)
        os.close(2)
        p=subprocess.Popen(program, shell=True)
        p.wait()
        os._exit()

edit: typo ('=' -> '.')

Last edited by Procyon (2009-02-13 16:29:37)

Offline

#3 2009-02-13 16:30:16

N30N
Member
Registered: 2007-04-08
Posts: 273

Re: Cant spawn urxvt in python

u_no_hu wrote:

i guess its not the proper way to do this.

Indeed, you should be using the subprocess module.

Offline

#4 2009-02-13 16:48:25

Procyon
Member
Registered: 2008-05-07
Posts: 1,819

Re: Cant spawn urxvt in python

N30N wrote:
u_no_hu wrote:

i guess its not the proper way to do this.

Indeed, you should be using the subprocess module.

Subprocess module says nothing about replacing os.exec* though.

Offline

#5 2009-02-13 19:01:26

Procyon
Member
Registered: 2008-05-07
Posts: 1,819

Re: Cant spawn urxvt in python

What I mentioned above is working for me, except that mplayer is still giving me trouble. If you close stdin and stdout the mplayer window won't show up at all. Even with -noconsolecontrols.

I just tried the following, reading and writing to /dev/null instead:

            pid=os.fork()
            if pid == 0:
                os.setsid()
                readnull=open("/dev/null",'r')
                writenull=open("/dev/null",'w')
                p=Popen(cmd,shell=True,stdin=readnull,stdout=writenull,stderr=writenull)
                p.wait()
                readnull.close()
                writenull.close()
                os._exit(0)

I wonder what better practice is generally, closing stdin or piping /dev/null to stdin.

EDIT:

Actually, this seems to create zombie processes too.

EDIT2:

OK. Time to read up on Unix stuff: http://en.wikipedia.org/wiki/SIGCHLD

So add

import signal
signal.signal(signal.SIGCHLD, signal.SIG_IGN)

to the start of your program, the zombies will go away.

BTW using exec* is nicer than Popen because the spawned program won't have a python parent. That makes it look a lot better in htop etc.

Last edited by Procyon (2009-02-14 14:59:09)

Offline

#6 2009-02-14 15:14:49

Procyon
Member
Registered: 2008-05-07
Posts: 1,819

Re: Cant spawn urxvt in python

I am now trying this in pyfmii:
-No zombie processes
-Bash launches the program, this is good because you can background bash syntax and that is really nice for a launcher program
-Double fork, so the new process isn't attached to the launcher but to init, this looks better in htop/pstree etc
-execvp instead of Popen so the name of the parent isn't the launcher

def main():
    signal.signal(signal.SIGCHLD, signal.SIG_IGN)
            pid=os.fork()
            if pid == 0:
                os.setsid()
                pid=os.fork()
                if pid > 1:
                    os._exit(0)
                os.closerange(0,os.sysconf("SC_OPEN_MAX")) #close all open FDs
                os.open("/dev/null", os.O_RDWR) #make /dev/null stdin
                os.dup2(0,1) #and stdout
                os.dup2(0,2) #and stderr
                toexec=["bash","-c"]
                toexec.append(cmd) #run command through bash
                os.execvp("bash",toexec)

This site has been a great help: http://code.activestate.com/recipes/278731/

Last edited by Procyon (2009-02-14 15:16:43)

Offline

Board footer

Powered by FluxBB