Please find the following snippet:
import argparse
parser = argparse.ArgumentParser(
description="Create plot from data",
formatter_class=lambda prog: argparse.HelpFormatter(
prog, max_help_position=27))
action = parser.add_mutually_exclusive_group(required=True)
action.add_argument('--foo', help="Create foo plot") # input is <filename>.foo
action.add_argument('--bar', help="Create bar plot") # Input is <filename>.bar
I run this in linux terminal emulator. Is it possible within python that, in the terminal, double tabiing will show only files with extension foo or bar, depending on argument, and not all the files in PWD?
I have found TAB autocomplete python CLI, but that is a decade old. Is there any option now?
Update #Lenormju:
I have updated the code as:
action.add_argument('--foo', help="Create foo plot", choices=('agr'))
action.add_argument(
'--bar', help="Create bar plot").completer = ChoicesCompleter('.agr')
So, now in terminal,
python ~/bin/code.py --foo [TAB][TAB]
I am expecting this to show files with .agr extensions only. Instead it is still shown all the files present in PWD.
Actually, this should not work, because "choices" means, I have to choose between 'a' 'g' or 'r'. May be I was not clear in the main question, show I have elaborated it.
When you press Tab, your shell expects to receive a list of strings (the possible completions).
Either you register a Bash function to do the completion for your Python script (see this answer) so that your Python script is not called until the command line is finished.
Or else you do it with your Python script (see this answer for argcomplete which mixes well with argparse). In this case, Your Python script is indeed executed, but after computing the possible completions it will exit, and thus not run anything else. In other words, it will be called repeatedly to provide completion suggestions, then once at the end with the full command line to do its actual work.
Showing only the files with certain extensions depending on the argument is "just" a matter of customizing the completer.
Your question is really just a duplicate of the other, but you seem to not see it. If the misunderstanding is actually on my side, please provide detailed explanations.
Related
I am trying to debug a script which takes command line arguments as an input. Arguments are text files in the same directory. Script gets file names from sys.argv list. My problem is I cannot launch the script with arguments in pycharm.
I have tried to enter arguments into "Script parameters" field in "Run" > "Edit configuration" menu like so:
-s'file1.txt', -s'file2.txt'
But it did not work. How do I launch my script with arguments?
P.S. I am on Ubuntu
In PyCharm the parameters are added in the Script Parameters as you did but, they are enclosed in double quotes "" and without specifying the Interpreter flags like -s. Those flags are specified in the Interpreter options box.
Script Parameters box contents:
"file1.txt" "file2.txt"
Interpeter flags:
-s
Or, visually:
Then, with a simple test file to evaluate:
if __name__ == "__main__":
import sys
print(sys.argv)
We get the parameters we provided (with sys.argv[0] holding the script name of course):
['/Path/to/current/folder/test.py', 'file1.txt', 'file2.txt']
For the sake of others who are wondering on how to get to this window. Here's how:
You can access this by clicking on Select Run/Debug Configurations (to the left of ) and going to the Edit Configurations. A
gif provided for clarity.
On PyCharm Community or Professional Edition 2019.1+ :
From the menu bar click Run -> Edit Configurations
Add your arguments in the Parameters textbox (for example file2.txt file3.txt, or --myFlag myArg --anotherFlag mySecondArg)
Click Apply
Click OK
In addition to Jim's answer (sorry not enough rep points to make a comment), just wanted to point out that the arguments specified in PyCharm do not have special characters escaped, unlike what you would do on the command line. So, whereas on the command line you'd do:
python mediadb.py /media/paul/New\ Volume/Users/paul/Documents/spinmaster/\*.png
the PyCharm parameter would be:
"/media/paul/New Volume/Users/paul/Documents/spinmaster/*.png"
Notice that for some unknown reason, it is not possible to add command line arguments in the PyCharm Edu version. It can be only done in Professional and Community editions.
Add the following to the top of your Python file.
import sys
sys.argv = [
__file__,
'arg1',
'arg2'
]
Now, you can simply right click on the Python script.
The first parameter is the name of the script you want to run. From the second parameter onwards it is the the parameters that you want to pass from your command line. Below is a test script:
from sys import argv
script, first, second = argv
print "Script is ",script
print "first is ",first
print "second is ",second
from sys import argv
script, first, second = argv
print "Script is ",script
print "first is ",first
print "second is ",second
And here is how you pass the input parameters :
'Path to your script','First Parameter','Second Parameter'
Lets say that the Path to your script is /home/my_folder/test.py, the output will be like :
Script is /home/my_folder/test.py
first is First Parameter
second is Second Parameter
It took me some time to figure out that input parameters are comma separated.
I believe it's included even in Edu version. Just right click the solid green arrow button (Run) and choose "Add parameters".
It works in the edu version for me. It was not necessary for me to specify a -s option in the interpreter options.
In edit configuration of PyCharm when you are giving your arguments as string, you should not use '' (these quotations) for giving your input.
Instead of -s'file1.txt', -s'file2.txt'
simply use:
-s file1.txt, -s file2.txt
you can used -argName"argValue" like -d"rd-demo" to add Pycharm arguments
-d"rd-demo" -u"estate"
Arguments added in Parameters Section after selected edit Configuration from IDE
I'm using argparse, and in order to debug my scripts I also using Edit Configuration. For example below the scripts gets 3 parameters (Path, Set1, N) and an optional parameter (flag):
'Path' and 'Set1' from type str.
'N' from type int.
The optional parameter 'flag' from type boolean.
impor argparse
parser = argparse.ArgumentParser(prog='main.py')
parser.add_argument("Path", metavar="path", type=str)
parser.add_argument("Set1", type=str, help="The dataset's name.")
parser.add_argument("N", type=int, help="Number of images.")
parser.add_argument("--flag", action='store_true')
params = parser.parse_args()
In order to to run this in a debug or not by using command line, all needed is:
bar menu Run-->Edit Configuration
Define the Name for your debug/run script.
Set the parameters section. For the above example enter as follow:
The defualt 3 parameters must me included --> "c:\mypath" "name" 50
For the optional parameter --> "c:\mypath" "name" 50 "--flag"
parameter section
I would like to set the value of some objects in PyCharm before running my code and debugging. It would save me a lot of time, because I would not have to enter by myself the values of those objects.
For example, I would like to define foo = 1 and bar = 2 for the following code. (In reality, it is for more complex code with more objects.)
foo = input("value of foo ?")
foo = int(foo)
bar = input("value of bar ?")
bar = int(bar)
total = foo + bar
Is there a way to do that in PyCharm without modifying the code ? I do not want to do foo = 1 and bar = 2 because I would have to modify the code.
I looked online and tried to use the "Parameters" and "Environment variables" of the Run/Debug Configurations but could not succeed.
Thank you for your help.
Latest PyCharm 2018.2 EAP introduces input redirection feature, I believe this is exactly what you want:
I can think of three options:
Read the values from environment variables. You can set the values of the environment variables in your run configuration. You can even create multiple run configurations for different values and combinations of values.
Read the values from command line arguments. You can provide these arguments in your run configuration.
Run your program from the command line and redirect input from a file or pipe input from another command.
Either way, you set the run configuration once and only modify it when you need to change the values.
Note that this changes the interface to your program. Any users will have to set the environment variable or provide the command line arguments. For many situations command line arguments are very common when writing text-based programs. Optionally, you can write code that tests if the environment variables or command line arguments exists, and if not provide a prompt for the user to type them in.
I have a python script that I'd like to supply with an argument (usually) containing wildcards, referring to a series of files that I'd like to do stuff with. Example here:
#!/usr/bin/env python
import argparse
import glob
parser = argparse.ArgumentParser()
parser.add_argument('-i', action="store", dest="i")
results = parser.parse_args()
print 'argument i is: ', results.i
list_of_matched_files = glob.glob(results.i)
In this case, everything works great if the user adds quotes to the passed argument like so:
./test_script.py -i "foo*.txt"
...but often times the users forget to add quotes to the argument and are stumped when the list only contains the first match because UNIX already expanded the list and argparse only then gets the first list element.
Is there a way (within the script) to prevent UNIX from expanding the list before passing it to python? Or maybe even just to test if the argument doesn't contain quotes and then warn the user?
No. Wildcards are expanded by the shell (Bash, zsh, csh, fish, whatever) before the script even runs, and the script can't do anything about them. Testing whether the argument contains quotes also won't work, as the shell similarly strips the quotes from "foo*.txt" before passing the argument to the script, so all Python sees is foo*.txt.
Its not UNIX that is doing the expansion, it is the shell.
Bash has an option set -o noglob (or -f) which turns off globbing (filename expansion), but that is non-standard.
If you give an end-user access to the command-line then they really should know about quoting. For example, the commonly used find command has a -name parameter which can take glob constructs but they have to be quoted in a similar manner. Your program is no different to any other.
If users can't handle that then maybe you should give them a different interface. You could go to the extreme of writing a GUI or a web/HTML front-end, but that's probably over the top.
Or why not prompt for the filename pattern? You could, for example, use a -p option to indicate prompting, e.g:
import argparse
import glob
parser = argparse.ArgumentParser()
parser.add_argument('-i', action="store", dest="i")
parser.add_argument('-p', action="store_true", default=False)
results = parser.parse_args()
if results.p:
pattern = raw_input("Enter filename pattern: ")
else:
pattern = results.i
list_of_matched_files = glob.glob(pattern)
print list_of_matched_files
(I have assumed Python 2 because of your print statement)
Here the input is not read by the shell but by python, which will not expand glob constructs unless you ask it to.
You can disable the expansion using set -f from the command line. (re-enable with set +f).
As jwodder correctly says though, this happens before the script is run, so the only way I can think of to do this is to wrap it with a shell script that disables expansion temporarily, runs the python script, and re-enables expansion. Preventing UNIX from expanding the list before passing it to python is not possible.
Here is an example for the Bash shell that shows what #Tom Wyllie is talking about:
alias sea='set -f; search_function'
search_function() { perl /home/scripts/search.pl $# ; set +f; }
This defines an alias called "sea" that:
Turns off expansion ("set -f")
Runs the search_function function which is a perl script
Turns expansion back on ("set +f")
The problem with this is that if a user stops execution with ^C or some such then the expansion may not be turned back on leaving the user puzzling why "ls *" is not working. So I'm not necessarily advocating using this. :).
This worked for me:
files = sys.argv[1:]
Even though only one string is on the command line, the shell expands the wildcards and fills sys.argv[] with the list.
Scratching my head... this curl command will work fine from the command line when I copy it from here and paste it in my Windows 7 command line, but I can't get it to execute in my Python 2.7.9 script. Says the system cannot find the specified file. Popen using 'ping' or something like that works just fine, so I'm sure this is a goober typo that I'm just not seeing. I would appreciate a separate set of eyes and any comments as to what is wrong.
proc = subprocess.Popen("curl --ntlm -u : --upload-file c:\\temp\\test.xlsx http://site.domain.com/sites/site/SiteDirectory/folder/test.xlsx")
Have a look at second two paragraphs of the subprocess.Popen documentation if you haven't already:
args should be a sequence of program arguments or else a single string. By default, the program to execute is the first item in args if args is a sequence. If args is a string, the interpretation is platform-dependent and described below. See the shell and executable arguments for additional differences from the default behavior. Unless otherwise stated, it is recommended to pass args as a sequence.
On Unix, if args is a string, the string is interpreted as the name or path of the program to execute. However, this can only be done if not passing arguments to the program. [emphasis mine]
Instead you should pass in a list in which each argument to the program (including the executable name itself) is given as a separate item in the list. This is generally going to be safer in a cross-platform context anyways.
Update: I see now that you're using Windows in which case the advice on UNIX doesn't apply. On Windows though things are even more hairy. The best advice remains to use a list :)
Update 2: Another possible issue (and in fact the OP's issue as reported in the comments on this answer) is that because the full path to the curl executable was not given, it may not be found if the Python interpreter is running in an environment with a different PATH environment variable.
I've seen similar questions (e.g. Running a command in a new Mac OS X Terminal window ) but I need to confirm this command and its expected behavior in a mac (which I don't have). If anyone can run the following in Python 3 Mac:
import subprocess, os
def runcom(bashCommand):
sp = subprocess.Popen(['osascript'], stdin=subprocess.PIPE, stderr=subprocess.PIPE)
sp.communicate('''tell application "Terminal"\nactivate\ndo script with command "{0} $EXIT"\nend tell'''.format(bashCommand))
runcom('''echo \\"This is a test\\n\\nThis should come two lines later; press any key\\";read throwaway''')
runcom('''echo \\"This is a test\\"\n\necho \\"This should come one line later; press any key\\";read throwaway''')
runcom('''echo \\"This is testing whether I can have you enter your sudo pw on separate terminal\\";sudo ls;\necho \\"You should see your current directory; press any key\\";read throwaway''')
Firstly, and most basically, is the "spawn new terminal and execute" command correct? (For reference, this version of the runcom function came from this answer below, and is much cleaner than my original.)
As for the actual tests: the first one tests that internal double escaped \\n characters really work. The second tests that we can put (unescaped) newlines into the "script" and still have it work just like semicolon. Finally, the last one tests whether you can call a sudo process in a separate terminal (my ultimate goal).
In all cases, the new terminal should disappear as soon as you "press any key". Please also confirm this.
If one of these doesn't work, a correction/diagnosis would be most appreciated. Also appreciated: is there a more pythonic way of spawning a terminal on Mac then executing a (sudo, extended) bash commands on it?
Thanks!
[...] its expected behavior [...]
This is hard to answer, since those commands do what I expect them to do, which might not be what you expect them to do.
As for the actual tests: the first one tests that internal double escaped \n characters really work.
The \\n with the doubled backslash does indeed work correctly in that it causes echo to emit a newline character. However, no double quotes are emitted by echo.
The second tests that we can put (unescaped) newlines into the "script" and still have it work just like semicolon.
That works also.
Finally, the last one tests whether you can call a sudo process in a separate terminal (my ultimate goal).
There is no reason why this should not work also, and indeed it does.
In all cases, the new terminal should disappear as soon as you "press any key". Please also confirm this.
That will not work because of several reasons:
read in bash will by default read a whole line, not just one character
after the script you supply is executed, there is no reason for the shell within the terminal to exit
even if the shell would exit, the user can configure Terminal.app not to close a window after the shell exits (this is even the default setting)
Other problems:
the script you supply to osascript will appear in the terminal window before it is executed. in the examples above, the user will see every "This is a test [...]" twice.
I cannot figure out what $EXIT is supposed to do
The ls command will show the user "the current directory" only in the sense that the current working directory in a new terminal window will always be the user's home directory
throwaway will not be available after the script bashCommand exits
Finally, this script will not work at all under Python 3, because it crashes with a TypeError: communicate() takes a byte string as argument, not a string.
Also appreciated: is there a more pythonic way of spawning a terminal on Mac [...]
You should look into PyObjC! It's not necessarily more pythonic, but at least you would eliminate some layers of indirection.
I don't have Python 3, but I edited your runcom function a little and it should work:
def runcom(bashCommand):
sp = subprocess.Popen(['osascript'], stdin=subprocess.PIPE, stderr=subprocess.PIPE)
sp.communicate('''tell application "Terminal"\nactivate\ndo script with command "{0} $EXIT"\nend tell'''.format(bashCommand))