Movatterモバイル変換


[0]ホーム

URL:


Following system colour schemeSelected dark colour schemeSelected light colour scheme

Python Enhancement Proposals

PEP 822 – Dedented Multiline String (d-string)

PEP 822 – Dedented Multiline String (d-string)

Author:
Inada Naoki <songofacandy at gmail.com>
Discussions-To:
Discourse thread
Status:
Draft
Type:
Standards Track
Created:
05-Jan-2026
Python-Version:
3.15
Post-History:
05-Jan-2026

Table of Contents

Abstract

This PEP proposes to add a feature that automatically removes indentation frommultiline string literals.

Dedented multiline strings use a new prefix “d” (shorthand for “dedent”) beforethe opening quote of a multiline string literal.

Example (spaces are visualized as.):

defhello_paragraph()->str:....returnd"""........<p>..........Hello, World!........</p>...."""

Unliketextwrap.dedent(), indentation before closing quotes is alsoconsidered when determining the amount of indentation to be removed.Therefore, the string returned in the example above consists of the followingthree lines.

  • "....<p>\n"
  • "......Hello,World!\n"
  • "....</p>\n"

Motivation

When writing multiline string literals within deeply indented Python code,users are faced with the following choices:

  • Write the contents of the string without indentation.
  • Use multiple single-line string literals concatenated together instead ofa multiline string literal.
  • Usetextwrap.dedent() to remove indentation.

All of these options have drawbacks in terms of code readability andmaintainability.

  • Writing multiline strings without indentation in deeply indented codelooks awkward and tends to be avoided.In practice, many places including Python’s own test code choose othermethods.
  • Concatenated single-line string literals are more verbose and harder tomaintain. Writing"\n" at the end of each line is tedious.It’s easy to miss the semicolons between many string concatenations.
  • textwrap.dedent() is implemented in Python so it requires some runtimeoverhead. Moreover, it cannot be used to dedent t-strings.

This PEP aims to provide a built-in syntax for dedented multiline strings thatis both easy to read and write, while also being efficient at runtime.

Rationale

The main alternative to this idea is to implementtextwrap.dedent() in Cand provide it as astr.dedent() method.This idea reduces the runtime overhead oftextwrap.dedent().By making it a built-in method, it also allows for compile-time dedentationwhen called directly on string literals.

However, this approach has several drawbacks:

  • To support cases where users want to include some indentation in the string,thededent() method would need to accept an argument specifyingthe amount of indentation to remove.This would be cumbersome and error-prone for users.
  • When continuation lines (lines after line ends with a backslash) are used,they cannot be dedented.
  • f-strings may interpolate expressions as multiline string without indent.In such case, f-string +str.dedent() cannot dedent the whole string.
  • t-strings do not createstr objects, so they cannot use thestr.dedent() method.While adding adedent() method tostring.templatelib.Template is anoption, it would lead to inconsistency since t-strings and f-strings are verysimilar but would have different behaviors regarding dedentation.

Thestr.dedent() method can still be useful for non-literal strings,so this PEP does not preclude that idea.However, for ease of use with multiline string literals, providing dedicatedsyntax is superior.

Specification

Add a new string literal prefix “d” for dedented multiline strings.This prefix can be combined with “f”, “t”, “r”, and “b” prefixes.

