Amar Kalabić
Amar Kalabić

Reputation: 898

List index out of range in subprocess in Python

I was using code from this answer to turn volume up and down for certain program (BS.player in this case), but I'm having problems when calling get_master_volume function. I'm using it inside pyHook's "OnKeyboardEvent", here's that part of code:

def get_master_volume():
    config.read('%s/config.cfg'%os.path.abspath(os.path.dirname(__file__)))
    proc = subprocess.Popen('%s sget Master '%config.get('main', input1), shell=True,    stdout=subprocess.PIPE) #config.get gives FULL and correct path to bsplayer.exe
    amixer_stdout = proc.communicate()[0].split('\n')[4]
    proc.wait()

    find_start = amixer_stdout.find('[') + 1
    find_end = amixer_stdout.find('%]', find_start)

    return float(amixer_stdout[find_start:find_end])

def set_master_volume(volume):
    config.read('%s/config.cfg'%os.path.abspath(os.path.dirname(__file__)))
    val = float(int(volume))

    proc = subprocess.Popen('%s sset Master '%config.get('main', input1) + str(val) +  '%', shell=True, stdout=subprocess.PIPE) #config.get gives FULL and correct path to bsplayer.exe
    proc.wait()

def OnKeyboardEvent(event):
    #nothing important
    if config.has_option('main', input1):
       set_master_volume(get_master_volume() - 1)
       print "New volume:", get_master_volume()

This is error:

File "c:/Users/Amar/Documents/volume+/main.py", line 53, in get_master_volume
     amixer_stdout = proc.communicate()[0].split('\n')[4]
IndexError: list index out of range

Can anyone explain me why this error fires and what's causing it (and what should I do to fix it)?

Best regards!

EDIT:

When I print proc.communicate() out I get this errors, what this means?

proc.communicate:'C:\Program' is not recognized as an internal or external comma
nd,
operable program or batch file.
('', None)

EDIT 2:

New error after I fixed bug in my config which was pointing on wrong bsplayer.exe path:

 Traceback (most recent call last):
 File "c:\Python27\lib\site-packages\pyHook\HookManager.py", line 351, in Keybo
 ardSwitch
 return func(event)
 File "c:/Users/Amar/Documents/volume+/main.py", line 101, in OnKeyboardEvent
  set_master_volume(get_master_volume() - 1)
 File "c:/Users/Amar/Documents/volume+/main.py", line 54, in get_master_volume
  amixer_stdout = proc.communicate()[0].split('\n')[4]
 File "c:\Python27\lib\subprocess.py", line 798, in communicate
  stdout = _eintr_retry_call(self.stdout.read)
 File "c:\Python27\lib\subprocess.py", line 478, in _eintr_retry_call
  return func(*args)
 ValueError: I/O operation on closed file

Upvotes: 3

Views: 4724

Answers (2)

glglgl
glglgl

Reputation: 91049

Let me try to answer these (actually) three questions you have.

  1. IndexError

    File "c:/Users/Amar/Documents/volume+/main.py", line 53, in get_master_volume
        amixer_stdout = proc.communicate()[0].split('\n')[4]
    IndexError: list index out of range
    

    This means that either the [0] access tries to access an empty sequence (unlikely), or that [4] tries to access a sequence which has a maximum of 4 entries, so that [4] does not exist.

    Obviously, the output has less than 4 lines, so proc.communicate()[0].split('\n') has no [4] entry.

    Either the program output doesn't match the expectations, or something else is wrong.

  2. proc.communicate

    proc.communicate:'C:\Program' is not recognized as an internal or external comma
    

    nd, operable program or batch file. ('', None)

    This seems to indicate that there is something wrong with your call's command line.

    config.get('main', input1) seems to contain the full path, which lies in C:\Program Files. So you should surround your program path properly with " (at the right level).

  3. I/O on closed file

    This third problem can be resolved by inspecting the call trace. Here we can see that there is something wrong with the way things are called: the communicate() call fails, as the stdout of the subprocess is already closed.

    It is unclear to me why this is the case; maybe you are calling communicate() twice? On the first call, it is closed, and as you attempt a second call, it fails as stdout has already been read out.

Upvotes: 1

zmo
zmo

Reputation: 24812

uuuuh… I just looked at the other question you're using and there's something you're totally missing. You're trying to use parameters sent to the linux program amixer with your windows program bsplayer.exe that has nothing related with the former. It is very unlikely to work! And it is the reason why it does not work. cf the other question:

proc = subprocess.Popen('/usr/bin/amixer sget Master', shell=True, stdout=subprocess.PIPE)

So the first part of the question is solving your problem in calling bsplayer.exe, but what you actually want it to do does not work.


you should try the following to call the subprocess:

proc = subprocess.Popen([config.get('main', input1), 'sget', 'Master'], stdout=subprocess.PIPE)

because, though I don't have much experience with windows' python, I think that calling it with the shell might not work as expected, try to interpret your path as arguments. Though maybe if you put quotes around the argument it may work:

proc = subprocess.Popen('"%s" sget Master '%config.get('main', input1), shell=True,    stdout=subprocess.PIPE)

what led me to that conclusion is that:

'C:\Program' is not recognized as an internal or external command,

usually means that the path to bsplayer.exe is being cut at the first space.


edit:

Could you run the following, and add the output to your question?

Basically:

  • I moved the proc.wait() before the communicate
  • moved the selection of the fourth line of output in a second step
  • added a print out of the list of the output

what I want to know is whether the error you're having is because of communicate() not working, or because you get less than four lines in the output.

So the following version is solving your calling problem:

def get_master_volume():
    config.read('%s/config.cfg'%os.path.abspath(os.path.dirname(__file__)))
    p = [config.get('main', input1), 'sget', 'Master']
    print("PROG:", p)
    proc = subprocess.Popen(p, stdout=subprocess.PIPE)

    proc.wait()

    amixer_stdout = proc.communicate()[0].split('\n')
    print("OUTPUT", amixer_stdout)
    amixer_stdout = amixer_stdout[4]

    find_start = amixer_stdout.find('[') + 1
    find_end = amixer_stdout.find('%]', find_start)

    return float(amixer_stdout[find_start:find_end])

though it outputs nothing.

Does your command actually work?! Did you actually try to do:

"C:\Program Files (x86)\Webteh\BSPlayer\bsplayer.exe" sget Master

in cmd to see if it works?

Upvotes: 2

Related Questions