Reputation: 3163
I have a .py file with a single function that basically converts a .csv file to a .pkl file. This file, when invoked from the terminal, is supposed to convert the measurements between imperial and metrics depending on whether the input is true or not. For example
python3 file.py imperial_units=True
or python3 file.py imperial_units=False
and if there are no arguments provided like python3 file.py
, imperial units should just default to True:
This is my attempt:
import json, sys
import pandas as pd
def log_to_pickle(units):
...
if units == True: # If units == True, convert all metric to imperial
print('Imperial True')
if imperial_unit == False:
...
elif units == False: # If units == False, convert all imperial to metric
print('Imperial False')
if imperial_unit == True:
...
...
if __name__ == "__main__":
if sys.argv == True or sys.argv == '':
log_to_pickle(True)
else:
log_to_pickle(False)
I've added the print statements inside the if/elif block to see whether my inputs work. I ran python3 file.py imperial_units=True
and the output was 'Imperial False'
What am I doing wrong?
Upvotes: 3
Views: 918
Reputation: 77337
argparse
results in a more user-friendly program with help text and uses an arguably more standard method of dash-delimited parameters. Since you only want one thing, the unit of measure, I think it is best implemented with a choice flag that includes a default, but you could make a single argument such as --use-metric
that defaults to False
.
#!/usr/bin/env python3
import argparse
parser = argparse.ArgumentParser(description='CSV to Pickle with unit conversion to metric')
parser.add_argument('-u', '--unit', default='imperial', choices=['imperial', 'metric'],
help='Source unit type (imperial or metric)')
parser.add_argument('from_file', help="CSV file to convert")
parser.add_argument('to_file', help="Converted Pickle file")
args = parser.parse_args()
print(args.unit)
Options for using the program are
$ ./test.py --help
usage: test.py [-h] [-u {imperial,metric}] from_file to_file
CSV to Pickle with unit conversion to metric
positional arguments:
from_file CSV file to convert
to_file Converted Pickle file
optional arguments:
-h, --help show this help message and exit
-u {imperial,metric}, --unit {imperial,metric}
Source unit type (imperial or metric)
$ ./test.py --unit metric aaa.csv aaa.pkl
metric
$ ./test.py -u imperial aaa.csv aaa.plk
imperial
$ ./test.py aaa.csv aaa.pkl
imperial
$ ./test.py --unit=other aaa.csv aaa.pkl
usage: test.py [-h] [-u {imperial,metric}] from_file to_file
test.py: error: argument -u/--unit: invalid choice: 'other' (choose from 'imperial', 'metric')
Upvotes: 1
Reputation: 3504
There are a few wrong things here but the root cause is that the code doesn't use sys.argv
correctly. Try printing sys.argv
:
if __name__ == "__main__":
import sys
print(sys.argv)
$ python3 argv.py imperial_units=True ['argv.py', 'imperial_units=True']
You need to parse the command line arguments.
if __name__ == "__main__":
if len(sys.argv) <= 1 or sys.argv[1] == "imperial_units=True":
log_to_pickle(True)
else:
log_to_pickle(False)
Take a look at the argparse
package for more robust command line argument handling.
Upvotes: 4
Reputation: 23614
I would use argparse for this type of thing. Use a boolean flag argument. I also like to provide a positive and a negative version for ease of use.
import argparse
...
if __name__ == "__main__":
parser = argparse.ArgumentParser(prog='log-to-pickle')
parser.add_argument('--imperial-units', dest='imperial_units', action='store_true')
parser.add_argument('--no-imperial-units', dest='imperial_units', action='store_false')
parser.set_defaults(imperial_units=True)
args = parser.parse_args()
log_to_pickle(args.imperial_units)
Using argparse you'll get some nice help messages too:
$ python3 file.py -h
usage: log-to-pickle [-h] [--imperial-units] [--no-imperial-units]
optional arguments:
-h, --help show this help message and exit
--imperial-units
--no-imperial-units
Now you can call the application like this:
# These are the same given the default.
$ python3 file.py
$ python3 file.py --imperial-units
Or to do the negative version:
$ python3 file.py --no-imperial-units
If you really want to provide a string argument to the bool flag, then I would make a custom method to transform string to boolean:
import argparse
def bool_arg(val):
return val.lower() in ('y', 'yes', 't', 'true', 'on', '1')
if __name__ == "__main__":
parser = argparse.ArgumentParser(prog='log-to-pickle')
parser.add_argument('--imperial-units', type=bool_arg, default=True)
args = parser.parse_args()
print(args.imperial_units)
Examples:
$ python3 file.py --imperial-units y
True
$ python3 file.py --imperial-units True
True
$ python3 file.py --imperial-units 0
False
Upvotes: 4
Reputation: 3942
sys.argv
provides a list of arguments. You can iterate through it to find an argument that contains "imperial_units". You can then process this to get the boolean.
if __name__ == "__main__":
#Find arguments which contain imperial_units
arg = [x.split("=")[1] for x in sys.argv if "imperial_units" in x]
#If the length is 0, no args were found, default to true
#Otherwise use value after = and convert it to bool
arg = True if len(arg) == 0 else bool(arg[0])
log_to_pickle(arg)
Upvotes: 1