HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Locating a PID based on an IP address and port

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
addresslocatingandbasedportpid

Problem

I wrote a simple Python app that is given an IP address and port and the app then finds the PID listening on that IP:PORT.

I would like to hear constructive criticism regarding the bellow code.

import argparse
import sys
import socket
import subprocess
import os

def is_legal_ip(address):
    ip = address.split(":")
    pieces = ip[0].split('.')
    if len(pieces) != 4: 
        return False
    try: 
        return all(0 2 or len(sys.argv) == 1:
        print("Please provide a single argument.")
        exit(1)

    parser = argparse.ArgumentParser("Returns a PID listenning on IP:PORT")
    parser.add_argument('ip_port', metavar='IP:PORT', type=str, nargs=1, help='local address')
    args = parser.parse_args()

    if (is_legal_ip(args.ip_port[0]) == False):
        print("Invalid IP address.")
        exit(1)

    return(args.ip_port[0])

def address_to_pid(ip_port):
    devnull = open(os.devnull, 'w')
    output = subprocess.check_output("netstat -nptl".split(), stderr = devnull).splitlines()
    devnull.close()

    for line in output[2::]:
        raw_line = line.split()
        if raw_line[3] == ip_port:
            if raw_line[6] == "-": 
                return "No PID listening on address"
            else:
                return raw_line[6].split(r"/")[0]

def main():
    ip_port = parse_arguments()

    print address_to_pid(ip_port)

if __name__ == "__main__": main()

Solution

First off, a couple of quick tips:

  • Follow the PEP8 style guide



  • Instead of x == False, use not x, for example in if (is_legal_ip(args.ip_port[0]) == False)



  • Omit unnecessary imports (import socket in this example)



  • Use spaces around operators, for example all(0



  • Break lines after colons, for example in if __name__ == "__main__": main()



Leave the job of argument parsing to
argparse

No need to work with
sys.args yourself.
For example you can simply remove these lines:

if len(sys.argv) > 2 or len(sys.argv) == 1:
    print("Please provide a single argument.")
    exit(1)


The way you specified the ip-port arguments with
nargs=1,
argparse will take care of validating the number of command line arguments.

Simplify the argument parsing

You can omit
nargs=1.
But in that case,
the value of
args.ip_port will not be a list,
but a simple string.

You can also omit the
type=str, as that's the default.

Your usage would become simpler:

parser.add_argument('ip_port', metavar='IP:PORT', help='local address')
args = parser.parse_args()

if not is_legal_ip(args.ip_port):
    parser.error("Invalid IP address.")

return args.ip_port


Use two arguments instead of one

It's awkward that use
ip:port,
the splitting at multiple places is just noise.
It will be simpler and cleaner to use two arguments instead of one:

parser.add_argument('ip', help='local address')
parser.add_argument('port', type=int, help='local port')
args = parser.parse_args()

if not is_legal_ip(args.ip):
    parser.error("Invalid IP address.")

return args.ip, args.port


Notice that this way
argparse can take care of validating the port number.

Then in your
main function, you can get the ip address and port pair like this:

ip, port = parse_arguments()


Benefit from the error reporting features of
argparse

Instead of this:

if not is_legal_ip(args.ip_port):
    print("Invalid IP address.")
    exit(1)


I suggest to use this way:

if not is_legal_ip(args.ip_port):
    parser.error("Invalid IP address.")


argparse will print a usage help along with the error message,
and also exit.

A subtlety here is that
argparse will exit with code 2 in this case,
which is common in case of invalid parameters of UNIX commands.

Working with file handles

When working with files, use
with:

with open(os.devnull, 'w') as devnull:
    output = subprocess.check_output("netstat -nl".split(), stderr=devnull).splitlines()


This makes
devnull.close()` unnecessary, Python will call it when exiting the block.

Code Snippets

if len(sys.argv) > 2 or len(sys.argv) == 1:
    print("Please provide a single argument.")
    exit(1)
parser.add_argument('ip_port', metavar='IP:PORT', help='local address')
args = parser.parse_args()

if not is_legal_ip(args.ip_port):
    parser.error("Invalid IP address.")

return args.ip_port
parser.add_argument('ip', help='local address')
parser.add_argument('port', type=int, help='local port')
args = parser.parse_args()

if not is_legal_ip(args.ip):
    parser.error("Invalid IP address.")

return args.ip, args.port
ip, port = parse_arguments()
if not is_legal_ip(args.ip_port):
    print("Invalid IP address.")
    exit(1)

Context

StackExchange Code Review Q#98377, answer score: 9

Revisions (0)

No revisions yet.