Billal.Teiba
Billal.Teiba

Reputation: 63

Argparse error trying to do simple UDP server client

I've been trying this code and getting this message from the console:

usage: Experimental.py [-h] [-p PORT] {client,server}
Experimental.py: error: the following arguments are required: role

I can't decide what's wrong, this is the code:

    import argparse, socket
    from datetime import datetime
    MAX_BYTES = 65535
    def server(port):
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.bind(('127.0.0.1', port))
        print('Listening at {}'.format(sock.getsockname()))
        while True:
            data, address = sock.recvfrom(MAX_BYTES)
            text = data.decode('ascii')
            print('The client at {} says {!r}'.format(address, text))
            text = 'Your data was {} bytes long'.format(len(data))
            data = text.encode('ascii')
            sock.sendto(data, address)
    def client(port):
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        text = 'The time is {}'.format(datetime.now())
        data = text.encode('ascii')
        sock.sendto(data, ('127.0.0.1', port))
        print('The OS assigned me the address{}'.format(sock.getsockname()))
        data, address = sock.recvfrom(MAX_BYTES) # Danger!
        text = data.decode('ascii')
        print('The server {} replied {!r}'.format(address, text))


if __name__ == '__main__':
    choices = {'client': client, 'server': server}
    parser = argparse.ArgumentParser(description='Send and receive UDP locally')
    parser.add_argument('role', choices=choices, help='which role to play')
    parser.add_argument('-p', metavar='PORT', type=int, default=1060, help='UDP port (default 1060)')
    args = parser.parse_args()
    function = choices[args.role]
    function(args.p)

Upvotes: 1

Views: 771

Answers (1)

Austin Adams
Austin Adams

Reputation: 6554

The program is using the argparse module's ArgumentParser class, which assumes by default that positional arguments are required, so you can't omit them as you have. The argparse tutorial offers a good definition of a positional argument:

[A positional argument is] named so because the program should know what to do with the value, solely based on where it appears on the command line. This concept is more relevant to a command like cp, whose most basic usage is cp SRC DEST. The first position is what you want copied, and the second position is where you want it copied to.

In this case, when calling Experimental.py, you've left out the positional argument named role, which is defined near the end of your program:

parser.add_argument('role', choices=choices, help='which role to play')

(Because the first argument ('role') doesn't start with a - (like -p), add_argument registers it as a positional argument.)

The choices keyword argument to add_argument requires an iterable containing the possible values to supply, so the author of the program passes choices, a dictionary defined as:

choices = {'client': client, 'server': server}

This is actually pretty clever; iterating over a dictionary yields the keys, so argparse allows client and server as values for role. So, to fix this error, you should pass either client or server as an argument to the program, thereby supplying a value for role. Even better, you can check out the argparse tutorial. (I hate telling people to read the manual, but I think it might be helpful)

tl;dr: instead of:

$ python3 Experimental.py

you should try one of the following:

$ python3 Experimental.py server
$ python3 Experimental.py client

Upvotes: 2

Related Questions