2

Is it possible to differentiate between arguments based on a regex with argparse?

I would like my script to accept either an IP address or a network address, but need to be able to differentiate between the two because the backend API call requires me to specify which is used. I'd preferably not use an explicit flag to specify which argument is used, instead using a regex, but not sure this is possible?

Example usage:

ip-lookup.py 192.168.1.1

192.168.1.1 should be assigned to an argument named 'ip'

ip-lookup.py 192.168.0.0/16
ip-lookup.py 192.168.0.0 255.255.0.0
ip-lookup.py 192.168.0.0/255.255.0.0

192.168.0.0/16 should be assigned to an argument named 'network'

Code:

parser = argparse.ArgumentParser(description='Lookup an IP address or network in the backend IPAM system.')
parser.add_argument('ip', help='IP host address', type=str, required=False, **regex to match on IP address**)
parser.add_argument('network', help='IP network address', type=str, required=False, **regex to match on IP network**)

args = parser.parse_args()

args.ip         -> should only exist if the user entered an IP address as parameter
args.network    -> should only exist if the user entered an IP network as parameter
5
  • The only difference is the slash for network? Why is there a space here ip-lookup.py 192.168.0.0 255.255.0.0? Commented Dec 27, 2018 at 13:07
  • A slash or space preferably. Creating the regex itself is not a problem, the problem is how to assign arguments to an argument name based on a regex. Commented Dec 27, 2018 at 13:09
  • Please show your attempt so it is clearer what you ask for. Commented Dec 27, 2018 at 13:10
  • 1
    Another current [argparse] question attempts something similar - stackoverflow.com/questions/53944971/…. positional arguments are assigned strictly on position. It does not do 'value' matching. That's something you can do after parsing with your own code. Commented Dec 27, 2018 at 17:40
  • Your used of required=False will raise an error. Commented Dec 27, 2018 at 17:43

2 Answers 2

2

I would approach this by defining the arguments non-positional (-- in front of them when at the definition). For example

import argparse
parser = argparse.ArgumentParser()

parser.add_argument('--ip', help='IP host address', type=str, required=False)
parser.add_argument('--network', help='IP network address', type=str, required=False)

args = parser.parse_args()

print(getattr(args, 'ip'))
print(getattr(args, 'network'))

Now you can call this with test.py --ip 192.168.1.1 or test.py --network 192.168.0.0/255.255.0.0. This would return respectively:

192.168.1.1
None

or

None
192.168.1.1/255.255.0.0

Note that --network '192.168.1.1 255.255.0.0' would be required if the string contains a white space.

Sign up to request clarification or add additional context in comments.

2 Comments

Note if you prefer space the user would could enclose the value in quotes: --network '192.168.1.1 255.255.0.0'
Thanks b-fg and Micah, I will do it this way if the option with the regex is not feasible.
0

For anyone interested, I also looked at some alternatives to argparse such as Click, but since using arguments in this way is not following the unix way of using positional arguments, it doesn't seem to be built-in.

I did manage to get it to work, parsing the sys.argv directly. Not very elegant, but does work as expected:

import sys
import ipaddress

USAGE = """
 Usage:
    \t ip-lookup.py 192.168.1.1
    \t ip-lookup.py 192.168.0.0/16
    \t ip-lookup.py 192.168.0.0 255.255.0.0
    \t ip-lookup.py 192.168.0.0/255.255.0.0    
    """

# Parse command-line arguments
if (len(sys.argv) < 2):
    print('\n ### Please specify an IP address or subnet as arguments. ###')
    print(USAGE)
elif (len(sys.argv) == 2 and '/' in sys.argv[1]): # x.x.x.x/xx
        try:
            prefix = ipaddress.IPv4Network(sys.argv[1])
            urlparams = 'network?network='
        except:
            print('### Error: not a valid network address ###')
elif (len(sys.argv) == 2 and '/' not in sys.argv[1]): # x.x.x.x
        try:
            ip = ipaddress.IPv4Address(sys.argv[1])
            urlparams = 'ipv4address?status=USED&ip_address='
        except:
            print('### Error: not a valid IP address ###')
elif (len(sys.argv) == 3): # x.x.x.x x.x.x.x
    if(sys.argv[2].startswith('255.')):
        try:
            prefix = ipaddress.IPv4Network(sys.argv[1] + '/' + sys.argv[2])
            urlparams = 'network?network='
        except:
            print('### Error: not a valid network address ###')        
else:
    print('\n ### Error: too many arguments given. ###')
    print(USAGE)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.