This prefix is only for multiline string literals.So it can only be used with triple quotes (""" or''').

Opening triple quotes needs to be followed by a newline character.This newline is not included in the resulting string.The content of the d-string starts from the next line.

Indentation is leading whitespace characters (spaces and tabs) of each line.

The amount of indentation to be removed is determined by the longest commonindentation of lines in the string.Lines consisting entirely of whitespace characters are ignored whendetermining the common indentation, except for the line containing the closingtriple quotes.

Spaces and tabs are treated as different characters.For example,"hello" and"\thello" have no common indentation.

The dedentation process removes the determined indentation from every line inthe string.

  • Lines that are longer than or equal in length to the determined indentationmust start with the determined indentation.Othrerwise, Python raises anIndentationError.The determined indentation is removed from these lines.
  • Lines that are shorter than the determined indentation (includingempty lines) must be a prefix of the determined indentation.Otherwise, Python raises anIndentationError.These lines become empty lines.

Unless combined with the “r” prefix, backslash escapes are processed afterthe dedentation process.So you cannot use\\t in indentations.And you can use line continuation (backslash at the end of line) and removeindentation from the continued line.

Examples:

# d-string must starts with a newline.s=d""# SyntaxError: d-string must be triple-quoteds=d""""""# SyntaxError: d-string must start with a newlines=d"""Hello"""# SyntaxError: d-string must start with a newlines=d"""Hello..World!"""# SyntaxError: d-string must start with a newline# d-string removes the longest common indentation from each line.# Empty lines are ignored, but closing quotes line is always considered.s=d"""..Hello..World!.."""print(repr(s))# 'Hello\nWorld!\n's=d"""..Hello..World!."""print(repr(s))# '.Hello\n.World!\n's=d"""..Hello..World!"""print(repr(s))# '..Hello\n..World!\n's=d"""..Hello...World!..."""# Longest common indentation is '..'.print(repr(s))# 'Hello\n\n\nWorld!\n.'# Closing qutotes can be on the same line as the last content line.# In this case, the string does not end with a newline.s=d"""..Hello..World!"""print(repr(s))# 'Hello\nWorld!'# Tabs are allowed as indentation.# But tabs and spaces are treated as different characters.s=d"""--->..Hello--->..World!--->"""print(repr(s))# '..Hello\n..World!\n's=d"""--->Hello..World!.."""# There is no common indentation.print(repr(s))# '\tHello\n..World!\n..'# Line continuation with backslash works as usual.# But you cannot put a backslash right after the opening quotes.s=d"""..Hello\..World!\.."""print(repr(s))# 'Hello World!'s=d"""\..Hello..World.."""# SyntaxError: d-string must starts with a newline.# d-string can be combined with r-string, b-string, f-string, and t-string.s=dr"""..Hello\..World!\.."""print(repr(s))# 'Hello\\\nWorld!\\\n's=db"""..Hello..World!.."""print(repr(s))# b'Hello\nWorld!\n's=df"""....Hello, {"world".title()}!...."""print(repr(s))# 'Hello,.World!\n's=dt"""....Hello, {"world".title()}!...."""print(type(s))# <class 'string.templatelib.Template'>print(s.strings)# ('Hello, ', '!\n')print(s.values)# ('World',)

How to Teach This

The main difference betweentextwrap.dedent("""...""") and d-string can beexplained as follows:

  • textwrap.dedent() is a regular function, but d-string is part of thelanguage syntax. d-string has no runtime overhead, and it can removeindentation from t-strings.
  • When usingtextwrap.dedent(), you need to start with"""\ to avoidincluding the first newline character, but with d-string, the string contentstarts from the line afterd""", so no backslash is needed.
    importtextwraps1=textwrap.dedent("""\    Hello    World!    """)s2=d"""    Hello    World!    """asserts1==s2
  • textwrap.dedent() ignores all blank lines when determining the commonindentation, but d-string also considers the indentation of the closingquotes.This allows d-string to preserve some indentation in the result when needed.
    importtextwraps1=textwrap.dedent("""\      Hello      World!    """)s2=d"""      Hello      World!    """asserts1!=s2asserts1=='Hello\nWorld!\n'asserts2=='  Hello\n  World!\n'
  • Since d-string removes indentation before processing escape sequences,when using line continuation (backslash at the end of a line), the next linecan also be dedented.
    importtextwraps1=textwrap.dedent("""\    Lorem ipsum dolor sit amet, consectetur adipiscing elit,\sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.    Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris\nisi ut aliquip ex ea commodo consequat.    """)s2=d"""    Lorem ipsum dolor sit amet, consectetur adipiscing elit,\    sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.    Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris\    nisi ut aliquip ex ea commodo consequat.    """asserts1==s2

Other Languages having Similar Features

Java 15 introduced a feature calledtext blocks.Since Java had not used triple qutes before, they introduced triple quotes formultiline string literals with automatic indent removal.

C# 11 also introduced a similar feature calledraw string literals.

Julia andSwiftalso support triple-quoted string literals that automatically remove indentation.

PHP 7.3 introducedFlexible Heredoc and Nowdoc SyntaxesAlthough it uses closing marker (e.g.<<<END...END) instead oftriple quote, it removes indent from text too.

Ruby also has“squiggly” heredocthat removes indent from lines in heredoc.

Java, Julia, and Ruby uses the least-indented line to determine the amount ofindentation to be removed.Swift, C#, and PHP uses the indentation of the closing triple quotes orclosing marker.

Reference Implementation

A CPython implementation of PEP 822 is available atmethane/cpython#108.

Rejected Ideas

str.dedent() method

As mentioned in the Rationale section, this PEP doesn’t reject the idea of astr.dedent() method.A faster version oftextwrap.dedent() implemented in C would be useful forruntime dedentation.

However, d-string is more suitable for multiline string literals because:

  • It works well with f/t-strings.
  • It allows specifying the amount of indentation to be removed more easily.
  • It can dedent continuation lines.

Triple-backtick

It is considered thatusing triple backticksfor dedented multiline strings could be an alternative syntax.This notation is familiar to us from Markdown. While there were past concernsabout certain keyboard layouts,nowadays many people are accustomed to typing this notation.

However, this notation conflicts when embedding Python code within Markdown orvice versa.Therefore, considering these drawbacks, increasing the variety of quotecharacters is not seen as a superior idea compared to adding a prefix tostring literals.

__future__ import

Instead of adding a prefix to string literals, the idea of using a__future__ import to change the default behavior of multilinestring literals was also considered.This could help simplify Python’s grammar in the future.

But rewriting all existing complex codebases to the new notation may not bestraightforward.Until all multiline strings in that source code are rewritten tothe new notation, automatic dedentation cannot be utilized.

Until all users can rewrite existing codebases to the new notation,two types of Python syntax will coexist indefinitely.Therefore,many people preferred the new string prefixover the__future__ import.

Removing newline in the last line

Another idea considered was to remove the newline character from the last line.This idea is same to Swift’s multiline string literals.

With this idea, user can write multiline string having indent without trailingnewline like below:

s=d"""    Hello    World!  """# "  Hello\n  World!" (no trailing newline)s=d"""    Hello    World!  """# "  Hello\n  World!\n" (has a trailing newline)

However, including a newline at the end of the last line of a multiline stringliteral is a very common case, and requiring an empty line at the end wouldlook quite unnatural compared to Python’s traditional multiline string literals.

When usingtextwrap.dedent("""..."""), in many cases users had to write abackslash right after the opening quote, which was frustrating.Therefore, not including the newline after the opening quote in d-strings isa clear improvement for users.On the other hand, removing the newline at the end of the line before theclosing quote would likely cause confusion when rewriting code that usestextwrap.dedent("""...""").

Without this idea, if you don’t need remaining indentation but want to avoid atrailing newline, you can put the closing quotes on the same line as the lastcontent line.And if you need to retain some indentation without a trailing newline,you can use workarounds such as line continuation orstr.rstrip().

s=d"""    Hello    World!"""asserts=="Hello\nWorld!"s=d"""    Hello    World!\  """asserts=="  Hello\n  World!"s=dr"""    Hello    World!  """.rstrip()asserts=="  Hello\n  World!"

While these workarounds are not ideal, the drawbacks are considered smallerthan the confusion that would result from automatically removing the trailingnewline.

Sincetextwrap.dedent() does not consider the indentation of the closingquotes, these workarounds are not necessary when rewritingtextwrap.dedent()to d-strings.

Copyright

This document is placed in the public domain or under theCC0-1.0-Universal license, whichever is more permissive.


Source:https://github.com/python/peps/blob/main/peps/pep-0822.rst

Last modified:2026-01-21 14:56:13 GMT


[8]ページ先頭

©2009-2026 Movatter.jp