From: R (Chandra) Chandrasekhar on
Dear Folks,

I want to execute a command from within python using the subprocess module.

Coming from a Perl background, I thought I could use variable
interpolation in strings, but found that this is neither supported nor
the Python way. Accordingly, I am a little at sea about how to
accomplish it.

I have stated what I am trying to do in the minimal example below:

---
import subprocess

width = 5
height = 30
colors = ['#abcdef]', '#456789']
filename = "/tmp/image.png"

# I want to get the equivalent of variable interpolation in Perl
# so that the command
#
# convert -size 5x30 gradient:#abcdef-#456789 /tmp/image.png
#
# is derived from the variables above
# and executed by subprocess.call() or subprocess.Popen()
# from within Python
#
# Note that the command "convert" is from the ImageMagick suite
# It exists and is executable by the shell;
# the remaining values are arguments.
# The command has been confirmed to execute correctly.
---

Thanks in advance.

Chandra
From: Alf P. Steinbach on
* R (Chandra) Chandrasekhar:
>
> width = 5
> height = 30
> colors = ['#abcdef]', '#456789']
> filename = "/tmp/image.png"
>
> # I want to get the equivalent of variable interpolation in Perl
> # so that the command
> #
> # convert -size 5x30 gradient:#abcdef-#456789 /tmp/image.png
> #
> # is derived from the variables above

Assuming that the extra right square bracket in 'colors' is a typo, here's one way:

s = "convert -size {w}x{h} gradient:{g1}-{g2} {f}".format(
w = width, h = height, g1 = colors[0], g2 = colors[1], f = filename
)


Cheers & hth.,

- ALf
From: Peter Otten on
R (Chandra) Chandrasekhar wrote:

> I want to execute a command from within python using the subprocess
> module.
>
> Coming from a Perl background, I thought I could use variable
> interpolation in strings, but found that this is neither supported nor
> the Python way. Accordingly, I am a little at sea about how to
> accomplish it.

import subprocess

def convert(width=5, height=30, colors=['#abcdef', '#456789'],
filename="tmp/image with space in its name.png"):
lookup = locals()
assert all("\n" not in str(s) for s in lookup.values())
subprocess.call("""\
convert
-size
{width}x{height}
gradient:{colors[0]}-{colors[1]}
{filename}""".format(**lookup).splitlines())

convert()

Peter
From: R (Chandra) Chandrasekhar on
Peter Otten wrote:

> import subprocess
>
> def convert(width=5, height=30, colors=['#abcdef', '#456789'],
> filename="tmp/image with space in its name.png"):
> lookup = locals()
> assert all("\n" not in str(s) for s in lookup.values())
> subprocess.call("""\
> convert
> -size
> {width}x{height}
> gradient:{colors[0]}-{colors[1]}
> {filename}""".format(**lookup).splitlines())
>
> convert()
>
> Peter

Thank you. It works. Now I need to understand why and am able to follow
what you are doing part of the way:

1. Assign default values in the function definition.

2. Store the variables existing in the local namespace in the list lookup.

3. Assert that there are no newlines in the values in lookup converted
to strings. (Why? Is this because of splitlines() later on?)

4. Assemble a string (array?) for the subprocess.call argument using the
format string syntax (section 8.1.3 et seq. of the Python
documentation for 2.6.4). Your example works with default option of
shell=False for subprocess.call().

5. I am unable to decipher how you got to format(**lookup).splitlines())
especially the **prefix part, although I guess that splitlines() is
dishing out the arguments one by one from each line in the
subprocess.call argument.

Any correction of (1) to (4) and an explanation of (5) would be most
appreciated.

All in all, your code is a magnificent example of wrapping the function
within python. This is the first time I have seen something like this.

Thank you very much.

Chandra
From: John Posner on
On 2/15/2010 7:35 AM, R (Chandra) Chandrasekhar wrote:
> Dear Folks,
>
> I want to execute a command from within python using the subprocess module.
>
> Coming from a Perl background, I thought I could use variable
> interpolation in strings, but found that this is neither supported

Yes, it is: see the use of string.Template below.

> ... nor
> the Python way.

That right -- it isn't the Python way. Python offers two basic
alternatives. Alf already presented the use of the "new" format() method
of string objects. I think "traditional" Python string formatting might
be what you want in this case:

colors = ['#abcdef', '#456789']
format_string = "convert -size 5x30 gradient:%s-%s /tmp/image.png"
cmd_string = format_string % tuple(colors)

.... or better, by making *colors* a tuple instead of a list ...

colors = ('#abcdef', '#456789')
format_string = "convert -size 5x30 gradient:%s-%s /tmp/image.png"
cmd_string = format_string % colors

As Peter demonstrated, you need to use split() on *cmd_string* when you
send the command to subprocess.call().

Now if you *really* miss using Perl, try this:

c1, c2 = ('#abcdef', '#456789')
template = string.Template(
"convert -size 5x30 gradient:$c1-$c2 /tmp/image.png")
cmd_string = template.substitute(locals())

Not worth the trouble, IMHO.

Refs:

http://www.python.org/doc/2.5.2/lib/typesseq-strings.html
http://www.python.org/doc/2.5.2/lib/node40.html

HTH,
John