How to pass filepath as command line arugement in argparse python - python

I need to pass like
Python test.py -H host -U usr -P pass -L c:\newfolder\sample.sh -R /shell/sample.sh
I got error message too many values to unpack
Please help me to achieve this.
def check_argv(args=None)
parser = argparse.argumentParser()
parser.add_arugument('-H' , 'host')
#Same for all argument
#---------------------------------
Host, pass, user , local , remote = check_arg(sys.argv[1:])

You're probably getting the error because you are collecting less values than your function tuple is returning.That is in line.
And the correct way to use argparser function:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-H', action="store", dest="host")
parser.add_argument('-U', action="store", dest="usr")
params = parser.parse_args()
And in your code you can get your parameters like this
# FETCHING ARGUMENT DETAILS
print(params.host, params.usr)
OUTPUT:
$python test.py -H hostname -U testuser
>>>hostname testuser
You can continue adding as many values as needed and get them back in the same way.

Related

How to set optional arguments to positional arguments in Python's Argparse?

I have the following code:
# Get parsed arguments
args = argparse.ArgumentParser(description=Messages().Get(112))
# Get the arguments for sinit
args.add_argument('init', help=Messages().Get(100), action="store_true")
args.add_argument('--url', default=None, help=Messages().Get(101))
# Get the arguments for schema import
args.add_argument('schema-import', help=Messages().Get(104), action="store_true")
args.add_argument('--file', default=None, help=Messages().Get(104))
The --url argument should only be used with init. For example: script.py schema-import --url should not be accepted but script.py schema-import --file should.
How to set arguments as child arguments?
As mentioned there might be a way to do this with argparse, I'm not sure, but in any event I find it more transparent to explicitly handle argument dependencies in application logic. This should achieve what I think you want:
import argparse
import sys
args = argparse.ArgumentParser(description="please only use the '--url' argument if you also use the 'init' argument")
# Going to use aliases here it's more conventional. So user can use, eg,
# -i or --init for the first argument.
args.add_argument('-i', '--init', help='init help', action="store_true")
args.add_argument('-u', '--url', default=None, help='init help')
args.add_argument('-s', '--schema-import', help='schema-import help', action="store_true")
args.add_argument('-f', '--file', help='file help')
def main():
arguments = args.parse_args()
if arguments.url and not arguments.init:
# You can log an output or raise an exception if you want
# But most likely a print statment is most appropriate
# Since you are interacting with the CLI.
print("You can only use the URL option with init. Exiting")
sys.exit(0)
print("gaurd clauses passed. Here is my code...")
...
if __name__ == "__main__":
main()
Test results (my file called temp.py):
$python temp.py -u https://www.google.com
You can only use the URL option with init. Exiting
$
$python temp.py -i -u https://www.google.com
Gaurd clauses passed. Here is my code...
Why bother with doing all the logic when you can let argparse do all the work for you?
Simply use Sub-commands to define different "branches" of execution:
args = argparse.ArgumentParser(description=Messages().Get(112))
subparsers = args.add_subparsers()
parser_init = subparsers.add_parser('init', help=Messages().Get(100))
parser_init.add_argument('--url', default=None, help=Messages().Get(101))
parser_schema = subparsers.add_parser('schema-import', help=Messages().Get(104))
parser_schema.add_argument('--file', default=None, help=Messages().Get(104))
And this will give you what you want without any logic added:
>>> print(args.parse_args(['schema-import', '--url', "some.url"]))
usage: args.py [-h] {init,schema-import} ...
args.py: error: unrecognized arguments: --url some.url
>>> print(args.parse_args(['schema-import', '--file', "some.file"]))
Namespace(file='some.file')

"Invalid choice" error while using argparse

