Using textwrap.dedent() with bytes in Python 3
<p>When I use a triple-quoted multiline string in Python, I tend to use textwrap.dedent to keep the code readable, with good indentation:</p>

<pre><code>some_string = textwrap.dedent("""
    First line
    Second line
    ...
    """).strip()
</code></pre>

<p>However, in Python 3.x, textwrap.dedent doesn't seem to work with byte strings. I encountered this while writing a unit test for a method that returned a long multiline byte string, for example:</p>

<pre><code># The function to be tested

def some_function():
    return b'Lorem ipsum dolor sit amet\n  consectetuer adipiscing elit'

# Unit test

import unittest
import textwrap

class SomeTest(unittest.TestCase):
    def test_some_function(self):
        self.assertEqual(some_function(), textwrap.dedent(b"""
            Lorem ipsum dolor sit amet
              consectetuer adipiscing elit
            """).strip())

if __name__ == '__main__':
    unittest.main()
</code></pre>

<p>In Python 2.7.10 the above code works fine, but in Python 3.4.3 it fails:</p>

<pre class="lang-none prettyprint-override"><code>E
======================================================================
ERROR: test_some_function (__main__.SomeTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test.py", line 16, in test_some_function
    """).strip())
  File "/usr/lib64/python3.4/textwrap.py", line 416, in dedent
    text = _whitespace_only_re.sub('', text)
TypeError: can't use a string pattern on a bytes-like object

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (errors=1)
</code></pre>

<p>So: <strong>Is there an alternative to textwrap.dedent that works with byte strings?</strong></p>

<ul>
<li>I could write such a function myself, but if there is an existing function, I'd prefer to use it.</li>
<li>I could convert to unicode, use textwrap.dedent, and convert back to bytes. But this is only viable if the byte string conforms to some Unicode encoding.</li>
</ul>

<p>Answer 1: Triple-quoted multiline strings (and dedent) are a convenience (sometimes), not a necessity.  You can instead write a separate bytes literal ending with b'\n' for each line and let the parser join them.  Example:</p>

<pre><code>&gt;&gt;&gt; b = (
    b'Lorem ipsum dolor sit amet\n' # first line
    b'consectetuer adipiscing elit\n' # 2nd line
    )
&gt;&gt;&gt; b
b'Lorem ipsum dolor sit amet\nconsectetuer adipiscing elit\n'
</code></pre>

<p>I intentionally added whitespace and comments to the code that are not wanted in the resulting bytes, to should that they are not included.  I sometimes do the equivalent of the above with text strings.</p>

<p>Answer 2: Convert textwrap.dedent to process byte (see separate answer)</p>

<p>Answer 3: Omit the <code>b</code> prefix and add <code>.encode()</code> before or after <code>.strip()</code>.</p>

<pre><code>print(textwrap.dedent("""
            Lorem ipsum dolor sit amet
              consectetuer adipiscing elit
            """).encode())
# prints (same as Answer 2).
b'\nLorem ipsum dolor sit amet\n  consectetuer adipiscing elit\n'
</code></pre>

python python-3.x indentation literals python-unicode