limitededition43
limitededition43

Reputation: 1

getopt: How to enforce two options to be present?

I am trying to make two options mandatory. either both -l and -p should be there or -t and -p should be there.

opts, args = getopt.getopt(sys.argv[1:],":lt:p:c:", "listen","target","port","command"])
    
for o,a in opts:
    if o in ("-l"):
        print("Starting Listener on 0.0.0.0")
    
    elif o in ("-t"):
        if o in ("ip"):
            print("Connecting")
    
    else:
        print("Else",(o))

Upvotes: 0

Views: 489

Answers (1)

Paul P
Paul P

Reputation: 3927

A few notes:

  • Unless you have a good reason, I would suggest using Python's argparse. It is part of the standard library, it's flexible, simple to use and to extend, comes with nice features (like --help), and many people know it.

  • "Mandatory options" are generally discouraged from. Therefore, my suggestion would be to use one positional argument that needs to have a certain format.

For example:

import argparse
import ipaddress

# Assuming this is about an IP address and a port.
CONN_HELP = "Connection string must have the format IP_ADDRESS:PORT."

def valid_conn_str(conn):
    """Validate the connection string passed as a positional argument"""

    # If the colon is missing, the connection string is malformatted.
    if ":" not in conn:
        raise argparse.ArgumentTypeError(CONN_HELP)

    parts = conn.split(":")

    # If there are two or more colons,
    # the connection string is malformatted.
    if len(parts) > 2:
        raise argparse.ArgumentTypeError(CONN_HELP)

    # If the port part of the connection string is not an integer,
    # the connection string is malformatted.
    try:
        port = int(parts[1])
    except ValueError:
        raise argparse.ArgumentTypeError(CONN_HELP + " PORT must be an integer.")

    # If the port number is larger than 65535,
    # the connection string is malformatted
    if port > 65535:
        raise argparse.ArgumentTypeError(CONN_HELP + " PORT must be < 65535.")

    # You could add similar checks in order to validate the IP address
    # or whatever else you are expecting as the first part of the
    # connection string.
    #
    # If it is indeed an IP address, you could use the
    # ipaddress module for that, e.g.:

    try:
        ip_addr = ipaddress.ip_address(parts[0])
    except ValueError:
        raise argparse.ArgumentTypeError(CONN_HELP + " Invalid IP address.")

    # When all checks have passed, return the values.
    return parts[0], port


parser = argparse.ArgumentParser()
parser.add_argument(
    "conn",
    metavar="CONN_STRING",
    # Use the validator function to check the format.
    type=valid_conn_str,
    help=CONN_HELP,
)

# args contains all arguments that have been passed in.
args = parser.parse_args()

# The validation function returns two values,
# therefore connection string argument is a tuple.
target = args.conn[0]
port = args.conn[1]

print(target)
print(port)

When someone calls the script now, with for example:

$ my_script.py hello:world

they will see

usage: my_script.py [-h] CONN_STRING
my_script.py: error: argument CONN_STRING: Connection string must have the format IP_ADDRESS:PORT. PORT must be an integer.

Running it with a valid port but invalid IP address

$ my_script.py hello:123

will give:

usage: my_script.py [-h] CONN_STRING
my_script.py: error: argument CONN_STRING: Connection string must have the format IP_ADDRESS:PORT. Invalid IP address.

Running with valid IP address and port

$ my_script.py 123.123.123:123

will print

123.123.123.123
123

Upvotes: 1

Related Questions