3

I need to format a command line where some parameters come from simple variables, and others are the result of a longer expression. Python f-strings work well for the variables, and I'm using a printf-style %s and providing the long expression outside the string because it would just clutter the command template too much if placed inline:

run(f"""zstd -dc {diff_file} | 
      git apply -p2 {exclude_args} --directory='%s'""" % (os.getcwd() + "/output"))

I didn't store the directory parameter in a named variable because it only gets used once as opposed to diff_file and exclude_args.

Is there a cleaner way to avoid putting os.getcwd() + "/output" directly in the f-string, and also avoid mixing old printf %s syntax and new f-strings ?

Are there downsides to doing it like above other than it being a bit confusing to someone who reads the code ?

Edit: to clarify, my question is how to put placeholders in f-strings without declaring additional named variables, not how to inline calls to getcwd() in f-strings.

4
  • No need to store it, just use it as {os.getcwd()} Commented Dec 17, 2024 at 17:21
  • Do you actually need an absolute path explicitly constructed from the current working directory, rather than just using "output" by itself? Commented Dec 17, 2024 at 17:27
  • What does your "cluttered if placed inline" version look like? Commented Dec 17, 2024 at 17:30
  • @nocomment: Something like what has been suggested in the answers: run(f"git apply -d '{(os.getcwd()}/output}' -p2"). Commented Dec 17, 2024 at 19:14

3 Answers 3

3

From the docs

Formatted string literals (also called f-strings for short) let you include the value of Python expressions inside a string by prefixing the string with f or F and writing expressions as {expression}.

So

run(f"""zstd -dc {diff_file} | 
      git apply -p2 {exclude_args} --directory='{os.getcwd()}/output'""")

A line continuation must be added If the new line after the pipe is needed

run(f"""zstd -dc {diff_file} | \
      git apply -p2 {exclude_args} --directory='{os.getcwd()}/output'""")

Sign up to request clarification or add additional context in comments.

Comments

3

Don't use string formatting at all.

from pathlib import Path
from subprocess import Popen, PIPE, run

p = Popen(["zstd", "-dc", diff_file], stdout=PIPE)

# exclude_args itself should be a list like ["foo", "bar"] rather than
# a single string like "foo bar"
run(["git", "apply", "-p2", *exclude_args, "--directory", Path.cwd() / "output"], stdin=p.stdout)

Instead of creating a shell pipeline, create one subprocess for the zstd command, then connect its standard output to the standard input of a separate git subprocess.

You probably don't need Path at all; "output" alone as a relative path can be used by git as long as you don't execute git with a working directory different from your Python script's working directory.

Comments

0

I am a little confused, but if I understand the problem correct, then you want to put the % (os.getcwd() + "/output") stuff later, for example the next line.

You can do that:

string = f"""zstd -dc {diff_file} | 
      git apply -p2 {exclude_args} --directory='%s'"""
run(string % (os.getcwd() + "/output"))

There is also a format()-function in the str class which allows us to do the following two examples.

string = f"""zstd -dc {diff_file} | 
      git apply -p2 {exclude_args} --directory='{{0}}'"""
run(string.format((os.getcwd() + "/output")))


string = f"""zstd -dc {diff_file} | 
      git apply -p2 {exclude_args} --directory='{{placeholder}}'"""
run(string.format(placeholder=(os.getcwd() + "/output")))

Cuz I am confused about what you want, the same examples as oneliner:

run(f"""zstd -dc {diff_file} | 
      git apply -p2 {exclude_args} --directory='{{0}}'""".format((os.getcwd() + "/output")))


run(f"""zstd -dc {diff_file} | 
      git apply -p2 {exclude_args} --directory='{{placeholder}}'""".format(placeholder=(os.getcwd() + "/output")))

I hope the desired answer is some where in the post, if not please leave a comment.


For anyone confused what is going on:

str.format allows us to delay the f-string format. So for example we can do:

template = "{0} {user}!"
print(template.format("Hello", user="World"))
# results in "Hello World!")

So why are there doubled curly braces? (--directory='{{0}}') Thought we need only one opening and one closing. This is because f-strings uses the single curly braces for their special stuff. So we need them to escape them, which is done with double them. So f"{{0}}" results in the string "{0}".

1 Comment

Btw I recommend the usage of pathlib.Path for a os-independent way of path concatenating (os.getcwd() + "/output") could also be done as str(pathlib.Path("output").absolute()). This way backslashes are used in windows-systems.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.