I have a script with several functions (before that I had one script per function)
Each function will require different arguments (some positional/mandatory, some optional).
I'd like to be able to call a specific function within the script and then hand over the argparse arguments.
Commandline:
python3 getSites.py getSites host token
I thought that using
if __name__ == '__main__':
globals()[sys.argv[1]]()
to use sys.argv[1] as the choice for the fuction and then within the function use:
def getSites():
parser = argparse.ArgumentParser(description="SentinelOne Site Statistics (#mianly for licnese usage")
parser.add_argument('host', type=str, help='REQUIRED: Please enter the Host (i.e. everything after "https://" and before ".domain.com"')
parser.add_argument('token', type=str, help='REQUIRED: Please enter your token (without the string "ApiToken")')
parser.add_argument('-s', '--savepath', type=str,
help='OPTIONAL: full path of the directory where to write the resulting XLS file. Defaults to directory of the script itself')
parser.add_argument('-a', '--accountid', type=str,
help='OPTIONAL: Enter the AccountID to query. Defaults to all accounts / sites the token user has access to')
f_args = parser.parse_args()
would work but when entering e.g.
python3 getSites.py getSites firsthost 98456984652984652984652985
I get
getSites.py: error: unrecognized arguments: 98456984652984652984652985
My Questions:
Is it possible to archive this without the "overcomplicated" -c switch when calling the script?
should I rather go with moving the required arguments from argparse into the function? i.e. getsites(host, token) and then use argparse only for the optional arguments?
Benefit of the second method would be that I could also call the function from within a python script, not only from command line. On the other hand: the user would need to know exactly what they have to provide, as that won't be shown when runnign the script with -h
I'm a bit confused here and would appreciate and help for a newbie.
Related
So I'm trying to get my program to do take the command line arguments and use it in my script. I read argparse and the optparse documentation and I'm still lost.
What I'm trying to do is have my code execute this on the command line:
./program <-p port> <-s> [required1] [required2]
The -p is optional, and I want to make the port a variable in my script, like so:
server_address = ('some server name', **port**)
I thought that that's what store and dest would do... as in store would take the port argument and dest would be the variable name and I could call it like program.port. It doesn't work this way, however, and I can't find or decipher explanations for what exactly store and dest do.
I'm new to Python, so this might not be a well-formed question.
so, following the documentation:
You create a parser
import argparse
parser = argparse.ArgumentParser(description='Some helpful text about what your function does')
You add arguments, optional ones have '-'s before hand, see below
parser.add_argument('-p', '--port', type=int, default=0, help='port')
parser.add_argument('-s', help='I don\'t know what this is')
parser.add_argument('required_1') # Note the lack of dashes
parser.add_argument('required_2')
You need to parse the arguments with a function call
args = parser.parse_args()
This creates a namespace object which you can then access your variables from, see below
port = args.port
or
port = vars(args)['port']
req1 = args.required_1
req2 = args.required_2
etc...
For more information on namespace objects, checkout this question
Hopefully that helps.
Scenario: I have a python script that receives as inputs 2 directory paths (input and output folders) and a variable ID. With these, it performs a data gathering procedure from xlsx and xlsm macros, modifies the data and saves to a csv (from the input folder, the inner functions of the code will run loops, to get multiple files and process them, one at a time).
Issue: Since the code was working fine when I was running it from the Spyder console, I decided to step it up and learn about cmd caller, argparse and the main function. I trying to implement that, but I get the following error:
Unrecognized arguments (the output path I pass from cmd)
Question: Any ideas on what I am doing wrong?
Obs: If the full script is required, I can post it here, but since it works when run from Spyder, I believe the error is in my argparse function.
Code (argparse function and __main__):
# This is a function to parse arguments:
def parserfunc():
import argparse
parser = argparse.ArgumentParser(description='Process Files')
parser.add_argument('strings', nargs=3)
args = parser.parse_args()
arguments = args.strings
return arguments
# This is the main caller
def main():
arguments = parserfunc()
# this next function is where I do the processing for the files, based on the paths and id provided):
modifierfunc(arguments[0], arguments[1], arguments[2])
#
if __name__ == "__main__":
main()
If you decided to use argparse, then make use of named arguments, not indexed. Following is an example code:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('input')
parser.add_argument('output')
parser.add_argument('id')
args = parser.parse_args()
print(args.input, args.output, args.id) # this is how you use them
In case you miss one of them on program launch, you will get human readable error message like
error: the following arguments are required: id
You could drop the entire parserfunc() function.
sys.argv does indeed contain all arguments (always processed as a string) as mentioned by grapes.
So instead of this:
modifierfunc(arguments[0], arguments[1], arguments[2])
This should suffice:
import sys
modifierfunc(sys.argv[0], sys.argv[1], sys.argv[2])
Perhaps, first do a print, to see if the sys.argv holds the values you expect.
print('Argument 0='+sys.argv[0])
print('Argument 1='+sys.argv[1])
print('Argument 2='+sys.argv[2])
I have a lot of arguments for my script. And along with the argparser, I want users to also have the option to specify those arguments via a config file.
parser.add_argument('-a','--config_file_name' ...required=False)
parser.add_argument('-b' ...required=True)
parser.add_argument('-c' ...required=False)
....
At this point I just need the logic to implement the following:
Either the users can type in all the arguments in the command line or
They can type in the first argument, specify the file name and the code fills in/overwrites all the remaining optional arguments from the config file.
How can this be achieved?
I don't think this is up to argparse to handle.
Argparse simple needs to check if the argument for the config file is there and pass it on to your program.
You need to handle this in your program, which would mean doing something like:
...
arguments=parser.parse_args()
if len(arguments.config_file_name):
f=fopen(arguments.config_file_name,'rb')
conf_settings = f.read()
for line in conf_settings:
#parse your config format here.
this way, if the config_file_name is set, you will overwrite any possible given arguments, and if not, the program will be executed with the arguments specified by the user.
for example:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("a")
args = parser.parse_args()
if args.a:
#We're getting config from config file here...
else:
#We're getting config from the command line arguments
#Now we can call all functions with correct configuration applied.
One way to solve this problem is with xargs command line utility. This wouldn't give you exactly the command line interface that you describe, but would achieve the same effect.
xargs will run a command with arguments that it has read from standard input. If you pipe your arguments from the config file into xargs then your python program will be called with the arguments stored in the file. For example:
config.txt
-a -b --input "/some/file/path" --output "/another"
You could then use these arguments to your program like so
xargs ./my-prog.py --optional-arg < config.txt
because of the way that argparse works, it will take the value of the last argument if duplicates are found. Thus, if you have duplicate arguments, the ones from the config file will be chosen.
I am writing a python script.
It takes two arguments, a file, and the optional argument of a set of rules. The rules need to be formatted as a dictionary.
I am new to argparse and want to make this command line friendly.
If I leave the optional argument out and just type in the file, the script runs perfectly. If I add in a test dictionairy, it returns --
har_parser.py: error: unrecognized arguments:
I am not sure if it my misuse of the command line, which would be an easy fix if I need to change how I am passing in arguments.
Currently I am running the script as so...:
$ python myScript.py arg1 arg2
The other more likely scenario is that I wrote my function incorrectly, due to my novice experience with argparse.
Any direction would be appreciated.
def main():
parser = argparse.ArgumentParser()
parser.add_argument("file", nargs=1)
parser.add_argument("--rules")
args = parser.parse_args()
if args.rules:
print(parseHar(str(args.file[0]), args.rules[0]))
else:
print(parseHar(str(args.file[0])))
parser = argparse.ArgumentParser()
parser.add_argument('-model', type=str, default='linear_model')
parser.add_argument('-featuredim', type=int, default=20)
parser.add_argument('-inputfeatures', type=str, default='/Users/myname/Downloads/face-rating-master/data/features_ALL.txt')
parser.add_argument('-labels', type=str, default='/Users/myname/Downloads/face-rating-master/data/ratings.txt')
The above code can be run successfully. The following line
args = parser.parse_args()
gives this error:
usage: ipykernel_launcher.py [-h] [-model MODEL] [-featuredim FEATUREDIM]
[-inputfeatures INPUTFEATURES] [-labels LABELS]
ipykernel_launcher.py: error: argument -featuredim: invalid int value: '/Users/myname/Library/Jupyter/runtime/kernel-7d72fc3c-2c11-47e4-87f3-3587b2461a52.json'
An exception has occurred, use %tb to see the full traceback.
Code from https://github.com/avisingh599/face-rating
try:
args = parser.parse_args() #call from command line
except:
args = parser.parse_args(args=[]) #call from notebook
The featuredim argument expects an integer (i.e., type=int) but you've passed in a string that indicates the path to a json file.
You could get this script to work by re-engineering its logic a bit so that it would read in a JSON and then extract the integer from a key value (similar to how many scripts will read in .ini configuration files), but within the context of what you're doing that's overkill (and really wouldn't help much with your dilemma, since you want to use Jupyter). For reference, the file that you're passing into the script doesn't give you direct access to the variables defined in your notebook, and just stores information used by the application (such as the IP address that the notebook is running on and the TCP port number that it is using). To illustrate, this is what a similar JSON file on my laptop looks like:
09/28 18:26:53 [jsp2205#kaheta: ~/Library/Jupyter/runtime]
$ less kernel-40dad791-ffa1-4687-bcd1-3ec831884c83.json
{
"stdin_port": 60476,
"ip": "127.0.0.1",
"control_port": 60477,
"hb_port": 60478,
"signature_scheme": "hmac-sha256",
"key": "b52ef99c-0a1a70b17b600daea5263a29",
"kernel_name": "",
"shell_port": 60474,
"transport": "tcp",
"iopub_port": 60475
}
Your best bet is to take the code from trainModel.py, remove ArgumentParser, and replace any references to the arguments with hardcoded variables. Then add each of the lines in the modified code to your Jupyter notebook and modify the hardcoded variables manually within the notebook.
For other scripts using ArgumentParser, you might want to see if they pass the arguments extracted using parse_args() into a function, and then use an import statement similar to the following to import that function directly into your notebook. So something like the following:
cd [directory containing script]
from [script name minus the .py extension] import [function name]