Emacs: pass arguments to inferior Python shell during buffer evaluation - python

recently I started using Emacs as a Python IDE, and it not quite intuitive... The problem I am struggling with right now is how to pass command line arguments to the inferior python shell when the buffer is evaluated with C-c C-c. Thanks for help.

This doesn't appear to be easily possible; the inferior process managed by the python.el module is designed to persist across many invocations of python-send-buffer (and friends). One solution I've found is to write your own function that sets sys.argv programmatically from within the inferior process:
(defun python-send-buffer-with-my-args (args)
(interactive "sPython arguments: ")
(let ((source-buffer (current-buffer)))
(with-temp-buffer
(insert "import sys; sys.argv = '''" args "'''.split()\n")
(insert-buffer-substring source-buffer)
(python-send-buffer))))
Execute this function in your *scratch* buffer and/or save it in your .emacs file, then, if you want, bind it to a convenient key sequence. C-c C-a doesn't seem to be used by python-mode, so perhaps:
(global-set-key "\C-c\C-a" 'python-send-buffer-with-my-args)
The command will prompt you for arguments to use, then copy your source buffer into a temporary buffer, prepending it with a code snippet that sets sys.argv to the list of arguments you supplied, and finally will call python-send-buffer.
The above code will just naively split the string you type on whitespace, so if you need to supply arguments that have whitespace in them, you'll need a more sophisticated algorithm.

Related

In python, how to pass an array argument to powershell script

I have a PowerShell script, which has two parameters, the first one is a string, the second one is an array of string.
I would like to call this PowerShell script from my python code. How to pass the array type parameter to PowerShell?
If I write something like this:
subprocess.run(['powershell.exe', 'script.ps1', 'arg1', '#("str1", "str2")'])
Powershell think '#("str1", "str2")' is a string, not an array.
Edit
I found a workaround
subprocess.run(['powershell.exe', 'script.ps1 arg1 #("str1", "str2")'])
It doesn't look beautiful, but works. and in this way, I can't use -File after powershell.exe
Your original command does work as written (except that you must use .\script.ps1 rather than script.ps1, unless the script is in the system's path), as does the second one you added later, because it implicitly uses the PowerShell CLI's -Command parameter rather than its
-File parameter.
In short:
Passing arrays is fundamentally only supported with -Command, which interprets the subsequent arguments as PowerShell code, where the usual PowerShell syntax applies.
With -File, by contrast, all arguments after the target-script argument are passed verbatim, as strings, so there is no concept of an array.
I suggest using the following approach, for increased robustness and conceptual clarity:
subprocess.run(['powershell.exe', '-noprofile', '-c', '.\script.ps1 arg1 #("str1", "str2")'])
Note: You can omit #(...) around the array elements - #() is never needed for array literals in PowerShell.
Note:
-noprofile ensures that PowerShell doesn't load the $PROFILE file(s), which avoids potential slow-downs and side effects.
-c (-Command) makes it explicit that you're passing PowerShell code rather than a script file with literal arguments (-File)
Do note that -Command arguments are subject to additional interpretation by PowerShell, so if you pass, say, a token $foo$ you intend to be a literal, PowerShell will expand it to just $ (if no $foo variable is defined), because it expands $foo as a variable reference; passing `$foo`$ (backtick-escaping) prevents that.
Note the .\ before script.ps1: Since you're using -Command you cannot execute a script by file name only (unless the script happens to be located in a directory listed in $env:PATH); as from inside PowerShell, executing scripts from the current directory requires .\ for security reasons; by contrast, file-name-only invocation does work with -File.
The script file as well as its arguments are passed as a single argument, which reflects how PowerShell will process the command.
-Command is the default in Windows PowerShell, but no longer in PowerShell Core (pwsh.exe), which defaults to -File; it is generally a good idea to explicitly use -Command (-c) or -File (-f) to make it obvious how PowerShell will interpret the arguments.
How subprocess.run() builds the command line and how PowerShell parses it:
Your original Python command passes #("str1", "str2") as an individual argument to subprocess.run():
subprocess.run(['powershell.exe', '.\script.ps1', 'arg1', '#("str1", "str2")'])
This results in the following command line executed behind the scenes:
powershell.exe .\script.ps1 arg1 "#(\"str1\", \"str2\")"
Note how only #("str1", "str2") is double-quoted, and how the embedded " chars. are escaped as \".
As an aside: PowerShell's CLI (arguments passed to powershell.exe) uses the customary \-escaping of literal " chars.; inside PowerShell, however, it is ` (backtick) that serves as the escape character.
Your second command combines the script.ps1 and #("str1", "str2") into a single argument:
subprocess.run(['powershell.exe', '.\script.ps1 arg1 #("str1", "str2")'])
This results in the following command line:
powershell.exe ".\script.ps1 arg1 #(\"str1\", \"str2\")"
Note how the single argument passed is double-quoted as a whole.
Generally, subprocess.run() automatically encloses a given argument in "..." (double quotes) if it contains spaces.
Independently, it escapes embedded (literal) " chars. as \".
Even though these command lines are obviously different, PowerShell's (implied) -Command logic processes them the same, because it uses the following algorithm:
First, enclosing double quotes around each argument, if present, are removed.
The resulting strings, if there are multiple, are concatenated with spaces.
The resulting single string is then executed as PowerShell code.
If you apply this algorithm to either of the above command lines, PowerShell ends up executing the same code, namely:
.\script.ps1 arg1 #("str1", "str2")
Lets say your python array is arr
try to do this:
subprocess.run(['powershell.exe', 'script.ps1', 'arg1', '\"{}\"'.format(','.join(arr))])
To send array in powershell script you can send it as "item1,item2,item3"
and the function str.join allow you to get this format easly
If this doesn't work, i would try to edit the script to use the $args argument in the powershell script to change the way you using your arguments
You can use single quotes on the command line - e.g. #('str1', 'str2') or escape the double quotes with backslashes - e.g. #(\"str1\", \"str2\")
For example with this script:
script.ps1
param( [string[]] $s )
write-host $s.GetType().FullName
write-host $s.Length
write-host ($s | fl * | out-string)
You can call it from a command prompt like this:
C:\> powershell.exe .\script.ps1 #('str1', 'str2')
System.String[]
2
str1
str2
or like this:
C:> powershell.exe .\script.ps1 #(\"str1\", \"str2\")
System.String[]
2
str1
str2
You might need to apply some python escape characters to get the desired result in your code though.

Difference between os.execl() and os.execv() in python

Is there a difference between os.execl() and os.execv() in python? I was using
os.execl(python, python, *sys.argv)
to restart my script (from here). But it seems to start from where the previous script left.
I want the script to start from the beginning when it restarts. Will this
os.execv(__file__,sys.argv)
do the job? command and idea from here. I couldn't find difference between them from the python help/documentation. Is there a way do clean restart?
For a little more background on what I am trying to do please see my other question
At the low level they do the same thing: they replace the running process image with a new process.
The only difference between execv and execl is the way they take arguments. execv expects a single list of arguments (the first of which should be the name of the executable), while execl expects a variable list of arguments.
Thus, in essence, execv(file, args) is exactly equivalent to execl(file, *args).
Note that sys.argv[0] is already the script name. However, this is the script name as passed into Python, and may not be the actual script name that the program is running under. To be correct and safe, your argument list passed to exec* should be
['python', __file__] + sys.argv[1:]
I have just tested a restart script with the following:
os.execl(sys.executable, 'python', __file__, *sys.argv[1:])
and this works fine. Be sure you're not ignoring or silently catching any errors from execl - if it fails to execute, you'll end up "continuing where you left off".
According to the Python documentation there's no real functional difference between execv and execl:
The “l” and “v” variants of the exec* functions differ in how command-line arguments are passed. The “l” variants are perhaps the easiest to work with if the number of parameters is fixed when the code is written; the individual parameters simply become additional parameters to the execl*() functions. The “v” variants are good when the number of parameters is variable, with the arguments being passed in a list or tuple as the args parameter. In either case, the arguments to the child process should start with the name of the command being run, but this is not enforced.
No idea why one seems to restart the script where it left off but I'd guess that that is unrelated.

How to use subprocess to fire and forget in python 2.7

I need to execute a command line in the bakground in python 2.7. I need to fire and forget.
Here is the command:
cmd = "/usr/local/bin/fab -H %s aws_bootstrap initial_chef_run:%s,%s,%s -w" % (...)
How do I use the subproccess module?
e.g. is it
subprocess.call([cmd])
or
subprocess.call(["/usr/local/bin/fab", "-H %s aws_bootstrap initial_chef_run:%s,%s,%s -w"])
I dont get how to use the list. Or is every element of the list what would be a white space.
Thanks
each thing that would be seperated by whitespace is a seperate entity of the list
subprocess.call is blocking however
subprocess.popen is non-blocking
cmd = ["/usr/local/bin/fab", "-H",var1,"aws_bootstrap initial_chef_run:%s,%s,%s"%(var2,var3,var4), "-w"]
subprocess.popen(cmd) # dopnt wait just keep going
#or
subprocess.call(cmd) # wait until the command returns
you may however alternatively pass the command as one big string
cmd = "/usr/local/bin/fab -H %s aws_bootstrap initial_chef_run:%s,%s,%s -w" % (...)
subprocess.call(cmd)
in general this method(passing a single string) is frowned upon for some reason that has never been explained sufficiently to me
I used this recently to fire a perl script, like so:
var = "C:\Users\user\Desktop"
retcode = subprocess.call(["perl", '.\hgncDL.pl',var])
Working code
Define hParam and runParams in following code and you're good to go:
hParam = 'hParam'
runParams = (a,b,c)
args = ('/usr/local/bin/fab', '-H', hParam, 'aws_bootstrap', 'initial_chef_run:%s,%s,%s' % runParams, '-w')
subprocess.Popen(args)
Details
How do I use <any python module> module?
https://docs.python.org is a good starting point.
In particular, docs for subprocess module available here.
I can't provide direct links for each case later in this answer due to restriction imposed by low reputation. Each time I will be referring to 'docs', look for a section in docs on the module.
I need to execute a command line in the background in python 2.7. I need to fire and forget
Consider subprocess.Popen(args). Note capital 'P'.
See docs for more details.
subprocess.call(args) works in similar way, but it would block until the command completes. As stated in docs:
Run the command described by args. Wait for command to complete, then return the returncode attribute.
How to use the sequence form of args parameter?
This is covered in "Frequently used arguments" section of docs:
args is required for all calls and should be a string, or a sequence of program arguments. Providing a sequence of arguments is generally preferred, as it allows the module to take care of any required escaping and quoting of arguments (e.g. to permit spaces in file names).
Also, passing an args in a string form has its limitation:
If passing a single string, either shell must be True or else the string must simply name the program to be executed without specifying any arguments.
Despite mentioned limitation, subprocess.Popen('cmd.exe /?') works for me. Win7, Python 2.7.8 64bit.
HTH, cheers.

Advantage of list over string in subprocess methods

What are the advantages of using list over string in subprocess methods? The ones I understand so far:
Security if input comes from external sources
Portability over different operating systems
Are there any others?
In my particular case, I'm using subprocess library to run tests on a software. Input does not come from external source. Tests are run only on Linux. Therefore, I see no benefit of lists over strings.
On POSIX, list and string arguments have different meaning and are used in different contexts.
You use a string argument and shell=True to run a shell command e.g.:
from subprocess import check_output
output = check_output("dmesg | grep hda", shell=True)
A list argument is used to run a command without the shell e.g.:
from subprocess import check_call
check_call(["ls", "-l"])
One exception is that call("ls") is equivalent to call(["ls"]) (a command with no arguments).
You should use a list argument with shell=False (default) except in those cases when you need the shell so the string argument is used.
It is almost always an error to use a list argument and shell=True (the arguments are interpreted as arguments to the shell itself instead of the command in this case). Don't use it.
If your question: what are the advantages of shell=False and hence the list argument over a string argument:
you don't need to escape the arguments, no shell interpolation such as word splitting, parameter expansion, command substitution occurs: what you see is what you get
support for arguments with spaces
support for arguments with special characters such as quotes, dollar sign, etc
it is clear where arguments boundaries are. They are explicitely separated.
it is clear what program is executed: it is the first item in the list
an argument that is populated from an untrusted source won't be able to execute arbitrary commands
why run a superfluous shell process unless you need it
Sometimes, it might be more convenient/readable to specify an argument as a string in the source code; shlex.split() could be used to convert it to a list:
import shlex
from subprocess import check_call
cmd = shlex.split('/bin/vikings -input eggs.txt -output "spam spam.txt" '
'''-cmd "echo '$MONEY'"''')
check_call(cmd)
See the docs.
On Windows, the arguments are interpreted differently. The native format is a string and the passed list is converted to a string using subprocess.list2cmdline() function that may not work for all Windows programs. shell=True is only necessary to run builtin shell commands.
If list2cmdline() creates a correct command line for your executable (different programs may use different rules for interpreting the command line) then a list argument could be used for portability and to avoid escaping separate arguments manually.

space in python subprocess.Popen() arguments

I try to invoke an external perl script in my python script. I used subprocess.Popen(). If run it like
subprocess.Popen([mybinary, '-arg1 argv1', '-arg2 argv2'])
the arguments are not sent to mybinary. But if I separate arguments from values, then it runs properly:
subprocess.Popen([mybinary, '-arg1', 'argv1', '-arg2', 'argv2'])
Why is it so? args needs to be a string or list. If I concatenate mybinary and the arguments into a single string for Popen(), Popen() does not work, either. I suspect it is relevant to the key-worded arguments (**kwargs). But the script invoked is external. I don't see the reason.
I try to invoke an external perl script in my python script. I used
subprocess.Popen(). If run it like
subprocess.Popen([mybinary, '-arg1 argv1', '-arg2 argv2'])
the arguments are not sent to mybinary.
I doubt that. I rather think they come along there in a non-proper way.
The script expects options and their values as two separate arguments. If they are combined, it doesn't work.
The arguments are passed in shape and count exactly as you give them in the list.

Categories