I'm trying to pretty-print a HTTP request (that I've mocked here).
from typing import NamedTuple
class RequestMock(NamedTuple):
method = 'POST'
url = 'https://bob.com'
body = 'body1\nbody2'
headers = {'a': '1', 'b': '2'}
I have a function that does this:
req = RequestMock()
def print1(req):
headers = '\n'.join(f'{k}: {v}' for k, v in req.headers.items())
s = '\n'.join([
f'{req.method} {req.url}',
headers,
req.body
])
print(s)
print1(req)
# POST https://bob.com
# a: 1
# b: 2
# body1
# body2
But when I've tried to rewrite it with f-strings
for clarity and ease of modification, I get some bad indents:
# what I want the code to look like
def print2(req):
headers = '\n'.join(f'{k}: {v}' for k, v in req.headers.items())
s = f"""
{req.method} {req.url}
{headers}
{req.body}
"""
print(s)
print2(req)
# POST https://bob.com
# a: 1
# b: 2
# body1
# body2
I know this is because I'm defining strings with newlines and putting them in a triple-quoted string. Is there a simple way to get the output I'm looking with a triple-quoted f-string
defined in a function and without having to know the indentation level of its definition? I've played with textwrap.indent
, textwrap.dedent
, str.lstrip
, re
, etc., but the code stops being simple and pythonic fast. The closest thing I've come up with is the following, but the length is awkward and I feel like I'm repeating myself.
def print3(req):
headers = '\n'.join(f'{k}: {v}' for k, v in req.headers.items())
s = textwrap.dedent("""
{method} {url}
{headers}
{body}
""").strip()
s = s.format(
method=req.method,
url=req.url,
headers=headers,
body=req.body,
)
print(s)
print3(req)
# POST https://bob.com
# a: 1
# b: 2
# body1
# body2
I think you can try to take advantage of implicit string concatenation for a semi-nice looking solution:
def print4(req):
headers = '\n'.join(f'{k}: {v}' for k, v in req.headers.items())
s = (f'{req.method} {req.url}\n'
f'{headers}\n'
f'{req.body}')
print(s)
print4(req)
Output:
POST https://bob.com
a: 1
b: 2
body1
body2
Note that, if you want, you can take out the parentheses and use backslashes:
s = f'{req.method} {req.url}\n' \
f'{headers}\n' \
f'{req.body}'
However, the style guide prefers parentheses over backslashes.
Another option:
def print5(req):
headers = '\n'.join(f'{k}: {v}' for k, v in req.headers.items())
s = f"""
{req.method} {req.url}
{headers}
{req.body}
"""
s = '\n'.join(l.lstrip() for l in s.splitlines())
print(s)
You can fix it with 2 tiny changes:
def print6(req, **w):
headers = '\n'.join(f'{k}: {v}' for k, v in req.headers.items())
method, url, body = \
w['method'], w['url'], w['body']
# < note the changes belowwwwwwwwwwww >
s = '\n'.join(line.lstrip() for line in f"""
{method} {url}
{headers}
{body}
""".split('\n')) # and note this .split('\n') over here
print(s)
print6(req)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With