I am trying to see if I can use argument autocompletion in Windows Powershell for my python script. Powershell allegedly supports argument completion.
Here's a minimal example which does not work:
#!/usr/bin/env python
# PYTHON_ARGCOMPLETE_OK
import argparse
import argcomplete
from argcomplete.completers import EnvironCompleter
def argument_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Minimal app where arcomplete should work")
parser.add_argument("--version", action="store_true", help="print out version").completer = EnvironCompleter
parser.add_argument("--do-this", action="store_true", help="do this").completer = EnvironCompleter
parser.add_argument("--do-that", action="store_true", help="do that").completer = EnvironCompleter
return parser
if __name__ == "__main__":
parser = argument_parser()
argcomplete.autocomplete(parser)
cli_args = parser.parse_args()
Then in Powershell I try: to type python -i minimal - and then press <TAB>, Nothing happens. What am I doing wrong? Maybe I should mention that I did not enable global autocompletion. Somehow when I run activate-global-python-argcomplete in powershell, I get an "Open With ..." dialogue.
It is possible, you just need a wrapper script and mind the correct encoding
parser = argument_parser()
output_stream = None
if "_ARGCOMPLETE_POWERSHELL" in os.environ:
output_stream = codecs.getwriter("utf-8")(sys.stdout.buffer)
argcomplete.autocomplete(parser, output_stream=output_stream)
args = parser.parse_args()
I have composed a minimal working example. It's based on Tibor's mat example, but that one is actually not working because it is missing the utf-8 encoding of the buffer.
Related
I am using the argparse library but for whatever reason I'm having difficult printing the -h argument. Here is the entire source I have:
# df.py
import argparse
parser = argparse.ArgumentParser(description='Dedupe assets in our library.')
parser.add_argument('--masters', nargs='?', default=None, type=int, help='Enter one or more ids.')
if __name__ == '__main__':
print ('hi')
I was under the impression that entering in the --h flag via:
$ python df.py --help
Would automatically print the help stuff for the file using argparse but I seem to be making false assumptions. It seems like I also have to add in something like this into my code?
if '--help' in sys.argv: print (parser.parse_args(['-h']))
What is the 'proper' way to print out the help args when using the argparse library?
You forgot to actually parse the arguments; if you put parser.parse_args() in after defining the parser, it would respond to -h/--help. Typically, you'd do something like:
args = parser.parse_args()
so that the args object can be used to access the parsed argument data.
I'll also note that the argument parsing should almost certainly be controlled by the if __name__ == '__main__': guard; if you're not being invoked as the main script, parsing the command line is unusual, to say the least. Idiomatic code would look something like:
# df.py
def main():
import argparse # Could be moved to top level, but given it's only used
# in main, it's not a terrible idea to import in main
parser = argparse.ArgumentParser(description='Dedupe assets in our library.')
parser.add_argument('--masters', nargs='?', type=int, help='Enter one or more ids.')
args = parser.parse_args()
print ('hi')
# Do something with args.masters or whatever
if __name__ == '__main__':
main()
I'm trying to read pathnames from the user and joining new directories to it. The problem is that the path string has directories starting with "n" or "t", which gives inconsistencies when parsing.
My OS in Windows 10, and my Python version is 3.7.6.
The supplied argument to argparse is 'C:\\Codes\\timpel\\noiseAnalysis\\Metadata'
import os
from argparse import ArgumentParser
def main(args):
case_name = 'APP001'
file_name = 'APP001.json'
input_path = args.input_dir
output_file = os.path.join(
args.input_dir,
case_name,
file_name
)
print(input_path)
print(output_file)
if __name__ == "__main__":
parser = ArgumentParser()
parser.add_argument('input_dir', type=str)
main(parser.parse_args())
The printing results are C:\\Codes\\timpel\\noiseAnalysis\\Metadata and C:\\Codes\\timpel\\noiseAnalysis\\Metadata\APP001\APP001.json respectively.
How should I deal with this type of problem consistently? How should I deal with different types of slashes in the pathname given by the user?
This should work. I needed to change some erroneous variable names, but if you call the program on the command line with:
C:\>prog.py C:\Codes\timpel\noiseAnalysis\Metadata it outputs:
C:\Codes\timpel\noiseAnalysis\Metadata
C:\Codes\timpel\noiseAnalysis\Metadata\APP001\APP001.json
At least in my version of Python (3.6). Code:
import os
from argparse import ArgumentParser
def main(args):
input_path = args.input_dir # <--
case_name = 'APP001'
file_name = 'APP001.json'
output_file = os.path.join(
input_path,
case_name,
file_name
)
print(input_path)
print(output_file)
if __name__ == "__main__":
parser = ArgumentParser()
parser.add_argument('input_dir', type=str)
main(parser.parse_args())
I'm not sure if this helps your question or maybe somebody coming here for argparser-ing Windows path problems, but I found a bug in IDLE that strips backslashes entered in "Run... Customized". This DOES NOT happen when running from the command line.
When you enter C:\Codes\timpel\noiseAnalysis\Metadata into Run... Customized in IDLE it becomesC:CodestimpelnoiseAnalysisMetadata, but works just find on the command line (e.g.:python prog.py C:\Codes\timpel\noiseAnalysis\Metadata)
There is a work-around for IDLE. If you entered the path in quotes (e.g.:'C:\Codes\timpel\noiseAnalysis\Metadata') it will be processed fine.
Also note that backslashes \ in IDLE arguments cause the pre-populated textbox to get screwed up even if you use the quotes work-around. The text that was passed to the previous run will now be surrounded in curly braces (e.g.:{C:CodestimpelnoiseAnalysisMetadata}).
I filed an issue on github for this and I'm waiting for a response.
I am trying to run one python script (Main_Script) which is supposed to get argparse flag and this script at the same time calls another script(Sub_Script) which is also supposed to get the flag to input. And when I call Main_Script I get an error which says that I can't use the flag because it is not defined in the script but it is actually defined. The error notification makes me use the flag from subscript instead.
MAIN_SCRIPT
parser = argparse.ArgumentParser(add_help=True)
parser.add_argument('-p', '--print_positive_results', action='store_true')
args = parser.parse_args()
PRINT_POSITIVE = args.print_positive_results
#I then use rhi global variable PRINT_POSITIVE
SUB_SCRIPT
import argparse
parser = argparse.ArgumentParser(add_help=True)
parser.add_argument('-d', '--debug', action='store_true')
args = parser.parse_args()
And when I call python MAIN_SCRIPT.py -p I get this
usage: test_grammar.py [-h] [-d]
test_grammar.py: error: unrecognized arguments: -p
DEBUG = False
if (args.debug ):
DEBUG = True
Seems like the command line args from the main script are passed through to the sub script.
You could try to (and probably should) wrap the argparse stuff into:
if __name__ == '__main__':
<argparse stuff>
With this the code is only called when the script is called from the command line. The real code could be outsourced into a function. This way you can use the script from command line with argparse and only import the function from the script if you call it from another script:
Main script:
import argparse
import subscript
if __name__ == '__main__':
parser = argparse.ArgumentParser(add_help=True)
parser.add_argument('-p', '--print_positive_results', action='store_true')
args = parser.parse_args()
...
subscript.your_function(<whatever your args are>)
...
Sub script:
import argparse
def your_function(<your args>):
<your code>
if __name__ == '__main__':
parser = argparse.ArgumentParser(add_help=True)
parser.add_argument('-d', '--debug', action='store_true')
args = parser.parse_args()
your_function(<whatever your args are>)
...
I have a python script using argparse. After typing in python script_name.py -h on the command line, it shows help message for another command but the code still works. The script can recognize options defined in it and run well. It looks like the script is packaged by something. I put argparse in a function and everything works well at the beginning. I just can't find out what causes the help message changed.
Here is the code:
#!/usr/bin/env python
import os
import sys
import json
import logging
import argparse
import handlers
HZZ_DIR = os.path.dirname(os.path.abspath(__file__))
ROOT_DIR = os.path.dirname(os.path.dirname(HZZ_DIR))
logger = logging.getLogger('hzz_logger')
logger.setLevel(logging.DEBUG)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
logger.addHandler(console)
def parse_args():
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument('job', choices=['ws','lm','np'],
help="ws: workspace; lm: limit; np: npranking")
arg_parser.add_argument('-a', '--action', nargs=1,
help="for Limit and NPranking: get/plot (limit/pull)")
arg_parser.add_argument('-b', '--blinded', action='store_true',
help="for Limit: true -- do expected only, false -- do observed as well.")
arg_parser.add_argument('-v', '--version', nargs=1, type=int,
help="input version")
arg_parser.add_argument('-t', '--tag', nargs=1,
help='workspace tag')
arg_parser.add_argument('-m', '--mass', nargs='+', type=int,
help='signal mass(es)')
arg_parser.add_argument('-c', '--config', nargs=1,
help='configure file')
arg_parser.add_argument('-u', '--update', action='store_true',
help="update default settings")
args = arg_parser.parse_args()
return args
def load_settings(args):
pass
def run_job(settings):
pass
def execute():
args = parse_args()
settings = load_settings(args)
run_job(settings)
if __name__ == '__main__':
execute()
The help message is pasted here, which is actually the help message a command not directly used in this code. The options for this command can also be recognized...
$ python hzz_handler.py -h
Usage: python [-l] [-b] [-n] [-q] [dir] [[file:]data.root] [file1.C ... fileN.C]
Options:
-b : run in batch mode without graphics
-x : exit on exception
-n : do not execute logon and logoff macros as specified in .rootrc
-q : exit after processing command line macro files
-l : do not show splash screen
dir : if dir is a valid directory cd to it before executing
-? : print usage
-h : print usage
--help : print usage
-config : print ./configure options
-memstat : run with memory usage monitoring
Wow, another anti-Pythonic ROOT mystery! Your question and the comments are really helpful. Why did not anybody post the answer with ROOT.PyConfig.IgnoreCommandLineOptions = True?
Here is a primitive work-around:
import argparse
# notice! ROOT takes over argv and prints its own help message when called from command line!
# instead I want the help message for my script
# therefore, check first if you are running from the command line
# and setup the argparser before ROOT cuts in
if __name__ == '__main__':
parser = argparse.ArgumentParser(
formatter_class = argparse.RawDescriptionHelpFormatter,
description = "my script",
epilog = "Example:\n$ python my_script.py -h"
)
parser.add_argument("param", type=str, help="a parameter")
parser.add_argument("-d", "--debug", action='store_true', help="DEBUG level of logging")
args = parser.parse_args()
if args.debug:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.INFO)
logging.debug("parsed args: %s" % repr(args))
import ROOT
...
if __name__ == '__main__':
<do something with args>
In sort the answer is that always calls import ROOT after the argparse. Then ROOT won't take over the argparse and prints the required message that we want.
I am using optparse to get command line input.
Lets say that I am running a script demo.py and it creates some output. But unless I specify the command line input, the output is not written to a file.
I am trying to do the following:
python demo.py in command line should run the script, but not write the output anywhere.
python demo.py -o in command line should write the output to my default file name output.txt.
python demo.py -o demooutput.txt in command line should write the output to file demooutput.txt.
PS: I would not prefer to switch to argparse from optparse.
You can use optparse-callbacks to achieve this.
Here is how it wiill work for your use case.
parser.add_option("-o", action="callback", dest="output", callback=my_callback)
def my_callback(option, opt, value, parser):
if len(parser.rargs) > 0:
next_arg = parser.rargs[0]
if not next_arg.startswith("-"):
# Next argument is not another option
del parser.rargs[0]
setattr(parser.values, option.dest, next_arg)
return
# If not processed, set the default value
setattr(parser.values, option.dest, "output.txt")
I don't think there is unfortunately - the only way I can think of is hacking around the problem by adding your own logic statements. The following code should do the trick.
import re, sys
import optparse from OptionParser
usage = "usage: %prog [options] arg"
parser = OptionParser(usage)
if '-f' in argv:
a = argv.index('-f')
if (a != len(argv)-1) and re.search('[.]txt', argv[a+1]):
parser.add_option("-f", "--foo", dest="foo")
else:
parser.add_option("-f", dest="foo", action="store_true")
This doesn't answer the direct question, 'how to define an Action...', but it handles the inputs in a simple way.
Set '-o' to be 'store_true'. If True check the 'args' variable for a file name.
(options, args) = parser.parse_args()
if options.o:
if args:
dest = args[0]
else:
dest = 'output.txt'
else:
dest = ''
(In argparse the equivalent would be to define a positional argument with nargs='?'.)
If these are the only arguments, you could also get by with checking for the filename without requiring the `-o'.
Another possibility - 'store_const', with the positional 'filename' having priority:
parser = optparse.OptionParser()
parser.add_option('-o',dest='dest',action='store_const', const='output.txt', default='')
(options, args) = parser.parse_args()
if args:
options.dest = args[0]
print options