Skip to main content

Basically textwrap.dedent with t-string support.

Project description

better dedent

PyPI - Version PyPI - Python Version


It's like textwrap.dedent, but with added t-string support.

The problem: interpolating before dedenting

Have you ever used textwrap.dedent with an f-string that has newline characters in a replacement field?

For example, given this code string (which has newlines in it):

code = r"""
def strip_each(lines):
    new_lines = []
    for line in lines:
        new_lines.append(line.rstrip("\n"))
    return new_lines
""".strip("\n")

Using textwrap.dedent with an f-string that uses code in a replacement field results in very strange indentation:

>>> print(dedent(f"""\
...     Example function:
...         {code}
...
...     That function was NOT indented properly!"""))
Example function:
    def strip_each(lines):
new_lines = []
for line in lines:
    new_lines.append(line.rstrip("\n"))
return new_lines


The problem is that f-strings immediately interpolate their replacement fields.

That `code` string is injected into the new string before `dedent` has a chance to even look at the string.
By the time `textwrap.dedent` does its dedenting, the weirdness has already happened.


## The solution: interpolating after dedenting

Passing a t-string to the `better_dedent.dedent` function allows the replacement fields to maintain their original indentation level.

Using the same `code` string as before:

```python
code = r"""
def strip_each(lines):
    new_lines = []
    for line in lines:
        new_lines.append(line.rstrip("\n"))
    return new_lines
""".strip("\n")

The better_dedent.dedent function will dedent the t-string and then inject the replacement field, resulting in much more sensible indentation:

>>> print(dedent(t"""\
...     Example function:
...         {code}
...
...     That function was indented properly!""")
...
Example function:
    def strip_each(lines):
        new_lines = []
        for line in lines:
            new_lines.append(line.rstrip("\n"))
        return new_lines

That function was indented properly!

Using a t-string allows for dedenting the whole string before the replacement fields are inserted and then inserting the replacement fields.

Note that if an f-string is passed to better_dedent.dedent, it will simply delegate to textwrap.dedent.

undent

This package also includes an undent function, which will strip a leading newline (note the lack of \ after t"""):

>>> print(undent(t"""
...     Example function:
...         {code}
...     That function was indented properly!"""))
Example function:
    def strip_each(lines):
        new_lines = []
        for line in lines:
            new_lines.append(line.rstrip("\n"))
        return new_lines
That function was indented properly!

The undent function will also strips a trailing newline by default:

>>> print(undent(t"""
...     Example function:
...         {code}
...     That function was indented properly!
... """))
Example function:
    def strip_each(lines):
        new_lines = []
        for line in lines:
            new_lines.append(line.rstrip("\n"))
        return new_lines
That function was indented properly!
>>> print("Note that there's no blank line above this prompt")
Note that there's no blank line above this prompt

Passing strip_trailing=False to undent will suppress trailing newline removal.

Installation

pip install better-dedent

Or if you have uv installed and you'd like to play with it right now:

uvx --with better-dedent python

You can then import dedent and undent like this:

from better_dedent import dedent, undent

And try them out:

code = r"""
def strip_each(lines):
    new_lines = []
    for line in lines:
        new_lines.append(line.rstrip("\n"))
    return new_lines
""".strip("\n")

undent(t"""
    Here is some example code:

        {code}

    That indentation worked out nicely!
""")

License

better-dedent is distributed under the terms of the MIT license.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

better_dedent-0.0.2.tar.gz (6.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

better_dedent-0.0.2-py3-none-any.whl (4.6 kB view details)

Uploaded Python 3

File details

Details for the file better_dedent-0.0.2.tar.gz.

File metadata

  • Download URL: better_dedent-0.0.2.tar.gz
  • Upload date:
  • Size: 6.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: python-httpx/0.28.1

File hashes

Hashes for better_dedent-0.0.2.tar.gz
Algorithm Hash digest
SHA256 fc9e70b0ac84d018a6d61fbd41dd506dcda11a97d0e9618ef5bfe51941e011c5
MD5 4cb9664da7aa2536b416ed61d72201fb
BLAKE2b-256 960ee4a9f0ad351c255a85149baacc8114321860e1d31942a56d17371a9daf25

See more details on using hashes here.

File details

Details for the file better_dedent-0.0.2-py3-none-any.whl.

File metadata

File hashes

Hashes for better_dedent-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 c2063bc8db1e273297ecb83f4f1b42656cf1db669e3edc225f5e7b62f0ef3c1c
MD5 149c5f94c7417f3c5964b6715d0f26d5
BLAKE2b-256 e4d205608efc3614ae4e4c290bf2dcf991cbb4c5cdd24e8fec8582e5a0a00c68

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page