3

I am using argument parser to allow passing port number to my program. I then try to validate the value, and one of the first tests is the is int test:

    parser = argparse.ArgumentParser(description='Provides XML-RPC API.')
    parser.add_argument('--port', dest='port', default=9666, type=int,
                       help='port to listen on, 9666 by default.');
    args = parser.parse_args()

    if args.port is not int:
        raise TypeError("Port must be integer (%s given), use -h argument to get help."%(type(args.port).__name__))
    if args.port>=65536 or args.port<=0:
        raise IndexError("Port must be lower than 65536, 65535 is the maximum port number. Also, port can only be positive, non-zero number. Use -h argument to get help.")
    if args.port<=1204:
        print("Notice: ports up to 1024 are reserved to well known services, using this port numbers often leads to conflicts. Use -h argument to get help.")

Now whatever I do, I get the error:

Port must be integer (int given), use -h argument to get help.

I mean, what the hell? Even the error says it was int, so what's going on?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778

4 Answers4

6

args.port is not int will return False if args.port was ever set to the int type:

args.port = int
args.port is int  # True

or you re-bound int to the same object args.port already is bound to:

int = args.port
args.port is int  # True

That is because is tests if two references are pointing to the exact same object.

Perhaps you were confused on how to test for the type of an object, for that you use the isinstance() function, or test the output of type():

type(args.port) is not int  # False if args.port is a different type
not isinstance(args.port, int)  # False if args.port is a different type and not a subclass either

This will never be the case either, because you configured your ArgumentParser option to only accept integers:

parser.add_argument('--port', dest='port', default=9666, type=int,
                   help='port to listen on, 9666 by default.')

The type=int argument is important, because that means that anything that cannot be interpreted as an integer will be rejected by the parser. You will never get past parser.parse_args() here:

>>> import argparse
>>> parser = argparse.ArgumentParser(description='Provides XML-RPC API.')
>>> parser.add_argument('--port', dest='port', default=9666, type=int,
...                     help='port to listen on, 9666 by default.')
_StoreAction(option_strings=['--port'], dest='port', nargs=None, const=None, default=9666, type=<type 'int'>, choices=None, help='port to listen on, 9666 by default.', metavar=None)
>>> parser.parse_args(['--port', 'not an integer'])
usage: [-h] [--port PORT]
: error: argument --port: invalid int value: 'not an integer'

That's because 'not an integer' cannot be converted by the int() function; a ValueError is raised and argparse translates that to an error message for you:

>>> int('not an integer')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'not an integer'

You could supply your own conversation function that limits the range:

def portnumber(value):
    port = int(value)  # may raise ValueError
    if not 0 <= port <= 65536:
        raise ValueError('Not a valid port number')
    return port

Then use that as the type:

parser.add_argument('--port', dest='port', default=9666, type=portnumber,
                   help='port to listen on, 9666 by default.')
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
3

In general, testing if something is an int is done by isinstance(args.port, int). However, this check is not needed here since argparse will guarantee it to be an integer for you. If you had not specified a default, None would also have been a possibility, but this is not the case here since you did specify a default value.

Gustav Larsson
  • 8,199
  • 3
  • 31
  • 51
1

'int' refers to the class itself. 'is not int' would evaluate to False if you passed the class 'int' itself, which is not likely to happen. You probably want 'isinstance( args.port, int)' instead.

aldo
  • 2,927
  • 21
  • 36
0

is not int will always return True unless you do int is not int. This is because is is an identity test, which means that it checks whether the two arguments are the same object.

Let's say you try

>>> 1 is not int

This will return True because 1 is not the same object as int. We can check this more deeply.

>>> type(1)
<class 'int'>

As you can see 1 is indeed an int, but int itself is actually as class.

To fix this, you can just do

not isinstance(args.port, int)

as suggested by other answers.

You could also do

type(args.port) is not int

which checks if the type of args.port is int.

michaelpri
  • 3,521
  • 4
  • 30
  • 46