This seems like it should be so simple, but i am having some serious issues. All i want to do is see if the user input matches a 2 letter expression. I guess my biggest problem i have is that i am not very familiar with the re library and the documentation does not really help me too much.
This is what i have tried so far:
try 1
if re.match(sys.argv[3], "GL", re.I):
input_file_path = "V:\\test"
try 2
if re.ignorecase(sys.argv[3], "GL"):
input_file_path = "V:\\test"
try 3
if sys.argv[3] == "GL":
input_file_path = "V:\\test"
The way i call the program to run: filename.py tester test GL
"tester" and "test" are not really used yet.
EDIT: I found my main problem. I was calling a bunch of if statements rather than elif. So the last one that said else: exit() always got hit (cause i was testing the first if). rookie mistake
Just convert the string to test to lowercase before comparing and you should be fine:
if sys.argv[3].lower() == "gl":
input_file_path = "V:\\test"
More notably, regular expressions are not the right tool for this job.
Your re.match is backward. The pattern comes first. Try:
if re.match('GL', sys.argv[3], re.I):
input_file_path = "V:\\test"
Obviously the third argument is not 'GL'. print sys.argv and you will see that. My guess is that you are off by one in your index.
Show us the commandline you use to run your script.
printing the sys.argv[3] prints exactly GL – LiverpoolFTW
Then the bug is elsewhere. If you print sys.argv[3].lower() == "gl" just before, and input_file_path just after, you will see the expected values. What you really need here is a debugger. pdb is the built-in standard, but I highly recommend pudb.
For quick setup, paste these into a terminal. virtualenv is a industry standard for keeping project dependencies separate.
cd ~
wget https://raw.github.com/pypa/virtualenv/1.6.3/virtualenv.py
python virtualenv.py mypy
source mypy/bin/activate
pip install pudb
Source that activate file whenever you want to get into the environment. Run deactivate (an alias defined by activate) to get out. Make sure to use the python in the environment (ie #!/usr/bin/env python) rather than hard-coding a particular python instance.
Related
I'm a beginner Python coder and I'm very unsure on how to create a simple shell program that accepts commands (ex. printrecipes, printinventory, load etc.)
The input should look like:
$ loadrecipes
$ printmoney()
20
For this shell, I'm trying to use a while loop so it continues through the program without crashing even if they input a command that is acceptable.
def handle_commands():
keep_going=True
command=input("$" + " ")
while keep_going:
if command == '$ quit':
keep_going = False
break
elif command == "$ loadrecipefile(recipe_file)"
j
elif command == "$ printrecipes":
printrecipes()
elif command == "$ printiinventory":
printiinventory()
elif command == "$ printmoney":
printmoney()
elif command == "$ buyingredient":
I have no idea what to go from here.
The commands are that loadrecipes(recipe_file) takes in one argument, all print commands don't take an argument, buyingredient(ingredient_name, number:int) takes in 2 arguments (the ingredient name and how many of those ingredients).
So, for each command I have created a function in correspondence. Such as for printiinventory() I have:
def printiinventory():
print(iinventory['apple'],iinventory['beets'],iinventory['carrots'])
so if the command is:
$ printiinventory
0 4 3
it should come out to be like this
So your flow should look like this:
while True:
command = input("$ ")
if command is ...
elif ...:
Very similar to what you have, with the difference that you don't need to expect $ into the user's input. Input function prints the argument passed and returns SOLELY the user's input, not the rest of the content in the same line. So you should check for commands like command == "printrecipes", etc.
Explanation:
This piece of code:
x = input(str)
Is equivalent to:
print(str); x = input(str)
with the only difference that print() creates a new line, so the input will be taken from the line just below the printed content.
You could emulate this behaviour (the printing in the same line, that is) with the IO low-level Python modules, but there is no need when you can do just that.
Edit
In order to parse the commands, you can opt for the classical command line interface syntax, that separates command name and argument with spaces, or you could make your own parser. In case you go for the first, you could use Python's built-in argparse module. In case you'd rather use the second (which is more of a headache, especially if you are a starter), you have to write your own parser from scratch. Is not that big of a deal if you know regex, but I'm afraid that's a different question you should ask in the site. I would recommend you to take a look at some tutorials. Just googling: "make my own command parser python" gives you thousands of results, even though most of them will go for classic command line parsing syntax.
Edit 2
I've noticed you use some sort of flag to check if you need to keep going inside the loop. That is useless in the piece of code you use; just use break command and you're good to go.
Edit 3
Taking a close look at the OP's comments, I see you are trying to write Python code to be executed by a Python script. You can for sure do that; you've got the eval and exec modules, BUT note that this is a very risky practice, code can very easily be injected into your program, causing huge security holes. It is highly discouraged to do that. You have to separate command parsing from task executing. The user cannot ever have direct access to the control flow of the program.
Is there a way in Python to detect, within a process, where that process is being executed? I have some code that includes the getpass.getpass() function, which is broken in Spyder, and it's annoying to go back and forth between the command line and the IDE all the time. It would be useful if I could add code like:
if not being run from Spyder:
use getpass
else:
use alternative
Here is the solution I ended up using. After reading Markus's answer, I noticed that Spyder adds half a dozen or so environment variables to os.environ with names like SPYDER_ENCODING, SPYDER_SHELL_ID, etc. Detecting the presence of any of these seems relatively unambiguous, compared to detecting the absence of a variable with as generic a name as 'PYTHONSTARTUP'. The code is simple, and works independently of Spyder's startup script (as far as I can tell):
if any('SPYDER' in name for name in os.environ)
# use alternative
else:
# use getpass
Since the string is at the beginning of each environment variable name, you could also use str.startswith, but it's less flexible, and a little bit slower (I was curious):
>>> import timeit
>>> s = timeit.Timer("[name.startswith('SPYDER') for name in os.environ]", "import os")
>>> i = timeit.Timer("['SPYDER' in name for name in os.environ]", "import os")
>>> s.timeit()
16.18333065883474
>>> i.timeit()
6.156869294143846
The sys.executable method may or may not be useful depending on your installation. I have a couple WinPython installations and a separate Python 2.7 installation, so I was able to check the condition sys.executable.find('WinPy') == -1 to detect a folder name in the path of the executable Spyder uses. Since the warning that shows in IDLE when you try to use getpass is less "loud" than it could be, in my opinion, I ended up also checking the condition sys.executable.find('pythonw.exe') == -1 to make it slightly louder. Using sys.executable only, that method looks like:
if sys.executable.find('pythonw.exe') == sys.executable.find('WinPy') == -1:
# use getpass
else:
# use alternative
But since I want this to work on other machines, and it's much more likely that another user would modify their WinPython installation folder name than that they would rename their IDLE executable, my final code uses sys.executable to detect IDLE and os.environ to detect Spyder, providing a "louder" warning in either case and keeping the code from breaking in the latter.
if any('SPYDER' in name for name in os.environ) \
or 'pythonw.exe' in sys.executable:
password = raw_input('WARNING: PASSWORD WILL BE SHOWN ON SCREEN\n\n' * 3
+ 'Please enter your password: ')
else:
password = getpass.getpass("Please enter your password: ")
By default, Spyder uses a startup scrip, see Preferences -> Console -> Adanced setting. This option is usually set to the scientific_startup.py file that loads pylab et al.
The easiest solution is to just add a global variable to the file and then use that in your if statement, e.g. add this line at the end of scientific_startup.py:
SPYDER_IDE_ACTIVE = True
In your script:
if not 'SPYDER_IDE_ACTIVE' in globals():
use getpass
else:
use alternative
This will work without throwing an error. You can also use exceptions if you like that more.
A second solution would be (if you cannot modify that file for some reason) to just check if the environment variable PYTHONSTARTUP is set. On my machine (using the Anaconda Python stack), it is not set for a regular Python shell. You could do
import os
if not 'PYTHONSTARTUP' in os.environ:
use getpass
else:
use alternative
Spyder provides the option of executing the current editor script in a native system terminal. This would produce identical behavior as if you were running from the command line. To set this up, open the Run Settings dialog by hitting F6. Then select the radio button "Execute in an external System terminal". Now run the script as usual by hitting F5. You should be able to use getpass in the normal fashion with this approach.
You could add env variable when running in Spyder and check it in code.
I am a bit confused as to how to get this done.
What I need to do is call an external command, from within a Python script, that takes as input several arguments, and a file name.
Let's call the executable that I am calling "prog", the input file "file", so the command line (in Bash terminal) looks like this:
$ prog --{arg1} {arg2} < {file}
In the above {arg1} is a string, and {arg2} is an integer.
If I use the following:
#!/usr/bin/python
import subprocess as sbp
sbp.call(["prog","--{arg1}","{arg2}","<","{file}"])
The result is an error output from "prog", where it claims that the input is missing {arg2}
The following produces an interesting error:
#!/usr/bin/python
import subprocess as sbp
sbp.call(["prog","--{arg1} {arg2} < {file}"])
all the spaces seem to have been removed from the second string, and equal sign appended at the very end:
command not found --{arg1}{arg2}<{file}=
None of this behavior seems to make any sense to me, and there isn't much that one can go by from the Python man pages found online. Please note that replacing sbp.call with sbp.Popen does not fix the problem.
The issue is that < {file} isn’t actually an argument to the program, but is syntax for the shell to set up redirection. You can tell Python to use the shell, or you can setup the redirection yourself.
from subprocess import *
# have shell interpret redirection
check_call('wc -l < /etc/hosts', shell=True)
# set up redirection in Python
with open('/etc/hosts', 'r') as f:
check_call(['wc', '-l'], stdin=f.fileno())
The advantage of the first method is that it’s faster and easier to type. There are a lot of disadvantages, though: it’s potentially slower since you’re launching a shell; it’s potentially non-portable because it depends on the operating system shell’s syntax; and it can easily break when there are spaces or other special characters in filenames.
So the second method is preferred.
I have a .pythonrc in my path, which gets loaded when I run python:
python
Loading pythonrc
>>>
The problem is that my .pythonrc is not loaded when I execute files:
python -i script.py
>>>
It would be very handy to have tab completion (and a few other things) when I load things interactively.
From the Python documentation for -i:
When a script is passed as first argument or the -c option is used, enter interactive mode after executing the script or the command, even when sys.stdin does not appear to be a terminal. The PYTHONSTARTUP file is not read.
I believe this is done so that scripts run predictably for all users, and do not depend on anything in a user's particular PYTHONSTARTUP file.
As Greg has noted, there is a very good reason why -i behaves the way it does. However, I do find it pretty useful to be able to have my PYTHONSTARTUP loaded when I want an interactive session. So, here's the code I use when I want to be able to have PYTHONSTARTUP active in a script run with -i.
if __name__ == '__main__':
#do normal stuff
#and at the end of the file:
import sys
if sys.flags.interactive==1:
import os
myPythonPath = os.environ['PYTHONSTARTUP'].split(os.sep)
sys.path.append(os.sep.join(myPythonPath[:-1]))
pythonrcName = ''.join(myPythonPath[-1].split('.')[:-1]) #the filename minus the trailing extension, if the extension exists
pythonrc = __import__(pythonrcName)
for attr in dir(pythonrc):
__builtins__.__dict__[attr] = getattr(pythonrc, attr)
sys.path.remove(os.sep.join(myPythonPath[:-1]))
del sys, os, pythonrc
Note that this is fairly hacky and I never do this without ensuring that my pythonrc isn't accidentally clobbering variables and builtins.
Apparently the user module provides this, but has been removed in Python 3.0. It is a bit of a security hole, depending what's in your pythonrc...
In addition to Chinmay Kanchi and Greg Hewgill's answers, I'd like to add that IPython and BPython work fine in this case. Perhaps it's time for you to switch? :)
I'm trying to save myself just a few keystrokes for a command I type fairly regularly in Python.
In my python startup script, I define a function called load which is similar to import, but adds some functionality. It takes a single string:
def load(s):
# Do some stuff
return something
In order to call this function I have to type
>>> load('something')
I would rather be able to simply type:
>>> load something
I am running Python with readline support, so I know there exists some programmability there, but I don't know if this sort of thing is possible using it.
I attempted to get around this by using the InteractivConsole and creating an instance of it in my startup file, like so:
import code, re, traceback
class LoadingInteractiveConsole(code.InteractiveConsole):
def raw_input(self, prompt = ""):
s = raw_input(prompt)
match = re.match('^load\s+(.+)', s)
if match:
module = match.group(1)
try:
load(module)
print "Loaded " + module
except ImportError:
traceback.print_exc()
return ''
else:
return s
console = LoadingInteractiveConsole()
console.interact("")
This works with the caveat that I have to hit Ctrl-D twice to exit the python interpreter: once to get out of my custom console, once to get out of the real one.
Is there a way to do this without writing a custom C program and embedding the interpreter into it?
Edit
Out of channel, I had the suggestion of appending this to the end of my startup file:
import sys
sys.exit()
It works well enough, but I'm still interested in alternative solutions.
You could try ipython - which gives a python shell which does allow many things including automatic parentheses which gives you the function call as you requested.
I think you want the cmd module.
See a tutorial here:
http://wiki.python.org/moin/CmdModule
Hate to answer my own question, but there hasn't been an answer that works for all the versions of Python I use. Aside from the solution I posted in my question edit (which is what I'm now using), here's another:
Edit .bashrc to contain the following lines:
alias python3='python3 ~/py/shellreplace.py'
alias python='python ~/py/shellreplace.py'
alias python27='python27 ~/py/shellreplace.py'
Then simply move all of the LoadingInteractiveConsole code into the file ~/py/shellreplace.py Once the script finishes executing, python will cease executing, and the improved interactive session will be seamless.