I'm writing a python script which takes in arguments from command line in the following format:
./myfile subcommand --change --host=myhost --user=whatever
--change is in a way required, meaning that for now this is only option available, but later I will add other options, but eventually user needs to choose between those options.
--host is required. It's not a positional argument.
--user is not required, and there is a default value for it. It's not a positional argument.
I'm using the following code to create this command, but
#!/usr/bin/env python
import argparse
parser = argparse.ArgumentParser(description='My example explanation')
subparsers = parser.add_subparsers(dest='action')
subparsers.required = True
subcommand_parser = subparsers.add_parser('subcommand')
subcommand_parser_subparser = subcommand_parser.add_subparsers()
change_subcommand_parser = subcommand_parser_subparser.add_parser('--change')
change_subcommand_parser.add_argument('--host', required=True)
change_subcommand_parser.add_argument('--user', required=False, default='Salam')
print(parser.parse_args())
When I try this code with the following command in CLI:
./myscript something --change --host hello --user arian
I get the following error:
usage: myscript subcommand [-h] {--change} ...
myscript subcommand: error: invalid choice: 'hello' (choose from '--change')
My questions are:
What's wrong with this code?
How can I make users type --user & --host, and not use them as positional arguments?

how to handle input arguments that may be one value or a list of many?

I am working with a script from GitHub that someone wrote, which is designed for the command line. All the examples show something like:
thisscript.py -u 'teepee' -p pword999 -d 8
I want to run this inside another function, where I can use it like:
thisscript(username='teepee', password='pword999',...)
The main file includes a number of class definitions and this main function (below). What is the proper way to wrap this function such that I can call it as its own function?
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='...', add_help=False, usage='thisscript.py -u username -p password [options]')
parser.add_argument('-u', metavar='<STR>', type=str, help='username')
parser.add_argument('-p', metavar='<STR>', type=str, help='password')
parser.add_argument('-a', metavar='<INT>', type=int, default=4, help='Number ascending [4]')
parser.add_argument('-d', metavar='<INT>', type=int, default=0, help='Number descending [0]')
I have tried doing import thisscript but that doesn't seem to work.
I think the simplest solution would be to call the script the way it was designed to be called -- in the shell -- by using the subprocess module.
import subprocess
def transcript(
username,
password,
descending_num=0,
ascending_num=4,
):
command = f'transcript.py -u {username} -p {password} -d {descending_num} -a {ascending_num}'
subprocess.call(command, shell=True)
Giving the function keyword arguments to mirror the script's default arguments is optional, but makes usage clearer.

Call my Chat script from other Python script

This question is similar to other python from within python script calling but none of those are working for me.
I have a chat script which uses Python XMPP to send a chat message. The syntax is as so:
python chat.py -c "recipient#example.com" -u "sender#example.com" -p "secret" -m "message"
script:
#!/usr/bin/python
import sys
import argparse
import xmpp
def main(argv):
parser = argparse.ArgumentParser()
parser.add_argument('-c', dest='contact', required=True)
parser.add_argument('-u', dest='username', required=True)
parser.add_argument('-p', dest='password', required=True)
parser.add_argument('-m', dest='message', required=False, default='')
args = parser.parse_args(argv)
if (args.message == '') and not sys.stdin.isatty():
for line in sys.stdin:
args.message = args.message + line
jid = xmpp.protocol.JID(args.username)
jabber = xmpp.Client(jid.getDomain(), debug=[])
jabber.connect(server=(jid.getDomain(), 5222) )
jabber.auth(jid.getNode(), args.password)
jabber.send(xmpp.Message(args.contact, args.message.strip()))
if __name__ == "__main__":
main(sys.argv[1:])
as you can see, it takes 4 arguments.
Now I have another python script which is listening to sensors. I am trying to get it to send chat messages when it detects sensor readings so from within listen.py I am doing this:
...
import chat
...
chat.main('-c "chatto#server.com" -u "chatfrom#server.com" -p "password" -m "Yo Yo Yo Wassup"')
....
I have also tried subprocess.call but perhaps have not gotten the syntax correct. both python scripts are in the same directory. So for those of you looking for a specific question, How can I call the chat.py from within listen.py while providing the four required args?
parse_args() is going to expect a list of strings. For simple ones you can just split() on spaces, but in your case you have a complex argument with internal spaces so that won't work.
Don't include quotes in the strings. Those are used to allow an argument to have internal spaces when defined from the shell, but they aren't sent into argv.
chat.main(['-c', 'chatto#server.com', '-u', 'chatfrom#server.com', '-p', 'password', '-m', 'Yo Yo Yo Wassup'])
See more here: https://docs.python.org/2/library/argparse.html#beyond-sys-argv

