Search code examples
pythonwindowstcpsubprocessnsq

getaddrinfow fails if running process from Python


I try to run 3rd party process (nsqd.exe) from my python script but when I do nsqd fails to bind socket. I've no idea why.

The script I'm using:

import subprocess
import sys

proc = subprocess.Popen(['nsqd.exe', '-tcp-address="127.0.0.1:{}"'.format(sys.argv[1]),
    '-http-address="127.0.0.1:{}"'.format(sys.argv[2])])

print("the commandline is {}".format(proc.args))
proc.wait()
sys.exit(proc.returncode)

And the output:

D:\bsm.tar\bsm\final\nsqd>python nsqd.py 4150 4151
the commandline is ['nsqd.exe', '-tcp-address="127.0.0.1:4150"', '-http-address="127.0.0.1:4151"']
[nsqd] 2016/09/26 21:41:51.974681 nsqd v0.3.8 (built w/go1.6.2)
[nsqd] 2016/09/26 21:41:51.975681 ID: 864
[nsqd] 2016/09/26 21:41:51.979675 NSQ: persisting topic/channel metadata to nsqd.864.dat
[nsqd] 2016/09/26 21:41:52.004711 FATAL: listen ("127.0.0.1:4150") failed - listen tcp: lookup "127.0.0.1: getaddrinfow: No such host is known.

If I run that by myself directly everything works fine:

D:\bsm.tar\bsm\final\nsqd>nsqd.exe -tcp-address="127.0.0.1:4150" -http-address="127.0.0.1:4151"
[nsqd] 2016/09/26 21:42:20.093848 nsqd v0.3.8 (built w/go1.6.2)
[nsqd] 2016/09/26 21:42:20.094850 ID: 864
[nsqd] 2016/09/26 21:42:20.095851 NSQ: persisting topic/channel metadata to nsqd.864.dat
[nsqd] 2016/09/26 21:42:20.127984 TCP: listening on 127.0.0.1:4150
[nsqd] 2016/09/26 21:42:20.127984 HTTP: listening on 127.0.0.1:4151
[nsqd] 2016/09/26 21:42:22.111580 NSQ: persisting topic/channel metadata to nsqd.864.dat
[nsqd] 2016/09/26 21:42:22.111580 TCP: closing 127.0.0.1:4150
[nsqd] 2016/09/26 21:42:22.112553 HTTP: closing 127.0.0.1:4151
[nsqd] 2016/09/26 21:42:22.135635 NSQ: closing topics
[nsqd] 2016/09/26 21:42:22.135635 QUEUESCAN: closing
[nsqd] 2016/09/26 21:42:22.135635 LOOKUP: closing
[nsqd] 2016/09/26 21:42:22.135635 ID: closing

D:\bsm.tar\bsm\final\nsqd>

Maybe somebody have idea what is wrong?

Win10, python352. Running as admin does not help.

Thanks.


Solution

  • Remove the double quotes in your Popen so it becomes:

    proc = subprocess.Popen(['nsqd.exe', 
                             '-tcp-address=127.0.0.1:{}'.format(sys.argv[1]), 
                             '-http-address=127.0.0.1:{}'.format(sys.argv[2])
                           ])
    

    Before passing your command to CreateProcess, Python converts the list to a string using subprocess.list2cmdline:

    >>> subprocess.list2cmdline(['nsqd.exe', '-tcp-address="127.0.0.1:4150"', '-http-address="127.0.0.1:4151"'])
    'nsqd.exe -tcp-address=\\"127.0.0.1:1234\\" -http-address=\\"127.0.0.1:1234\\"
    

    nsqd.exe thinks "127.0.0.1 is the hostname - hence the failed lookup.

    Additional Information

    The reason the double quotes work on the command line is that they have special meaning when a function such as CommandLineToArgvW is used to split the command line into individual arguments: normally arguments are delimited by whitespace, but when a quoted string is encountered, the quotes are stripped and the entire string becomes one argument.

    This is also why Python is \-escaping the quotes: it expects the resultant line to be parsed in the above manner.

    If you pass Popen a string rather than a list, list2cmdline will not be called and you should get the same results as removing the double quotes (i.e. it will be like running it from the command line):

    proc = subprocess.Popen('nsqd.exe "-tcp-address=127.0.0.1:{}" '
                            '"-http-address=127.0.0.1:{}"'
                            .format(sys.argv[1], sys.argv[2]))
    

    You can see this illustrated in the following (perhaps contrived) example:

    import subprocess
    subprocess.Popen('c:\python27\python.exe "--version"')
    subprocess.Popen(['c:\python27\python.exe', '"--version"'])
    

    The first Popen will print the python version. The second will look for a file named "--version": can't open file '"--version"': [Errno 22] Invalid argument