I was reading programming python 4th edition by Mark Luze, Oreilly, by teaching myself.
There's an example on how to fork a child process, which I do not quite understand:
os.execlp('python', 'python', 'child.py', #other args#)
In an interactive shell(like bash), I know I can type python child.py #args# to ask python interpreter to run child.py with args.
Why are there TWO 'python' in the execlp() function? If I put only one python in the function, I would get an error complainting cannot find file or directory, which is the 1st args of child.py
The first argument is the program to execute (found on the PATH). The rest are the sys.argv arguments to the program.
The first such argument is the program name used to invoke it, and the display value used in the OS process list. It is the value of sys.argv[0] in a python script.
First of all, execlp is rarely used today. In most cases, you'd use the subprocess module, like this:
subprocess.call(['python', 'child.py'])
The first argument of execlp is the file you want to execute.
The latter arguments form the argument array to that program (sys.argv in Python). The first argument is then the name the program got invoked with. For example, Python sets the name to '-c' if the program is being run with the -c option. Similarly, grep behaves differently depending on the first argument, so that users can execute rgrep to imply grep -r.
Related
How can I run a python function from an AHK script? If it's possible, how would I:
Pass arguments to the python function?
Return data from the python function back to my running AHK script?
The only relevant information I could find was this answer, however I could not manage to implement it into my own script.
My best attempt at implementing this is the following snippet of code:
e::; nothing, because RunWait it's the command recommended in the question that I linked, so that means that I have to do a RunWait every time I press e?
There is no native way to do this, as the only interaction AHK can do with Python is to run an entire script. However, you can modify the python script to accept arguments, then you can interact between the scripts (like the linked question).
The solution is as follows- similar to the question you linked, set up the python script so that it takes the function name and function parameters as arguments, then have it run the function with those arguments.
Similar to my answer on the linked question, you can use sys.argv to do this:
# Import the arguments from the sys module
from sys import argv
# If any arguments were passed (the first argument is the script name itself, so you must check for >1 instead of >0)
if len(argv) > 1:
# This next line is basically saying- If argv is longer than 2 (the script name and function name)
# Then set the args variable to everything other than the first 2 items in the argv array
# Otherwise, set the args variable to None
args = argv[2:] if len(argv) > 2 else None
# If arguments were passed, then run the function (second item in argv) with the arguments (the star turns the list into args, or in other words turns the list into the parameter input format)
# Otherwise, run the function without arguments
argv[1](*args) if args else argv[1]()
# If there is code here, it will also execute. If you want to only run the function, then call the exit() function to quit the script.
Then, from AHK, all you would need to do is run the RunWait or Run command (depending on whether you want to wait for the function to finish) to call the function.
RunWait, script.py "functionName" "firstArgument" "secondArgument"
The second part of your question is tricky. In the question you linked, there is a nice explanation on how to return integer values (TLDR: use sys.exit(integer_value)), however if you want to return all sorts of data, like strings, for example, then the problem becomes more confusing. The thing is that at this point, I think the best solution is to write the output to a file, then have AHK read the file after the Python script is done executing. However, if you're already going to go down the "write to a file, then read it" route, then you might as well have already done that from the start and used that method to pass the function name and arguments to the python script.
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 would like to be able to log the command used to run the current python script within the script itself. For instance this is something I tried:
#test.py
import sys,subprocess
with open('~/.bash_history','r') as f:
for line in f.readlines():
continue
with open('logfile','r') as f:
f.write('the command you ran: %s'%line.strip('\n'))
However the .bash_history does not seem to be ordered in chronological order. What's the best recommended way to achieve the above for easy logging? Thanks.
Update: unfortunately sys.argv doesn't quite solve my problem because I need to use process subtitution as input variables sometimes.
e.g. python test.py <( cat file | head -3)
What you want to do is not universally possible. As devnull says, the history file in bash is not written for every command typed. In some cases it's not written at all (user sets HISTFILESIZE=0, or uses a different shell).
The command as typed is parsed and processed long before your python script is invoked. Your question is therefore not related to python at all. Wether what you want to do is possible or not is entirely up to the invoking shell. bash does not provide what you want.
If your can control the caller's shell, you could try using zsh instead. There, if you setopt INC_APPEND_HISTORY, zsh will append to its history file for each command typed, so you can do the parse history file hack.
One option is to use sys.argv. It will contain a list of arguments you passed to the script.
import sys
print 'Number of arguments:', len(sys.argv), 'arguments.'
print 'Argument List:', str(sys.argv)
Example output:
>python test.py
Number of arguments: 1 arguments.
Argument List: ['test.py']
>python test.py -l ten
Number of arguments: 3 arguments.
Argument List: ['test.py', '-l', 'ten']
As you can see, the sys.argv variable contains the name of the script and then each individual parameter passed. It does miss the python portion of the command, though.
First off I looked in the documentation and found this:
http://www.tutorialspoint.com/python/python_command_line_arguments.htm
And then I also found this:
how to execute a python script file with an argument from inside another python script file
I am using pycharm 2.7.3 and when I try to run my script I get and error (the error is not pycharm related). THe only way around this error I have found out to be is to run the script through the windows PowerShell with a couple of "arguments" (is that what its called?)
The code that follows is what makes up my script, temp.py
from sys import argv
# this what the Ide fails to run
scripts, first, second, third = argv
print "The script is called:", scripts
print "Your first variable is:", first
print "Your second variable is:", second
print "Your third variable is:", third
Next is what I types into the powershell
NOTE: The above code is temp.py
python temp.py first 2nd 3rd
1) What are those pieces of text called that appear after the "python temp.py", is it arguments?
2) Is it possible to run the code with the so called arguments in the IDE (I am using pyCharm)
3) If '2)' = True, how so?
1) What are those pieces of text called that appear after the "python temp.py", is it arguments?
Yes, the things you pass after temp.py on the shell are called "arguments", or "command line arguments". Sometimes they're called "parameters" instead, sometimes with the word "actual" as a prefix.*
And note that temp.py and all of its arguments are themselves arguments of python.
The sys.argv variable that you're using specifically gives you:
The list of command line arguments passed to a Python script…
2) Is it possible to run the code with the so called arguments in the IDE (I am using pyCharm)
Yes. Almost every IDE has a way to do this.
3) If '2)' = True, how so?
Read the PyCharm docs for details, but IIRC, they're part of the "run/debug configuration"; when you use the "Edit Configurations" command and edit one, there's a box labeled "Script Parameters" for you to enter the arguments.
* Using "parameters" is a bit misleading. Speaking technically, using the terminology Python uses for function calling, you should say that your script has a single parameter, a variable-positional parameter which gathers all of the arguments used to call it, which you can access as sys.argv. But as you can guess, nobody actually says things that way. If someone says "command line parameters" or "script parameters", they mean "the things in sys.argv".
The code is like this:
os.execlp('python', 'python', 'child.py', #other args#) # this works
os.execlp('python', 'child.py', #other args#) # this doesn't work
I read this question: execlp() in python
But I'm still confused. The answer said:
The first argument is the program to execute (found on the PATH). The
rest are the sys.argv arguments to the program.
However, if I run: python child.py 1 2 3 and the sys.argv of this process would be ["child.py", "1", "2", "3"], where the python doesn't exist. Then why should I add python as the second parameter of os.execlp?
When python is executed, it creates sys.argv for you. The values in that list are based on the arguments passed to it by the operating system, but it leaves off the sys.executable value from that list.
In other words, when Python is invoked, it sets sys.argv to everything but it's own executable.
When you invoke a new executable via os.execlp(), you still need to include Python in that as that is what executable that the OS will run. The first two values of what you a pass to os.execlp() are still required, whatever you find in sys.argv later on.
The second python is a name for python, it can be any string, but it has to be there.
See the second paragraph of http://docs.python.org/3/library/os.html?highlight=os.exec#process-management:
The various exec* functions take a list of arguments for the new program loaded into the process. In each case, the first of these arguments is passed to the new program as its own name rather than as an argument a user may have typed on a command line. For the C programmer, this is the argv[0] passed to a program’s main(). For example, os.execv('/bin/echo', ['foo', 'bar']) will only print bar on standard output; foo will seem to be ignored.
I realize this was answered LONG ago and the answer is basically right, but there are a few things that are misleading in the way it is worded and in the comments to the answer that I would like to address.
First, I think the clearer way to state what is happening is to highlight that the difference is between the Unix argv list that a process gets handed by the OS and the python sys.argv. The python sys.argv is the Unix argv list with the first element (the command name) removed.
The various os.exec* commands use their first argument to be the actual executable to invoke and the remainder of the line is the Unix argv list, which means that the second argument passed to execlp will be interpreted by the executable as the command line name it was invoked as.
Which takes us to the problem with the comment. The reason that the ls example os.execlp('ls','.') "works" is not because ls does anything special to detect it is called with too few arguments. This example code starts the 'ls' executable with the unix argv list being ['.']. That just means that the ls executable gets started while being told (oddly) that it was invoked as '.', and there are no other command line arguments. And what does ls do when it is run with no other command line arguments: it prints the contents of the current directory, or exactly what one mistakenly thought they were doing when the invoked os.execlp('ls', '.').
You can see that this example really isn't "working" by instead trying os.execlp('ls', '/some/non-existant/path'). That also prints out the contents of the current working directory, and would not be mistaken for "working".