argparse augment sub-command defaults via global options

I would like to be able to use the global options from an argparse.ArgumentParser object to override/augment the defaults values for a sub-command.
An example would be that displayed help reflects the global updates, i.e., for the following toy example:
import argparse
import os
import sys
# Global parser and options.
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("--user",
dest="user",
default=os.environ.get("USER"),
help="Override the setting of the $USER variable.")
# Sub-command parser and options.
subparsers = parser.add_subparsers()
command = subparsers.add_parser(
"command",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
command.add_argument("--config",
dest="config",
default="~%s/config" % os.environ.get("USER"),
help="The config file.")
options = parser.parse_args()
Ideally when when I run this in help mode I would get,
> python example.py --user thing command --help
usage: example.py command [-h] [--config CONFIG]
optional arguments:
-h, --help show this help message and exit
--config CONFIG The config file. (default: ~thing/config)
i.e., the config file path is user specific (thing). I realize that I could change the default config to be "~%(user)s/config" and then resolve this at run-time with the options namespace, however I would like the help to be more explicit.
I gather an alternative solution would be to try to parse the arguments once to obtain the global options, i.e.,
if "--help" in sys.argv:
# Parse the command minus the help to obtain the global options.
args = sys.argv[1:]
args.remove("--help")
# Update the defaults with the global options.
options = parser.parse_args(args)
command.set_defaults(config="~%s/config" % options.user)
# Re-parse the options.
parser.parse_args()
Though this seems somewhat hacky. Is there a better approach/solution?
After defining the global options, but before defining the subcommands, call parse_known_args to find out what the value of --user is. Then, finish defining the subparser commands, using the value of --user to define the default of --config, before
calling parse_args to parse all options on the command line.
This is a little different from your alternative, but it keeps all the command-line processing inside the argparse object.
(I trimmed down your code a little just to shorten it for this answer.)
import os
import argparse
# Global preparser and options.
preparser = argparse.ArgumentParser(add_help=False)
preparser.add_argument("--user", dest="user", default=os.environ.get("USER"))
# ****** NEW *******
options, _ = preparser.parse_known_args() # Ignore what we haven't defined yet
user = options.user # Use this to define the default of --config
parser = argparse.ArgumentParser(parents=[ preparser ], add_help=True,
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
# Sub-command parser and options.
subparsers = parser.add_subparsers()
command = subparsers.add_parser("command")
# ****** MODIFIED *******
command.add_argument("--config", dest="config", default="~%s/config" % (user,))
options = parser.parse_args()
Here's a solution that modifies the help line directly, at runtime. It could also modify os.environ or some other global as well, but I'll keep it simple. The key is assigning the action created by add_argument('--config'...) to a variable. help is just an attribute of that variable. You are free to modify that.
class FooAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
config.help = 'new user (%s)'% values
setattr(namespace, self.dest, values)
parser = argparse.ArgumentParser()
parser.add_argument('--user',action=FooAction)
sub = parser.add_subparsers()
cmd = sub.add_parser('cmd')
config = cmd.add_argument('--config',help='initial help')
config.help = 'default help' # alt way of setting 'help'
# print config # to see other attributes of the config action
args = parser.parse_args()
print args
Invoked with just cmd -h we get the default help
$ python stack12167228.py cmd -h
usage: stack12167228.py cmd [-h] [--config CONFIG]
optional arguments:
-h, --help show this help message and exit
--config CONFIG default help
Invoked with --user xxx cmd -h, the help is customized
$ python stack12167228.py --user xxx cmd -h
usage: stack12167228.py cmd [-h] [--config CONFIG]
optional arguments:
-h, --help show this help message and exit
--config CONFIG new user (xxx)

Categories