methane | 2026-01-28 07:35:15 UTC | #1 Happy new year, and new PEP. After several years of long discussion, I wrote up the d-string PEP. https://peps.python.org/pep-0822/ ## Old threads: * https://discuss.python.org/t/30127 * https://discuss.python.org/t/35907 * https://discuss.python.org/t/40679 * https://discuss.python.org/t/90988 ## Abstract This PEP proposes to add a feature that automatically removes indentation from multiline string literals. Dedented multiline strings use a new prefix “d” (shorthand for “dedent”) before the opening quote of a multiline string literal. Example (spaces are visualized as `_`): ```python def hello_paragraph() -> str: ____return d""" ________

__________Hello, World! ________

____""" ``` The closing triple quotes control how much indentation would be removed. In the above example, the returned string will contain three lines: * `"____

\n"` (four leading spaces) * `"______Hello, World!\n"` (six leading spaces) * `"____

\n"` (four leading spaces) ## Motivation When writing multiline string literals within deeply indented Python code, users are faced with the following choices: * Accept that the content of the string literal will be left-aligned. * Use multiple single-line string literals concatenated together instead of a multiline string literal. * Use `textwrap.dedent()` to remove indentation. All of these options have drawbacks in terms of code readability and maintainability. * Left-aligned multiline strings look awkward and tend to be avoided. In practice, many places including Python’s own test code choose other methods. * Concatenated single-line string literals are more verbose and harder to maintain. * `textwrap.dedent()` is implemented in Python so it require some runtime overhead. It cannot be used in hot paths where performance is critical. This PEP aims to provide a built-in syntax for dedented multiline strings that is both easy to read and write, while also being efficient at runtime. ------------------------- tstefan | 2026-01-05 18:17:22 UTC | #2 Like the proposal, however I have one question: [quote="Inada Naoki, post:1, topic:105519, username:methane"] “d” for dedented multiline strings. This prefix can be combined with “f”, “t”, and “r” prefixes. [/quote] I was wondering why it cannot be combined with “b”. I think dedentation and the bytes data type are orthogonal to each other (from a design perspective, maybe not from the implementation perspective). ------------------------- dg-pb | 2026-01-06 01:27:31 UTC | #3 [quote="Inada Naoki, post:1, topic:105519, username:methane"] ``` def hello_paragraph() -> str: ____return d""" ________

__________Hello, World! ________

____""" ``` [/quote] So to get: ``` "

\n" "__Hello, World!\n" "

\n" ``` I would need to write: ``` def hello_paragraph() -> str: ____return d""" ____

______Hello, World! ____

""" ``` ? ------------------------- blhsing | 2026-01-06 02:22:59 UTC | #4 Thanks for putting together the PEP! [quote="Inada Naoki, post:1, topic:105519, username:methane"] ## Rationale * f-strings may interpolate expressions as multiline string without indent. In such case, f-string + `str.dedent()` cannot dedent the whole string. [/quote] This is one of the key advantages of a d-string versus the status quo, so the specs should go into details of exactly how it works. Specifically, the question I raised in https://discuss.python.org/t/pre-pep-d-string-dedented-multiline-strings-with-optional-language-hinting/90988/98 regarding trailing newlines in evaluations from `{...}` should be addressed. Namely, given: ``` text = d''' Hello World! ''' paragraph = df'''

__{text}

''' ``` where `text` becomes `'Hello\nWorld!\n'`, with a trailing newline, should `paragraph` preserve the trailing newline to become `'

\n__Hello\n__World!\n\n

'`, or should it automatically remove the trailing newline to avoid a blank line in the output, so it can become a prettier `'

\n__Hello\n__World!\n

'`? If the implicit behavior of automatically removing a trailing newline from a `{...}` evaluation spooks people, I suggested using a backslash to explicitly avoid an extra newline: ``` paragraph = df'''

__{text}\

''' ``` But then it will likely make most df-strings ridden with ugly backslashes. I'm personally more in favor of an implicit behavior of automatic removal of trailing newlines from `{...}` evaluations to keep the usage clean, but would not mind an explicit solution. ------------------------- blhsing | 2026-01-06 02:32:29 UTC | #5 [quote="dg-pb, post:3, topic:105519, username:dg-pb"] So to get: ``` "

\n" "__Hello, World!\n" "

\n" ``` I would need to write: ``` def hello_paragraph() -> str: ____return d""" ____

______Hello, World! ____

""" ``` ? [/quote] To remove all the leading spaces from `

` and `

` you should align the closing triple quotes with them: ``` def hello_paragraph() -> str: ____return d""" ____

______Hello, World! ____

____""" ``` ------------------------- methane | 2026-01-06 03:57:42 UTC | #6 [quote="tstefan, post:2, topic:105519"] I was wondering why it cannot be combined with “b”. I think dedentation and the bytes data type are orthogonal to each other (from a design perspective, maybe not from the implementation perspective). [/quote] fmm, I did not support byte strings in the same way, as t-strings and f-strings do not support byte strings. However, I can’t think of a clear reason why d-strings couldn’t support byte strings. From an implementation perspective, it is easier for d-strings to support byte strings compared to t-strings and f-strings. When writing C or HTML snippets, Unicode strings are usually used, but byte strings are also used in some cases. I will take some time to consider whether to add support for byte strings. ------------------------- methane | 2026-01-06 08:41:26 UTC | #7 I don’t like the idea of changing f-string behavior. d-string should only remove indent before processing backshash escape, f-string, and t-string. In other words, this assertion must pass for any input text.: ```python s1 = df""" foo {text} bar """ s2 = f"""\ foo {text} bar """ assert s1 == s2 ``` Instead, you can strip trailing newline with regular Python methods: ```python paragraph = df'''

__{str(text).rstrip('\n')}, or __{str(text).removesuffix('\n')}

''' ``` ------------------------- jack1142 | 2026-01-06 08:11:21 UTC | #8 I like the way multi-line strings are dedented in C# ( https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/raw-string ), this proposal is similar, with the most significant difference (imo) being the inability to not have a trailing newline (C# doesn’t include the newline before the closing quotes). I wish this was possible to do with Python’s dedented multi-line strings though it seems to be at odds with the assertion comparing dedented and non-dedented multi-line strings that you presented above. I tend to agree that this assertion is important to hold true but it does seem like an annoying quirk that the string will always end with `\n`, requiring me to e.g. use `.rstrip()` on it. I can’t think of anything that would allow the assertion to hold true and allow the string to not have end with a trailing newline, sadly. ------------------------- blhsing | 2026-01-06 08:31:59 UTC | #9 [quote="Jakub Kuczys, post:8, topic:105519, username:jack1142"] it does seem like an annoying quirk that the string will always end with `\n`, requiring me to e.g. use `.rstrip()` on it. I can’t think of anything that would allow the assertion to hold true and allow the string to not have end with a trailing newline, sadly. [/quote] You can use a line continuation marker at the end to avoid a trailing newline: [quote="Inada Naoki, post:1, topic:105519, username:methane"] ``` s = d""" __Hello \ __World!\ __""" # line continuation works as ususal print(repr(s)) # 'Hello_World!' ``` [/quote] ------------------------- methane | 2026-01-06 10:38:52 UTC | #10 I explained in a previous thread why I think it's better to strip the newline immediately after the opening quotes but leave newline in the last line. [quote="Inada Naoki, post:124, topic:90988, username:methane"] ``` s = d""" one two three """ assert s == "one\ntwo\nthree\n" ``` This string looks three **lines**. And **line** in POSIX includes terminal newline. ([ref](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206)) rstrip or backslash is ugly. But it is rarely used, only for creating ugly multiline string. Of course this is my subjective opinion. [/quote] ------------------------- WH-2099 | 2026-01-07 01:46:15 UTC | #11 I prefer concatenating multiple single-line strings. d-strings remind me of the `< str: ____return d""" ________

__________Hello, World! ________

____""" ``` I would have expected this output: ```

__Hello, World!

``` without leading or trailing whitespace, so `"

\n__Hello, World!\n

"` ------------------------- blhsing | 2026-01-09 02:06:30 UTC | #21 [quote="Dima Tisnek, post:20, topic:105519, username:dimaqq"] In this example: ``` def hello_paragraph() -> str: ____return d""" ________

__________Hello, World! ________

____""" ``` I would have expected this output: ```

__Hello, World!

``` without leading or trailing whitespace, so `"

\n__Hello, World!\n

"` [/quote] Inada already [explained](https://discuss.python.org/t/pep-822-dedented-multiline-string-d-string/105519/10) why there should be a trailing newline. And since the indentation of the closing triple quotes is arguably the most visually intuitive way of specifying the level of dedentation, the above d-string should dedent by only 4 spaces instead of 8. I do agree that formatting the content of the string with more indentation than the enclosing quotes follows the current styling recommendations better, though it's probably a necessary tradeoff if we want the closing triple quotes to control the level of dedentation. ------------------------- h-vetinari | 2026-01-09 03:00:46 UTC | #22 Love the PEP, I think it's very well argued and thought-through! [quote="Inada Naoki, post:1, topic:105519, username:methane"] Opening triple quotes needs to be followed by a newline character. This newline is not included in the resulting string. [/quote] I can understand how you arrive at this from the POV of the algorithm that determines the indentation of the trailing triple-quote and deducts that from all the lines, but this restriction seems artificial to me. Taking the two relevant of your examples, I don't see how ``` s = d"""Hello __World! """ print(repr(s)) # 'Hello\n__World!\n' s = d"""\ __Hello __World __""" print(repr(s)) # 'Hello\nWorld!\n' ``` would have any ambiguity in their mechanics. Likewise for the following third example that ties both cases together ``` s = d"""Hello\ __World!\ __""" print(repr(s)) # 'HelloWorld!' ``` The way I read this is that the line containing the opening triple quotes simply does not participate in the stripping of indentation. This is IMO still a rule that's very intuitively explainable[^1]. As any feature, it has some potential for suboptimal use, e.g. [^1]: not least because the position of the opening quotes will generally be different from the rest of the multi-line string anyway. ``` s = d"""__Hello ______World! ____""" print(repr(s)) # '__Hello\n__World!\n' ``` but I think that's where we should rely on "consenting adults" being able to make the choices they prefer (and of course popular linters will come up with best practice rules anyway). To summarise: I think it's imperative to avoid ambiguity, but I also think it'd be better to avoid restrictions that aren't strictly necessary for that. ------------------------- brondsem | 2026-01-09 03:07:24 UTC | #23 [quote="methane, post:1, topic:105519"] Lines that are shorter than the determined indentation become just an empty line [/quote] This could lead to surprising outcomes if text that appears to be part of a string is not actually part of it. In addition to being confusing, it could be abused to be misleading, possibly even with security risk if the situation was just right. Can it be a SyntaxError instead? Perhaps with the exception of empty lines being allowed to still be empty lines. Maybe also allow lines with only indentation characters (spaces/tabs) ```python def examples(): ____d""" ____this should be an error ____""" ____d""" ____this would be ok: ____this too probably: __ ____""" ``` ------------------------- methane | 2026-01-09 04:25:29 UTC | #24 I intended to propose the same thing you are suggesting, but my English was inaccurate. The pointed-out sentence was intended to mean that when the indentation to be deleted is 4 spaces, lines with only 2 spaces would not result in a syntax error but become blank lines. However, it can be read as if any short string is allowed. I will revise the expression in that paragraph to avoid misunderstanding. ------------------------- dimaqq | 2026-01-09 04:46:02 UTC | #25 [quote="blhsing, post:21, topic:105519"] Inada already [explained](https://discuss.python.org/t/pep-822-dedented-multiline-string-d-string/105519/10) why there should be a trailing newline. And since the indentation of the closing triple quotes is arguably the most visually intuitive way of specifying the level of dedentation, the above d-string should dedent by only 4 spaces instead of 8. [/quote] So, this means in practice that users would write: ```python def hello_paragraph() -> str: ____return ( d""" ________

__________Hello, World! ________

____ """.strip() ) ``` If/when they want `"

\n__Hello, World!\n

"` If that’s the intention, maybe it’s OK? I kinda wonder if it’s getting almost as verbose as explicit `textwrap.dedent()` Perhaps classification and statistics of existing uses for `dedent` in the wild could be used to justify the chosen dedent level and no-strip policy vs one-strip/all-strip. ------------------------- dimaqq | 2026-01-09 04:44:34 UTC | #26 [quote="methane, post:1, topic:105519"] ## Specification Add a new string literal prefix “d” for dedented multiline strings. This prefix can be combined with “f”, “t”, and “r” prefixes. This prefix is only for multiline string literals. So it can only be used with triple quotes (`"""` or `'''`). Using it with single or double quotes (`"` or `'`) is a syntax error. [/quote] Maybe it’s better to rewrite this to be more explicit: * `dt, df, drf, drt` are allowed (as well as equivalent permutations `dt, td, df, fd, drf, dfr, rdf, rfd, fdr, frd, drt, dtr, rdt, rtd, tdr, trd`) * `dtf` (and equivalent permutations) are not allowed, as `tf` is not allowed And “The d prefix can only be used with triple quoted string literals and templates, `"""` and `'''`, because it’s intended for multi-line strings.” (the way it’s currently written, it’s a bit vague is `d"""a"""` is actually allowed. ------------------------- encukou | 2026-01-09 14:00:43 UTC | #27 This: > Mixing spaces and tabs in indentation raises a TabError, similar to Python’s own indentation rules. contradicts with: > ```py > s = d""" > --->____Hello > --->____World! > --->__""" # TabError: mixing spaces and tabs in indentation > ``` Under Python's indentation rules, it's perfectly fine to combine spaces and tabs. For example: ```py if True: --->____print(1) --->____print(2) ``` ```txt 1 2 ``` What's not allowed is mixing them “in a way that makes the meaning dependent on the worth of a tab in spaces” ([ref](https://docs.python.org/3/reference/lexical_analysis.html#indentation)). For example: ```py if True: --->____print(1) ____--->print(2) ``` ```pycon Traceback (most recent call last): ... IndentationError: unindent does not match any outer indentation level ``` I suggest instead specifying that each line must - start with *exactly* the whitespace string that's before the closing triple quote (which will be removed), or - contain *only* some beginning of that whitespace string (resulting in a blank line) ------------------------- tstefan | 2026-01-09 14:25:51 UTC | #28 [quote="Dima Tisnek, post:25, topic:105519, username:dimaqq"] So, this means in practice that users would write: ``` def hello_paragraph() -> str: ____return ( d""" ________

__________Hello, World! ________

____ """.strip() ) ``` [/quote] No, you would write ``` def hello_paragraph() -> str: ____return d""" ________

__________Hello, World! ________

\ ________""" ``` if they want `"

\n__Hello, World!\n

"` ------------------------- tmk | 2026-01-09 15:00:45 UTC | #29 It seems in Swift, the last `\n` is also stripped: ```swift let singleLineString = "These are the same." let multilineString = """ These are the same. """ ``` and to get a line break there, you have to include an empty line: ```swift let lineBreaks = """ This string starts with a line break. It also ends with a line break. """ ``` I get that in the unix world, everything should end with a newline, but I wonder whether it isn't more common in the world of Python to have things *not* end with a newline. I don't know the answer to this, but it might be a good idea to investigate this. ------------------------- methane | 2026-01-09 15:26:50 UTC | #30 There is no right answer about that. It's a matter of preference. [quote="Inada Naoki, post:124, topic:90988, username:methane"] FWIW, C# and Swift remove last newline. Java and Julia keep last newline. [/quote] To my eye, Swift example seems there is an empty line at last. I prefer Julia approach. ------------------------- methane | 2026-01-09 15:42:39 UTC | #31 To support multiline string without last newline without using, Julia approach looks better. https://docs.julialang.org/en/v1/manual/strings/#Triple-Quoted-String-Literals > The dedentation level is determined as the longest common starting sequence of spaces or tabs in all lines, excluding the line following the opening `"""` and lines containing only spaces or tabs (the line containing the closing `"""` is always included). Then for all lines, excluding the text following the opening `"""` , the common starting sequence is removed (including lines containing only spaces and tabs if they start with this sequence), ... > ``` > julia> """ > Hello, > world.""" > "Hello,\nworld." > ``` Although "longest common starting sequence of ... excluding ..." rule is not simple to explain, it almost same to `textwrap.dedent()`. If many people think that you should be able to write multiline strings without `\`, `"""[:-1]`, `""".rstrip()`, or `""".removesuffix('\n')`, let's make the dedent width determination the same as Julia. ------------------------- bwoodsend | 2026-01-09 17:09:24 UTC | #32 I personally think being able to control the remaining indentation by dedenting the trailing `"""` is more useful than being able to remove the trailing newline without a `\`. ------------------------- brettcannon | 2026-01-09 18:48:08 UTC | #33 [quote="Inada Naoki, post:31, topic:105519, username:methane"] Although “longest common starting sequence of … excluding …” rule is not simple to explain [/quote] It's the shortest indent following a literal newline that has non-whitespace on it, i.e. what's on the same line as `"""` doesn't count. Or: ```python min(re.finditer(r"\n(?P\w*)\W+", key=lambda match: len(match["indent"])) ``` The Julia docs summarize it as > triple-quoted strings are also dedented to the level of the least-indented line. Make it "dedented to the level of the least-indented line with non-whitespace characters" and I think that's simple enough. ------------------------- yoavrv | 2026-01-10 00:05:35 UTC | #34 This is a wonderful PEP, thank you for your hard work. I have to say that I don't find the trailing '\n' intuitive at all, even after reading more of the posts in this thread (and having read the original thread a while ago). Obviously this is steeped in personal taste, but I also think It will be easier to compose blocks of text together if the trailing line is remove. ```python def hello_paragraph(name): """assuming no trailing""" return df"""

hello {name}!

""" hello_world = hello_paragraph('world') body = df""" {hello_world.replace("\n", "\n ")} """ assert body == "\n

\n hello world!\n

\n" print(body)

hello world!

``` --- I think referring to POSIX line definition is not really relevant to most string processing in python: taking `"a\nb".splitline()` for example will give a length a list of length 2, and the common `"\n".join(...)` idiom doesn't add a trailing "\n". It's only at the edge of the program, when writing to file, when POSIX lines is relevant. Even then, writing `"a\nb\n"` with the trailing line, then opening the file in an editor (vs code) shows up as something like ```text 1 a 2 b 3 ``` So to me, and I'm guessing the majority of newer programmer, the "obvious" way do this with d-strings is to wrap the whole thing with triple quotes: They "look" almost like braces. ``` d""" a b """ ``` I think in general it will be easier to explicitly write trailing newlines when needed than having to explicitly remove it when needed. --- I think adding a supporting format specs for indentation-composition could solve this: If there is a definite, privileged "One obvious way" of making, indenting, and composing text blocks together, that would solve any personal preferences issue. Even/especially if all the format spec does is some trivial function like `.strip().replace()` ``` hello_world_block = ... # Exactly one of this is the correct way body = df""" {hello_world_block:indentation spec} OR {hello_world_block:indentation spec}/ OR {hello_world_block:indentation spec} OR {hello_world_block:indentation spec}/ """ ``` --- Or maybe even just a few more examples in the PEP would work? IDK I can't un-read it and it's late ------------------------- sirosen | 2026-01-10 04:35:41 UTC | #35 I mentioned in the earlier d-strings thread that I thought we should preserve the leading newline, for consistency with other string types. However, I 100% understand the appeal of stripping it off. If the leading newline is removed, does it make sense for the trailing one to be removed as well? I think there's a flavor of consistency there, in that the leading and trailing lines are the delimiters but not part of the content. --- I still think there are too many string prefixes, and prefer the `str.dedent` method idea, acknowledging its limitations. But if we're doing d-strings, let's make it as good as possible! ------------------------- hprodh | 2026-01-10 06:25:05 UTC | #36 [quote="Yoav Ravid, post:34, topic:105519, username:yoavrv"] `# Exactly one of this is the correct way` [/quote] I was already suggesting a dedicated symbol in the previous thread, there is no indentation "spec" necessary, the reindentation can be inferred from the braces position : ``` body = df""" {->hello_world_block} """ ``` The general opinion about this was that it probably belongs to another PEP. ------------------------- h-vetinari | 2026-01-10 07:08:15 UTC | #37 [quote="Stephen Rosen, post:35, topic:105519, username:sirosen"] If the leading newline is removed, does it make sense for the trailing one to be removed as well? I think there’s a flavor of consistency there, in that the leading and trailing lines are the delimiters but not part of the content. [/quote] I think the following consideration is key [quote="Inada Naoki, post:7, topic:105519, username:methane"] In other words, this assertion must pass for any input text.: ``` s1 = df""" foo {text} bar """ s2 = f"""\ foo {text} bar """ assert s1 == s2 ``` [/quote] But I'd want to make it symmetrical w.r.t. `\`. If `s2` begins with `"""\` (modulo string qualifiers) then so should `s1`; both starting with bare `"""` should match just the same. I believe the only way to make all of this internally consistent (especially with regular multi-line strings) is to _not touch beginning or trailing newlines_. Let's please avoid magic pre-processing that user cannot opt out of, but do the "obvious" thing, with user freedom to opt into stripping (e.g. by adding a backslash or simply beginning with the body right after the opening `"""`). As I mentioned further up, this is easy to make consistent by excepting the line containing the opening `"""` from any dedenting. Julia also exempts this line [quote="Inada Naoki, post:31, topic:105519, username:methane"] [quote] The dedentation level is determined as the longest common starting sequence of spaces or tabs in all lines, excluding the line following the opening `"""` [/quote] [/quote] The trailing `"""` is slightly harder, because according to the current draft, it determines how much indentation is stripped. But if this is switched to the Julia methodology [quote="Inada Naoki, post:31, topic:105519, username:methane"] If many people think that you should be able to write multiline strings without `\`, `"""[:-1]`, `""".rstrip()`, or `""".removesuffix('\n')`, let’s make the dedent width determination the same as Julia. [/quote] then this issue resolves itself as well, and people can then -- as for the opening quotes -- either use ``` some_long_variable = d""" ____body\ ____""" ``` or ``` some_long_variable = d""" ____body""" ``` to suppress the trailing newline. ------------------------- JakobStadler | 2026-01-11 18:55:14 UTC | #38 I'm +1 on stripping the first and last newline. I think seeing additional newlines is more obvious in showing intent that there should be more lines than the single backslash doing the opposite. (And the backslash placement is bad on my German keyboard, so the less I need them, the better) I also think it's easier to work with when copy pasting text in and out. If I take a paragraph of some Markdown file (e.g. documentation) and copy it into a python multiline string (e.g. docstring) or vice versa, I don't want to add/remove backslashes every time. I can change indentation easily with (Shift) Tab, can't say the same for the backslashes. ------------------------- webreflection | 2026-01-13 08:59:03 UTC | #39 my 2 cents: I use *codedent* (JS) which takes a different approach: the end of the string doesn’t matter, it’s only the very first non empty line that dictates the indentation, making easier to read and reason about (imho). That is: ```python def greetings(name): return df"""

Hello {name}!

""" greetings('d') # \n

\n Hello d!

\n ``` The ending indentation aligns well with the starting indentation of the string and for a PL where indentation is everything I think it’s a more elegant approach. On top of that, new lines are preserved (if desired) but these can be easily stripped after so no `\n` surrounding, yet no data is lost. Only thing that matters is, again, the very first line which can have tabs and/or spaces up to a non tab-space char. All other lines that match that `^\s{4}` (4 spaces indentation per line) will be sanitized, those that don’t won’t. Wouldn’t be this approach a simplified improvement over current *ending ruler* proposal? **edit** moreover, all examples here are tiny chinks of code … I can see me scrolling until the end of a potentially long query/HTML/Markdown content to eventually fix the issue in case indentantion is off VS knowing at the very first line *where* indentation is meant to disappear (before every other line). ------------------------- methane | 2026-01-13 09:28:53 UTC | #40 [quote="Jakob Stadler, post:38, topic:105519, username:JakobStadler"] I also think it’s easier to work with when copy pasting text in and out. If I take a paragraph of some Markdown file (e.g. documentation) and copy it into a python multiline string (e.g. docstring) or vice versa, I don’t want to add/remove backslashes every time. I can change indentation easily with (Shift) Tab, can’t say the same for the backslashes. [/quote] You don't have to remove trailing newline from Python or markdown every time. Most Python/Markdown snippets should have trailing newline. You need to strip it in only rare case. For example, https://github.com/python/cpython/blob/main/Lib/test/test_regrtest.py have 45 `dedent()`s for python snippets. None of them strip last newline. ------------------------- tmk | 2026-01-13 10:51:52 UTC | #41 [quote="Inada Naoki, post:40, topic:105519, username:methane"] For example, [cpython/Lib/test/test_regrtest.py at main · python/cpython · GitHub](https://github.com/python/cpython/blob/main/Lib/test/test_regrtest.py) have 45 `dedent()`s for python snippets. None of them strip last newline. [/quote] None of them strip the first newline either. So I think this test file might not be the best example. (EDIT: and I don't know if we need more examples, but I just noticed that the KDL config language [strips the last newline in dedented strings](https://kdl.dev/spec/#section-3.12.2.3).) ------------------------- methane | 2026-01-15 15:04:28 UTC | #42 I was only arguing against "add/remove backslashes every time". I never denied that there are use cases for removing the last newline. Besides the backslash, there are other ways to remove the last newline, such as `[:-1]`, `.rstrip()`, and `.removesuffix('\n')`. And I am proposing to change the specification to borrow Julia's rules in order to prevent d-strings from becoming more inconvenient than textwrap.dedent() in use cases where the last newline character needs to be removed. ------------------------- methane | 2026-01-15 15:24:42 UTC | #43 [quote="H. Vetinari, post:22, topic:105519, username:h-vetinari"] I can understand how you arrive at this from the POV of the algorithm that determines the indentation of the trailing triple-quote and deducts that from all the lines, but this restriction seems artificial to me. [/quote] The primary reason for not including the newline immediately after the opening triple quote in the string is intuitive clarity. As can be seen from the sample code of `textwrap.dedent()`, where a backslash is written immediately after the opening triple quote to remove the newline, it is very unnatural and difficult to understand when lines within the string to be dedented start after the opening triple quote rather than with an indent. Instead of thinking that d-strings remove indentation and line breaks from triple-quoted strings, consider that d-strings treat the part after the indentation as the body of the string. The following image visually explains why the newline immediately after the opening triple quote is ignored, while the newline before the closing triple quote is naturally and clearly included in the string. ![image|216x462, 50%](upload://u2oJMyIhjOc87CX0Ifnq5rIENzk.png) Another reason to ignore the newline immediately after the opening triple quote is to leave room for adding something there in the future. It may allow writing comments starting with `#` in the future. [Previous thread about d-string](https://discuss.python.org/t/pre-pep-d-string-dedented-multiline-strings-with-optional-language-hinting/90988) had proposed adding language hint there. However, to move the discussion forward, we propose the specification of this PEP with room to add those ideas in the future. ------------------------- h-vetinari | 2026-01-15 20:28:08 UTC | #44 [quote="Inada Naoki, post:43, topic:105519, username:methane"] The following image visually explains why the newline immediately after the opening triple quote is ignored, while the newline before the closing triple quote is naturally and clearly included in the string. [/quote] I think that's pretty subjective. I certainly don't see why dedenting (a horizontal operation) should be removing any newlines, including the initial one. Beyond intuition however, I'm mostly concerned with consistency. We're beginning to have quite a few string modifiers (`r`, `f`, `d`, `t`, `b`; as well as no modifier, of course). `d` would be the _only_ one to strip newlines, and the downside of the confusion this will cause IMO far outweighs any other benefit. This inconsistency is IMO not justified especially because anyone who cares about removing the initial newline can easily do so (under the "don't strip newlines" model) with little effort ``` s = d"""\ ____foo ______bar ____baz ____""" # or s = d"""foo ______bar ____baz ____""" ``` Besides consistency, we can make the same argument about someone who _does_ want the initial newline, and who -- under your model -- would be forced to do ``` # d-strings strip initial newlines but we need one here s = d""" ____ ____foo ______bar ____baz ____""" ``` You can argue that this case is less common (I agree), but the wart this causes in the code[^1] is IMO many times worse. It's so distinct from regular multi-line string handling that it will almost always need a comment. [^1]: and in the git history, when changing modifiers So in summary, stripping newlines creates multiple inconsistencies with existing string handling and user expectations, and I see no reason that's close to compelling enough to give up that consistency. [quote="Inada Naoki, post:43, topic:105519, username:methane"] Another reason to ignore the newline immediately after the opening triple quote is to leave room for adding something there in the future. It may allow writing comments starting with `#` in the future. [Previous thread about d-string](https://discuss.python.org/t/pre-pep-d-string-dedented-multiline-strings-with-optional-language-hinting/90988) had proposed adding language hint there. [/quote] Comments -- like dedenting -- should have nothing to do with newlines. Doing so would lead to yet more magical interactions that people have to spend time learning, rather than doing the obvious. Don't get me wrong, I can see the appeal of stripping the initial newline. But then this should be done as a separate PEP for all multi-line strings, not for a single modifier. "Special cases aren't special enough to break the rules." ------------------------- yoavrv | 2026-01-15 22:10:17 UTC | #45 I don't think that's true. The entire point of d-strings is that they make it easier to work with text blocks inside indented python code by changing the indentation logic, so it's specifically, deliberately inconsistent with the other string types. I think the important points to consider when thinking about the indentation are not if they are consistent, but * How easy it is to read indented blocks inside already indented python code * How easy it is to write blocks of text in already indented python code * How easy it is to compose together these blocks of text What your answers are determine where/how you think d-string should indent the text. I think having to put a `\` at the start of practically every d-string is just inviting bugs from the 1/N chance people forgetting to add and not noticing because it "look right". I expect `N~50`. Having to remember doing this will make it harder to write, and having to notice the `\` or lack thereof will make it harder to read correctly. ___ Honestly, I almost *never* see a `\` line break in python code, especially "good" python code. I *personally* find it extremely distasteful. The [PEP 8 style guide](https://peps.python.org/pep-0008/#maximum-line-length) also discourage its use, allowing it only as "last resort" > The preferred way of wrapping long lines is by using Python’s implied line continuation inside parentheses, brackets and braces. Long lines can be broken over multiple lines by wrapping expressions in parentheses. These should be used in preference to using a backslash for line continuation. > > Backslashes may still be appropriate at times. For example, long, multiple with-statements could not use implicit continuation before Python 3.10, so backslashes were acceptable for that case: So I think for most python programmer using `\` will feel very foreign. If that is the case, I believe solutions that involve having to add extra indentation deliberately will be always be more natural, clear, and obvious then solutions that require using `\` to remove indentation. ------------------------- h-vetinari | 2026-01-15 22:41:08 UTC | #46 [quote="Yoav Ravid, post:45, topic:105519, username:yoavrv"] I don’t think that’s true. [/quote] It's completely unclear what you're referring to. Please use the quote feature. [quote="Yoav Ravid, post:45, topic:105519, username:yoavrv"] I think the important actual points to consider when thinking about the indentation are *not* if they are consistent, but [...] [/quote] What you're suggesting is to design this feature in complete isolation, and ignoring all the ways this will cause permanent friction, through irregularity of the shiny new feature you've just designed with the rest. If Python did that, you'd get a patchwork of barely-fitting pieces, not a coherent language. Irregularity is a permanent cost, because each inconsistent behaviour decreases the ability of our puny brains to keep all relevant context "in memory". So it shouldn't be paid lightly[^1]. [^1]: that's not to say irregularity is never justified, but the benefits need to offset the costs. [quote="Yoav Ravid, post:45, topic:105519, username:yoavrv"] I think having to put a `\` at the start of practically every d-string is just inviting bugs from the 1/N chance people forgetting to add and not noticing because it “look right”. [/quote] Well, you're assuming that it's a common case that people _need_ to remove an initial newline. I question that it's common, much less so ubiquitous that it outweighs the other costs. Composition is another big one. One of the most fundamental strength of Python that new users learn basically on day 0, is how powerfully array-indexing can be composed, e.g. `a[1:N] + a[N:-1] == a[1:-1]`, without having to do special-casing around `N`. Messing with newlines means breaking this kind of composability for different kinds of strings (and suddenly the string component that you turned into a d-string gets attached incorrectly to something else in other places of the code far from the definition site). In contrast to the bug you describe (which only affects people that require to remove the initial newline), this will hit a far larger set of users -- anyone doing concatenation of multi-line strings -- who simply "forgot" (due to irregularity of the feature) to insert an extra newline when converting something to a d-string. [quote="Yoav Ravid, post:45, topic:105519, username:yoavrv"] Honestly, I almost *never* see a `\` line break in python code, especially “good” python code. I *personally* find it extremely distasteful. [/quote] Sure. That still doesn't make it a good enough reason to break the regularity. You only have to "pay" the `\` if you feel even more strongly about an initial newline[^2]. [^2]: completely aside from the second variant I showed to avoid the newline that doesn't need a `\`. ------------------------- barry-scott | 2026-01-15 23:47:06 UTC | #47 [quote="H. Vetinari, post:46, topic:105519, username:h-vetinari"] What you’re suggesting is to design this feature in complete isolation, and ignoring all the ways this will cause permanent friction, through irregularity of the shiny new feature you’ve just designed with the rest. If Python did that, you’d get a patchwork of barely-fitting pieces, not a coherent language. [/quote] You argue for consistency for string types that make dedent feature difficult to use in common use cases. The dedent feature should be designed to be usable for the 99.9% of use cases, and consistency is a secondary consideration. Losing the leading \n seems obviously a very good idea. Keeping the tailing \n is, I think the most common use case. Needing to use \ for any use case I hope can be avoided. I don’t like to see tailing \ in my code. ------------------------- gcewing | 2026-01-16 03:30:45 UTC | #48 > > Someone replied to a topic you are Watching. > > The entire point of d-strings is that they make it easier to work with > text blocks inside indented python code by changing the indentation > logic, so it’s specifically, deliberately inconsistent with the other > string types. > Rather than re-use triple quotes for this kind of string, I would rather the syntax look completely different. Having the contents of the string interpreted in radically different ways depending on the presence or absence of a d prefix is rather too subtle for my taste. A while ago I came up with this: string s: ------------------------- methane | 2026-01-16 03:56:59 UTC | #49 [quote="H. Vetinari, post:46, topic:105519, username:h-vetinari"] In contrast to the bug you describe (which only affects people that require to remove the initial newline), this will hit a far larger set of users – anyone doing concatenation of multi-line strings – who simply “forgot” (due to irregularity of the feature) to insert an extra newline when converting something to a d-string. [/quote] In past threads, we also considered the idea of using `from __future__ import dedent_string_literals` instead of the d-prefix. So if `future import` had been chosen there, I would have designed the rules to prioritize maximum compatibility with all multiline string literals. However, people chose the d-prefix over future import. We do not need to replace all `"""..."""` with d-strings. I designed the rules with priority given to usability in contexts such as `textwrap.dedent("""...""")`. Therefore, I prioritized eliminating the inconvenience that a `\` is almost always required immediately after `textwrap.dedent("""`, over the inconvenience of needing to add a newline when rewriting `"""` as `d"""`. When making technical decisions, many objective facts are taken into consideration, but the final choice of priorities is always subjective. My subjective judgment may not align with yours; however, since I am the author of this PEP, it is ultimately written based on my own subjective decisions. The presentation of new facts that support your position is welcome, but let us refrain from continuing subjective and abstract discussions about whether the initial newline should be preserved or removed. ------------------------- h-vetinari | 2026-01-16 04:24:09 UTC | #50 [quote="Inada Naoki, post:49, topic:105519, username:methane"] However, people chose the d-prefix over future import. [/quote] I would weigh a self-consistent design (generally speaking, not talking about newlines here) higher than an informal vote, where most people go by what looks/feels better, mostly without considering the full set of consequences. It's like designing the nuclear powerplant around the bikeshed proposal that people liked the most. [quote="Inada Naoki, post:49, topic:105519, username:methane"] When making technical decisions, many objective facts are taken into consideration, but the final choice of priorities is always subjective. My subjective judgment may not align with yours; however, since I am the author of this PEP, it is ultimately written based on my own subjective decisions. [/quote] Sure it's your PEP, you write it how you think it's best. Despite my enthusiasm for most of the PEP, the inconsistencies turn me from +1 to -0.5. ------------------------- bwoodsend | 2026-01-16 17:51:44 UTC | #51 [quote="H. Vetinari, post:44, topic:105519, username:h-vetinari"] Beyond intuition however, I’m mostly concerned with consistency. We’re beginning to have quite a few string modifiers (`r`, `f`, `d`, `t`, `b`; as well as no modifier, of course). `d` would be the *only* one to strip newlines, and the downside of the confusion this will cause IMO far outweighs any other benefit. [/quote] ... and `r` strings are the only ones to interpret backslashes literally and, until `t` strings came along, `b` strings were the only strings to not produce `str` types and `f` strings were the only strings to be sensitive to the surrounding namespace. In all cases, it's precisely their inconsistencies that made them useful. I think I probably would have agreed with you had I not hit all of the issues that the PEP tries to solve. I want to write a multline string so I intuitively reach for Python's multiline string feature, using it as I've seen countless times in docstrings... ```python return f""" line 1 line 2 {multiline_variable_bit} line 4 """ ``` but by the time I've adjusted for all the ways Python very literally interprets the spaces and newline characters between the quotes, it ends up looking something like: ```python return """\ line 1 line 2 {} line 4 """.format(multiline_variable_bit) ``` It's consistent behaviour but it's both surprising and unhelpful. The so-called *consistency* of regular multiline strings feels more like a pedantic hyper-literalism rather than a feature. So yes, it's inconsistent, we're well aware that it's inconsistent, but I'd still argue that it makes it a better, and IMO more intuitive, feature. ------------------------- guido | 2026-01-16 18:06:08 UTC | #52 The way I see it: d-strings strip various forms of cosmetic whitespace added to make the string more readable in the context of the surrounding code. They are also very useful for docstrings, so the d prefix is perfect. (Like r-strings are perfect for regular expressions.) ------------------------- h-vetinari | 2026-01-16 20:34:17 UTC | #53 [quote="Brénainn Woodsend, post:51, topic:105519, username:bwoodsend"] In all cases, it’s precisely their inconsistencies that made them useful. [/quote] IMO you're mixing a feature's primary purpose with its secondary effects. In all cases, the primary purpose was important enough to open up some new aspect (but those PEPs still ensured the syntactic/semantic fallout is minimized). [quote="Brénainn Woodsend, post:51, topic:105519, username:bwoodsend"] but by the time I’ve adjusted for all the ways Python very literally interprets the spaces and newline characters between the quotes, it ends up looking something like: [/quote] It still sounds to me like people would want to change multiline string newline handling more generally -- which I even tend to agree with -- but because it's hard to change any existing behaviour, it'll now only be in d-strings, which I think is suboptimal. [quote="Brénainn Woodsend, post:51, topic:105519, username:bwoodsend"] So yes, it’s inconsistent, we’re well aware that it’s inconsistent, but I’d still argue that it makes it a better, and IMO more intuitive, feature. [/quote] Given the responses/hearts, it seems people are willing to pay that price, so I'll stop making my case. As long as this is a conscious choice rather than confirmation bias due to enthusiasm, it'll be fine. ------------------------- sirosen | 2026-01-16 21:35:44 UTC | #54 I would rather have the leading escape in exchange for more consistent multiline string rules. But as it's the job of the proposed d-string to manipulate whitespace, I think having different rules here is OK. The thing which puzzles me is why the trailing newline shouldn't -- for the sake of internally consistent behavior -- also be stripped off. Why should I expect two newlines printed in this example? ``` print( d""" A text this short Is my full report """ ) ``` I don't quite understand the argument here. Removing the leading newline is intuitive, but the trailing one is not? My intuition is that either the first and last lines are semantic or they are not. ------------------------- oscarbenjamin | 2026-01-16 22:00:10 UTC | #55 [quote="Stephen Rosen, post:54, topic:105519, username:sirosen"] Why should I expect two newlines printed in this example? ``` print( d""" A text this short Is my full report """ ) ``` [/quote] I got confused about what you were saying here at first. There are two lines so I would expect the string to have two newlines but what you actually mean is that print will append a newline so you end up having an extra blank line i.e. two newlines at the end of the output. The counterpoint would be why this should be missing a line ending: ``` with open('foo.txt', 'w') as f: f.write( d""" A text this short Is my full report """ ) ``` It seems intuitive to me that the two middle lines here should show how it would look in the file if I were to e.g. `cat foo.txt`. I think `print` is the oddball there for appending a newline. When working with a multiline string you generally want the string to have its own newlines. Otherwise it's better to have a list of strings. ------------------------- barry-scott | 2026-02-05 11:40:35 UTC | #56 [quote="Stephen Rosen, post:54, topic:105519, username:sirosen"] The thing which puzzles me is why the trailing newline shouldn’t – for the sake of internally consistent behavior – also be stripped off. [/quote] Always stripping trailing /n would mean that you do not get double /n/n when printing a d’’’string’’’ or inserting it into an other multiline string. If a trailing /n is required you would need to leave a empty line before the closing quote. ------------------------- bwoodsend | 2026-01-16 23:52:20 UTC | #57 The trailing newline question is admittedly use-case dependent. * When composing a large multi-line string by concatenating bits together having the trailing newline is good. ```python chunks = [d""" line 1 line 2 """] if something: chunks.append(d""" optional line 3 optional line 4 """) chunks.append(d""" line 5 line 6 """) return "".join(chunks) ``` * When composing via f-strings, it [gets in the way](https://discuss.python.org/t/pep-822-dedented-multiline-string-d-string/105519/34). * When writing a text file, the trailing newline is usually needed. * When writing test cases for something that generates multi-line strings, the trailing newline would ideally match whatever's being tested. ^[I do this a lot in Java. It's so satisfying – not a feeling I'm used to experiencing whilst going anywhere near Java.] ```python def test(): assert tabulate.tabulate([[1, 2], [3, 4]], ["loooooong", "short"]) == d""" loooooong short ----------- ------- 1 2 3 4 """ # In this case, the trailing newline is not wanted ``` * When composing multi-line console output or error messages, it may or may not get in the way depending on whether whatever's putting the text on the screen appends a newline. It's also possible that an extra blank line is desired after a large block of text so that a resultant double newline is accidentally a good thing^[although relying on that behaviour may get confusing quickly]. ------------------------- sirosen | 2026-01-17 00:38:49 UTC | #58 Sorry about the ambiguity; yes, I meant the two trailing newlines. I was mostly responding to the statement that the proposed behavior is "intuitive". Right or wrong, I have a different intuitive expectation. [quote="Oscar Benjamin, post:55, topic:105519, username:oscarbenjamin"] It seems intuitive to me that the two middle lines here should show how it would look in the file if I were to e.g. `cat foo.txt`. I think `print` is the oddball there for appending a newline. [/quote] As I note above, I don't think you're *wrong*, but I definitely don't think in terms of `cat` here. I think in terms of existing multiline Python strings. When I learn that the leading newline is not part of the content, that jumps out at me as a new and special rule. Then I start expecting that the trailing one isn't either -- to me, that would match doing special cleanup on the start of the string. If the question is reframed away from "intuitive" and towards "what's ergonomic", I think the cases where a newline must be explicitly added are easier to handle comfortably than those where it must be explicitly removed. I'm thinking of `print()`, `"\n".join()`, inclusion in f-strings, and other scenarios like that. ------------------------- jack1142 | 2026-01-17 01:51:44 UTC | #59 I personally am more fond of the approach where the last newline is removed from the string as well, so that the syntax allows any string to be represented without having to resort to `\` escapes, since their use has been discouraged by the commonly used style guides/formatters (and I don’t like them myself either :smile:). The other option is slicing with `[:-1]` , but that involves runtime overhead. I suppose Python could technically learn to reduce slice operations on constants to the new constant, though for f-strings, such optimisation could only be applied in *some* cases. It definitely seems like adding new optimisations would be a way bigger endeavour compared to just designing the syntax such that it doesn’t impose restrictions on what strings can be made with it, though. What I’d really like to come back to, though, is how `\` works for removing the trailing newline: [quote="methane, post:1, topic:105519"] ```python s = d""" __Hello \ __World!\ __""" # line continuation works as ususal print(repr(s)) # 'Hello_World!' ``` [/quote] Is it just me, or does the behaviour here seem a bit surprising? `\` means ignored end of line, but then doesn’t it somewhat suggest that it is equivalent to: ```python s = d""" __Hello World!""" ``` …which is not valid syntax under the current proposal? I know that, as far as the tokeniser is concerned, we’re within a string, and so this is considered an escape sequence, not [explicit line joining](https://docs.python.org/3/reference/lexical_analysis.html#explicit-line-joining), but it does throw me off that it doesn’t work like a typical line continuation, where I can just join the lines back together and get the equivalent code. This could technically be solved by the earlier suggestion of just allowing the closing `"””` anywhere, not just indentation-only lines. However, I personally like having the closing `"””` on a separate line since it gives more control over how much of the indentation we want to remove (also brought up by Brénainn earlier). I do, however, think that the line continuation (`\`) should not be allowed in the line that precedes it due to what I’ve shown above as (imo?) an inconsistency with the way line continuation works today. I admit I don’t use that feature of the language at all, so please correct me if there’s actually a different example where you can’t just remove the line continuation and join the lines to get the equivalent code. Anyway, this leaves me with suggesting this variant (which I’m pretty sure was suggested here, but not sure by whom) as one that I’d consider least restrictive out of the bunch: ```python assert "Hello World!" == d""" __Hello \ __World! __""" assert "Hello\nWorld!" == d""" __Hello __World! __""" assert "Hello\nWorld!\n" == d""" __Hello __World! __ __""" assert "Hello\nWorld!\n" == d""" __Hello __World! __""" ``` I strongly agree with Stephen’s sentiment that the “what’s ergonomic” question is the right question to ask here. The syntax should feel familiar, but it doesn’t need to (and based on the discussion so far, it seems to me that *some* inconsistency is unavoidable) feel entirely consistent with existing strings, and that’s fine. ------------------------- gcewing | 2026-01-17 02:11:35 UTC | #60 Maybe we should make this look very different from existing triple-quoted strings by using something other than triple quotes. ``` s = <<< Multi-line indentation-respecting string literal contents goes here. >>> ``` That's clearly something else, so it can have its own syntactic rules without causing confusion. ------------------------- Fede_s | 2026-01-17 07:33:26 UTC | #61 There’s something that’s unclear to me in the PEP: what exactly is meant by left-aligned multiline string? Is it something like this? ```python def some_indented_code(): my_string = """

I don’t start from the code’s indentation level

""".strip() ``` If not, then how would you call the above and would it make sense to add it to the possible options in the Motivation section? It’s been my go-to for years for things like HTML or long SQL query literals. ------------------------- Monarch | 2026-01-17 08:23:44 UTC | #62 `d"""` is already something different, just like `r""` is something entirely different from `""`. We already have string modifiers that significantly change the output, from entirely different types (`b""` -> `bytes`, `t""` -> `Template`) to entirely different interpretations. Here is a real path on my desktop which, when put in quotes as `"C:\Users\user\Pictures\78981416.jpeg"`, is a `SyntaxError`, while the same path with `r"C:\Users\user\Pictures\78981416.jpeg"` is completely fine. We already live in a world where different string modifiers are worlds apart in their behavior, and it has proven to be quite intuitive. ------------------------- Monarch | 2026-01-17 09:11:46 UTC | #63 [quote="Brénainn Woodsend, post:57, topic:105519, username:bwoodsend"] The trailing newline question is admittedly use-case dependent. [/quote] I do agree with this, and with that in mind I personally lean toward stripping newlines on both ends because: * It is a simpler rule to remember and explain. * Anecdotally, I find it simpler and more concise to add newlines than to remove them. * Getting in the way of composing with f-strings feels like a pretty big drawback to me, whereas wanting newlines for concatenation or writing text files has simpler[^1] workarounds like `"\n".join(chunks)` and `f.write("\n")` or `f.write(chunk + "\n")`. [^1]: in my opinion ------------------------- JakobStadler | 2026-01-17 09:37:51 UTC | #64 [quote="Brénainn Woodsend, post:57, topic:105519, username:bwoodsend"] When composing a large multi-line string by concatenating bits together having the trailing newline is good. [/quote] I'd argue that this code works just as well with no trailing newline by just using `return "\n".join(chunks)` If you need the extra newline though, then I'd argue explicit adding (extra line) is more intuitive than explicit removing (backslash). ``` chunks = [d""" line 1 * line 2 """] if something: chunks.append(d""" optional line 3 * optional line 4 same line preamble of line 5: """) chunks.append(d""" line 5 * line 6 """) return "".join(chunks) ``` (Granted, that could be because I spend most of my work life on Windows systems and the last newline character in source code is actually displayed as an extra lines in IDEs, so I'm used to see a visual line at the end.) For another example that may go against expectations: ``` letters = d""" a b c """.split("\n") ``` What's `len(letters)`? My intuition tells me it should be 3, but with trailing newline, it would actually be 4. So I can't just chuck into a `for l in letters` loop because I have to deal with the empty string in the last item first. ------------------------- blhsing | 2026-01-17 09:52:31 UTC | #65 [quote="Jakob Stadler, post:64, topic:105519, username:JakobStadler"] For another example that may go against expectations: ``` letters = d""" a b c """.split("\n") ``` What’s `len(letters)`? My intuition tells me it should be 3, but with trailing newline, it would actually be 4. So I can’t just chuck into a `for l in letters` loop because I have to deal with the empty string in the last item first. [/quote] The more idiomatic way to do this is to call the `splitlines` method instead: ``` letters = '''\ a b c '''.splitlines() ``` And `len(letters)` would be 3 as expected. ------------------------- methane | 2026-01-17 10:24:06 UTC | #66 A left-aligned multiline string means exactly what you say it means. I'm not good at English, so I'm writing English sentences using AI translation. If there is a clearer term, please teach me it. Your sample code only has one level of indentation, so left-aligned string is not a problem. But as I wrote in the Motivation section, "When writing multiline string literals within deeply indented Python code", it can be matter. ```python class MyClass ... def method(self, ...): ... try: ... for ... in ...: ... if ... : html = Markup("""

I don’t start from the code’s indentation level

""".strip()) ``` Some people doesn't code like this. That's why `textwrap.dedent()` used much in CPython testcode. ------------------------- Fede_s | 2026-01-17 10:57:27 UTC | #67 分かりました、ありがとうございます。I think the term works, I never thought of a way to call it before so I was unsure I got the right meaning, but as far as I know everyone else understood it, so it's fine. I don't think deeply indented, left-aligned strings are a big issue, but I do support the idea of a d-string, because I know left-aligned strings aren't very popular (my colleagues at work for example don't like them). Specifically, I support them with a syntax like the suggested `d"""` and would not want to see completely new syntax to support them. ------------------------- effigies | 2026-01-17 15:02:34 UTC | #68 IMO the ground has been trod pretty thoroughly here. Does it make sense for the trailing newline discussion to go into a Rejected Ideas section? If people agree that their position is either adopted by the PEP or fairly represented in the Rejected Ideas, then it's up to the SC to decide whether to adopt the PEP as-is or request to switch. Along those lines, I think the `from __future__` alternative should mention its benefits in addition to its reasons for rejection. "This could help simplify Python’s grammar in the future" is the smallest of them, IMO. There are already two interpretations: regular multiline strings and docstrings. Further, the PEP itself is evidence that there is a desire for another interpretation. Using `from __future__` would allow a transition to a single consistent interpretation, which benefits users, teachers and students. I do think that "a transition is practically impossible" is a valid argument against it (although I disagree), so it's fine for the PEP to take the position that three interpretations is the best we can hope to achieve. It would also be worth noting when citing the vote for new syntax over `__future__`, the poll did not permit multiple acceptable choices: https://discuss.python.org/t/pre-pep-d-string-dedented-multiline-strings-with-optional-language-hinting/90988/90 Thanks for the PEP! ------------------------- loic-simon | 2026-01-17 20:08:04 UTC | #69 [quote="Guido van Rossum, post:52, topic:105519, username:guido"] They are also very useful for docstrings, so the d prefix is perfect. (Like r-strings are perfect for regular expressions.) [/quote] About this, since the opening triple quotes needs to be followed by a newline character, usind d-srtrings in docstrings would look like ``` def complex(real=0.0, imag=0.0): d""" Form a complex number. Keyword arguments: real -- the real part (default 0.0) imag -- the imaginary part (default 0.0) """ ... ``` which violates [PEP 257](https://peps.python.org/pep-0257/#multi-line-docstrings): > Multi-line docstrings consist of a summary line just like a one-line docstring, followed by a blank line, followed by a more elaborate description. Since leading whitespace is already stripped from docstrings anyway ([since 3.13](https://docs.python.org/3.15/whatsnew/3.13.html#other-language-changes)), I suppose they can be considered "implicit d-strings" and thus the d prefix is not needed? The docstring use case is not currently mentionned in the PEP, I think it should (to either discourage its usage, or explain how it plays with PEP 257). ------------------------- guido | 2026-01-17 23:25:16 UTC | #70 PEP 257 is so widely violated that I consider it a failure — or at best a mere suggestion. I like the idea of using d-strings for docstrings, and maybe the d-semantics should implement whatever tools people use to strip whitespace from docstrings today. ------------------------- hprodh | 2026-01-18 05:04:04 UTC | #71 [quote="Loïc Simon, post:69, topic:105519, username:loic-simon"] , I suppose they can be considered “implicit d-strings” and thus the d prefix is not needed? [/quote] ~~This seems right. We can add to the doc that d-strings are transforming string just like doscstrings implicitly do.~~ In this case it might be possible to allow not having the initial line return, but this might be discouraged for esthetical reasons. The 'header line' indentation level should be considered being at the same level as the closing triple quote. For example : ``` def function(): return d"""#comment with contextmanager: statement() """ ``` The function would return ```text #comment with contextmanager: statement() ``` ------------------------- sirosen | 2026-01-18 03:24:49 UTC | #72 [quote="Chris Markiewicz, post:68, topic:105519, username:effigies"] IMO the ground has been trod pretty thoroughly here. Does it make sense for the trailing newline discussion to go into a Rejected Ideas section? If people agree that their position is either adopted by the PEP or fairly represented in the Rejected Ideas, then it’s up to the SC to decide whether to adopt the PEP as-is or request to switch. [/quote] I mostly agree. I'm sure @methane has thought about this plenty between the Ideas thread and authoring the PEP. And we can definitely trust the SC review process to examine the options carefully. The choice of which rules are used should be explained in the PEP, and I agree that Rejected Ideas is a great place to do most of this. But I don't find "intuitiveness" to be a great justification, since it's very subjective on its own. Plus, I find the proposed rules a bit _unintuitive_. So I think it is worth challenging this a bit, if only to produce a better document which covers the options well. [quote="Guido van Rossum, post:70, topic:105519, username:guido"] I like the idea of using d-strings for docstrings, and maybe the d-semantics should implement whatever tools people use to strip whitespace from docstrings today. [/quote] I like the idea of matching the dedent rules which are used for `__doc__` in theory, but I don't think it works. `inspect.cleandoc`[^1] is too aggressive about stripping whitespace for general use: > All leading whitespace is removed from the first line. Any leading whitespace that can be uniformly removed from the second line onwards is removed. Empty lines at the beginning and end are subsequently removed. Also, all tabs are expanded to spaces. [^1]: Assuming that's the standard to follow? `__doc__` assignment does a lot less. But `__doc__` ignores leading whitespace on the first line, which can produce some odd things: ``` def f(): """\ hello world """ # does not preserve a 2-space indent for 'world' ``` If the rules are "matches `__doc__` except for X, Y, Z", I'm not sure how convincing that is. But matching `__doc__` exactly seems like it wouldn't be acceptable, since that leading line behavior is guaranteed to confuse people. ------------------------- methane | 2026-01-19 04:36:42 UTC | #73 [quote="Chris Markiewicz, post:68, topic:105519, username:effigies"] IMO the ground has been trod pretty thoroughly here. Does it make sense for the trailing newline discussion to go into a Rejected Ideas section? [/quote] Of course. I am currently working on updating the reference implementation before updating PEP, so it will take some time until the PEP update. The PEP update I'm considering is as follows: * Add the "removing last newline" in the rejected ideas. * Allow closing quote after non-empty line to remove last newline. * Remove "common longest indent" instead of indent of closing quote, like Julia does. ------------------------- blhsing | 2026-01-19 08:31:09 UTC | #74 [quote="Inada Naoki, post:73, topic:105519, username:methane"] The PEP update I’m considering is as follows: * Add the “removing last newline” in the rejected ideas. * Allow closing quote after non-empty line to remove last newline. * Remove “common longest indent” instead of indent of closing quote, like Julia does. [/quote] I like those changes. In particular, although using the indent of the closing quotes as a dedentation level sounds like a cool feature, I can't really think of a good real-world scenario where I actually need to define a multiline string with a certain level of indentation for all lines. Removing this feature also allows the usage to follow the more prevalent styling convention of indenting the content of a multiline string with one more level than the starting statement and the closing quotes: ``` letters = d''' ____a ____b ____c ''' # letters == 'a\nb\nc\n' ``` as opposed to the weirder styling we'd have to adopt if this feature makes it to d-string: ``` letters = d''' a b c ''' # letters == 'a\nb\nc\n' ``` or: ``` letters = d''' ____a ____b ____c ____''' # letters == 'a\nb\nc\n' ``` ------------------------- methane | 2026-01-19 08:51:03 UTC | #75 Like Julia, I will calculate indent including closing quote line. So you still need to write ```python letters = d''' ____a ____b ____c ____''' # letters == 'a\nb\nc\n' ``` ------------------------- blhsing | 2026-01-19 08:53:59 UTC | #76 Ah OK so I misread your third point. I'm fine with the proposal with or without this feature. ------------------------- methane | 2026-01-20 16:26:46 UTC | #77 This is the PR to update this PEP. Please review it. https://github.com/python/peps/pull/4787 Note that PR is used only for review. Please keep using this thread for discussion of the PEP. ------------------------- h-vetinari | 2026-01-21 02:30:26 UTC | #78 [quote="Inada Naoki, post:77, topic:105519, username:methane"] This is the PR to update this PEP. Please review it. [/quote] I think "Keep opening newline" should be added to rejected ideas, just like you added "Removing newline in the last line". ------------------------- methane | 2026-01-21 07:40:48 UTC | #79 The benefit of the idea of removing the leading newline character, as opposed to the trailing one, is almost self-evident. On the other hand, the benefits of removing the initial line break are unclear. I understand that you think it is more symmetrical and consistent, but I do not agree. If you could write some more objective benefits that anyone would understand, I will add them to Rejected Ideas section. ------------------------- tmk | 2026-01-21 10:39:20 UTC | #80 Sorry if I missed it (I looked back through the thread and didn't see any obviously relevant post), but why was the PEP now switched from the Swift/C#/KDL approach where the closing delimiter determines indentation to Julia's approach where the least indented line determines indentation? EDIT: oh, I see, it's in order to support this: ```python s = d""" Hello World!""" # equivalent to s = "Hello\nWorld!" ``` Maybe the old approach (*closing delimiters determine indentation*) could be added to rejected ideas at least? ------------------------- tmk | 2026-01-21 11:11:05 UTC | #81 [quote="Guido van Rossum, post:70, topic:105519, username:guido"] I like the idea of using d-strings for docstrings, and maybe the d-semantics should implement whatever tools people use to strip whitespace from docstrings today. [/quote] A very incomplete survey of what existing tools do. ### `inspect.cleandoc` `cleandoc`'s behavior is kind of interesting: ```python >>> from inspect import cleandoc >>> cleandoc(""" ... a ... b ... """) 'a\nb' >>> cleandoc(""" ... a ... b ... """) 'a\nb' >>> cleandoc(""" ... a ... b ... """) 'a\nb\n ' ``` It ignores the last line for indentation computation and strips the last newline but only if the closing delimiter has less or equal indentation than the rest. ### sphinx See the implementation [here](https://github.com/sphinx-doc/sphinx/blob/cc7c6f435ad37bb12264f8118c8461b230e6830c/sphinx/util/docstrings.py#L42-L70). It seems to take into account the last line when determining common indentation (in contrast to `cleandoc`) and it always adds an empty line at the end (even if there was none in the string), for reasons to do with reST. ### pyright First [removes common indentation](https://github.com/microsoft/pyright/blob/138238b4b3a3a83d98f7d6bc78322fab69cf165a/packages/pyright-internal/src/analyzer/docStringUtils.ts#L15-L59) and then [removes all empty lines and strips the last newline](https://github.com/microsoft/pyright/blob/138238b4b3a3a83d98f7d6bc78322fab69cf165a/packages/pyright-internal/src/analyzer/docStringConversion.ts#L26-L43). --- So, the situation looks to be inconsistent, unfortunately. ------------------------- barry-scott | 2026-01-21 12:38:13 UTC | #82 How do I create a d’string’ where each lines has some indent now? I have often created fragments that drop into, say a makefile like file, that needs the indent. ------------------------- methane | 2026-01-21 14:28:09 UTC | #83 Unlike `textwrap.dedent()`, the line containing closing quote is always considered when calculating common indent, even if only whitespaces before the closing quote. So you can write like this: ```python # having trailing newline. s = d""" foo bar """ assert s == " foo\n bar\n" # if no trailing newline s = d""" foo bar\ """ assert s == " foo\n bar" # if you don't want to use line continuation s = dr""" foo bar """.rstrip() # or [:-1] assert s == " foo\n bar" ``` ------------------------- effigies | 2026-01-21 14:46:57 UTC | #84 Since it's not part of the PR, I just want to reiterate that I would appreciate it if the PEP would discuss the benefits of `from __future__`, in particular: [quote="Chris Markiewicz, post:68, topic:105519, username:effigies"] Using `from __future__` would allow a transition to a single consistent interpretation [across docstrings and other triple-quoted strings], which benefits users, teachers and students. [/quote] Full comment: https://discuss.python.org/t/pep-822-dedented-multiline-string-d-string/105519/68 I would be happy to write some text, if you like. ------------------------- methane | 2026-01-21 15:15:35 UTC | #85 I already wrote "This could help simplify Python’s grammar in the future." Unless this transition happens, teachers need to teach all behaviors, not only single consistent behavior to students. I didn't think that being able to achieve unified behavior only within a single file was a noteworthy advantage for PEP. ------------------------- effigies | 2026-01-21 15:52:19 UTC | #86 I see the user experience of knowing what the contents of a string will be after parsing as different from the complexity of the grammar, even if they both flow from the same specification. I also think that PEPs should take a long view. The advantage of a transition is that it will eventually allow for teaching a single, consistent behavior. It is a given that teaching multiple behaviors will be necessary during a transition, but it seems strange to claim that eventually consolidating isn't an advantage. Again, I am not asking you to adopt the `__future__` approach, and it's fine to reject it. I just think the advantages are being minimized. Anyway, it's your PEP. Do what you like. ------------------------- guido | 2026-01-21 16:10:48 UTC | #87 [quote="Thomas Kehrenberg, post:81, topic:105519, username:tmk"] A very incomplete survey of what existing tools do. [/quote] Ah, but I was thinking of the stdlib. Doesn’t either the string module or text wrap have a function for this? ------------------------- tmk | 2026-01-21 16:35:35 UTC | #88 [`inspect.cleandoc`](https://docs.python.org/3/library/inspect.html#inspect.cleandoc) is in the standard library, no? ------------------------- guido | 2026-01-21 17:14:34 UTC | #89 Seriously I had never heard of it. :slight_smile: I realize I was thinking of texterap.dedent(), but that was already discussed above, so I withdraw my comment. ------------------------- h-vetinari | 2026-01-22 00:35:34 UTC | #90 [quote="Inada Naoki, post:79, topic:105519, full:true, username:methane"] The benefit of the idea of removing the leading newline character, as opposed to the trailing one, is almost self-evident. [/quote] That's subjective, hence why it should be documented. [quote="Inada Naoki, post:79, topic:105519, full:true, username:methane"] On the other hand, the benefits of removing [keeping?] the initial line break are unclear. I understand that you think it is more symmetrical and consistent, but I do not agree. If you could write some more objective benefits that anyone would understand, I will add them to Rejected Ideas section. [/quote] It's difficult to write text for you, given that the rationale you gave so far [quote="Inada Naoki, post:49, topic:105519, username:methane"] When making technical decisions, many objective facts are taken into consideration, but the final choice of priorities is always subjective. My subjective judgment may not align with yours; however, since I am the author of this PEP, it is ultimately written based on my own subjective decisions. [/quote] is not exactly self-evident either. But as a point for you to reject, here's an attempt. Actually two, because there are two separate mechanisms: the one where nothing is stripped (for consistency), and the Julia approach, where _empty_ initial lines are stripped, but strings _do not_ have to start with a newline. --- ### Keeping the initial newline An argument brought up during discussions on DPO was that all string modifiers (`b`, `f`, `r`, `t` and now `d`) should keep consistent behaviour around initial (& trailing) newlines, not least since it can be surprising that `d`edenting (a horizontal operation) affects newlines (a "vertical" property), aside from introducing an unnecessary inconsistency in the semantics of multi-line strings that users will have to internalise. The proposed semantics would be that the opening line (directly following the triple quotes) would be excepted from dedenting. Prior art for this is [Scala](https://docs.scala-lang.org/overviews/scala-book/two-notes-about-strings.html#multiline-strings) [for example](https://scastie.scala-lang.org/lucmqKVDSgybj1IJhDl0zg), as well as Julia (partially, see below). This is rejected because [...][^1] [^1]: an example in your favour is Java; you're already referencing the JEP for that. ### Allow string content after opening triple-quotes Julia chooses an [approach](https://docs.julialang.org/en/v1/manual/strings/#Triple-Quoted-String-Literals) where a bare newline after the opening quotes is stripped (as in this PEP), but any non-empty content after the opening quotes is kept (and does not participate in dedenting). This is rejected as [...too complex, etc....] ------------------------- h-vetinari | 2026-01-22 01:21:19 UTC | #91 Actually, the prior art in other languages in the PEP are not very consistent at all w.r.t. handling opening and trailing newlines. The only language (of the ones surveyed) with matching behaviour is Java. | Language[^1] | Allows content
after opening `"""`| Strips opening `\n` | Strips trailing `\n` | Allows attaching
trailing `"""`
to content | |--- | --- | --- | ---|- | | [Java](https://www.jdoodle.com/online-java-compiler) | :cross_mark: | :white_check_mark: | :cross_mark: | :white_check_mark: | | [Julia](https://glot.io/snippets/hf2n76nv9g) | :white_check_mark: | :white_check_mark: (if line empty)
:cross_mark: (if non-empty) | :cross_mark: | :white_check_mark: | | [Swift](https://swiftfiddle.com/b4i4tuq2evfxxlp6572nrxgnmq) | :cross_mark: | :white_check_mark: | :white_check_mark: | :cross_mark: | | [C#](https://dotnetfiddle.net/) | :cross_mark: | :white_check_mark: | :white_check_mark: | :cross_mark: | | [PHP](https://www.php.net/manual/en/language.types.string.php) (7.3+) | :cross_mark: | :white_check_mark: | :white_check_mark: | :cross_mark: | | [Scala](https://scastie.scala-lang.org/QQR3gyxFRQ6LM5W1KZvTGg) | :white_check_mark: | :cross_mark: | :cross_mark: | :white_check_mark: | | Python[^2] | :white_check_mark: | :cross_mark: | :cross_mark: | :white_check_mark: | | This PEP | :cross_mark: | :white_check_mark: | :cross_mark: | :white_check_mark: | [^1]: links to online REPLs to test, or at least docs [^2]: all other multiline strings ------------------------- methane | 2026-01-22 02:11:57 UTC | #92 I understand that you feel it is more consistent to leave the first line break, but I cannot agree with you. The first newline is immediately after the opening quote, on the same line. It is not present on the lines within the string's content. On the other hand, the last newline character is neither immediately before the closing quote nor on the same line. That newline character is removed from the content line of the string. Comparisons with other languages are also not a strong enough reason to outweigh the actual benefits when deciding the details. Other languages do not distinguish between dedented multiline strings and non-dedented multiline strings, or even include things that are not syntax at all. There is little value in maintaining consistency with those languages. What's important for d-string is to provide an efficient and easy-to-use syntax for use cases that already use `textwrap.dedent("""...""")` and for use cases where you want to remove indentation from multiline strings but cannot use `textwrap.dedent("""...""")` for some reason. If you can explain the real-world benefits in such use cases, I will add a section for the idea. Regarding the idea of removing the final newline, I explained that it was rejected after comparing its merits and demerits, stating the merit that it would eliminate the need for a workaround in use cases where some indentation is desired but the final newline is not. ------------------------- methane | 2026-01-22 02:25:15 UTC | #93 I wrote "implify Python’s grammar in the future." in the PEP. When I wrote this sentence, I didn't feel the need to explicitly state that simple grammar has advantages for learning and teaching, but was it unclear? I really liked the idea of importing `__future__`. However, I understand the disadvantages of having two types of Python coexist for a long time, and I also understand how much people fear that, so I prioritized a practical solution over my own preference. If the merits are not sufficiently explained compared to the demerits, it is probably because the demerits are the reason why I had to make a choice I did not like. ------------------------- h-vetinari | 2026-01-22 02:32:12 UTC | #94 [quote="Inada Naoki, post:92, topic:105519, username:methane"] Comparisons with other languages are also not a strong enough reason to outweigh the actual benefits when deciding the details. [/quote] Then why do you refer to any prior art at all, if their reasoning and conclusions apparently have no weight? These are not details, but the core mechanics of the feature. [quote="Inada Naoki, post:92, topic:105519, username:methane"] If you can explain the real-world benefits in such use cases, I will add a section for the idea. [/quote] I did. The benefit is staying consistent with existing Python string handling. You don't have to agree with my conclusion to see that this would be an obviously beneficial thing (taken by itself). I even provided a formulation for you to reject with a proper argument -- "I cannot agree with you" is not an argument. [quote="Inada Naoki, post:92, topic:105519, username:methane"] What’s important for d-string is to provide an efficient and easy-to-use syntax for use cases that already use `textwrap.dedent("""...""")` and for use cases where you want to remove indentation from multiline strings but cannot use `textwrap.dedent("""...""")` for some reason. [/quote] This could be the seed for populating the "This is rejected because [...]" placeholder I left for you. ------------------------- methane | 2026-01-22 02:59:22 UTC | #95 Looking at this sheet, it is understood that there is no language that prohibit writing characters after an opening quote and preserves the newline after the opening quote. This PEP is consistent with languages that do not allow characters to be written after any other opening quote. It is a nonsensical design with no merit to not remove line breaks after an opening quote, when you cannot write characters there. P.S. Scala's stripMargin is string method, not syntax. It is totally different. String methods doesn't know where string came from (e.g. multiline string literal, regular string litera, other expressions, read from file...). ------------------------- h-vetinari | 2026-01-22 02:57:32 UTC | #96 [quote="Inada Naoki, post:95, topic:105519, username:methane"] It is a nonsensical design with no merit to not remove line breaks after an opening quote, when you cannot write characters there. [/quote] I agree, but you haven't answered why it must be forbidden to write characters there. To be clear, you're proposing to change a fundamental and long-standing aspect of python's string handling. This part: ![image|482x92, 100%](upload://bbxqZMAVZZF2rFQxKQtaoZo4WmV.png) The onus is on you to make the case why that's beneficial or necessary. Just saying "it's obviously better" is not good enough, you should explain _why_ it would be worse to stay consistent with existing behaviour. That's what I'm asking you to document (and you seem to have the arguments ready to go). I don't understand why that'd be controversial, much less worth resisting. ------------------------- methane | 2026-01-22 03:26:21 UTC | #97 [quote="H. Vetinari, post:96, topic:105519, username:h-vetinari"] I agree, but you haven’t answered why it must be forbidden to write characters there. [/quote] Oh! Did you claim me to write the idea that you agree it's totally meaningless? You should have insisted on adding to the rejected idea the idea of allowing characters to be written after quotes, rather than the idea of not removing the first line break. [quote="H. Vetinari, post:96, topic:105519, username:h-vetinari"] The onus is on you to make the case why that’s beneficial or necessary. Just saying “it’s obviously better” is not good enough, you should explain *why* it would be worse to stay consistent with existing behaviour. [/quote] I and several people already explain it several times. * https://discuss.python.org/t/pep-822-dedented-multiline-string-d-string/105519/43 * https://discuss.python.org/t/pep-822-dedented-multiline-string-d-string/105519/45 * https://discuss.python.org/t/pep-822-dedented-multiline-string-d-string/105519/49 Additionally, you also understood that writing characters after the opening quote would make the dedent rule less clear. * https://discuss.python.org/t/pep-822-dedented-multiline-string-d-string/105519/22 Scala doesn't have dedent syntax. `"""` is just a multiline string. Julia's `"""` have dedent feature, but it is also a general purpose multiline string. On the other hand, d-string is only for dedent use case. This is why d-string is different from `"""` in Scala and Julia. ------------------------- h-vetinari | 2026-01-22 03:49:42 UTC | #98 [quote="Inada Naoki, post:97, topic:105519, username:methane"] I and several people already explain it several times. [/quote] I understand that. What I'm saying is that the arguments should be reflected in the PEP. [quote="Inada Naoki, post:97, topic:105519, username:methane"] You should have insisted on adding to the rejected idea the idea of allowing characters to be written after quotes, rather than the idea of not removing the first line break. [/quote] Both questions are deeply intertwined -- once you forbid characters after the quote, the newline question disappears (but then the prohibition needs a rationale); if you allow characters there, then you need to discuss newline handling. Incidentally, Julia's approach would be _more_ compatible with existing Python that what you're proposing. You can reject the Julia approach as too complex of course, but that too should ideally be documented. [quote="Inada Naoki, post:97, topic:105519, username:methane"] Additionally, you also understood that writing characters after the opening quote would make the dedent rule less clear. [/quote] I don't agree that it's less clear. It's an additional corner case for an unusual usage pattern, for the benefit of being more consistent overall. This is also how Julia handles leading spaces on the opening line. [quote="Inada Naoki, post:95, topic:105519, username:methane"] P.S. Scala’s stripMargin is string method, not syntax. It is totally different. [/quote] I know what you mean, but that it's an implicit method is more of an implementation detail. For the purpose of this discussion (and actual usage in the language), `"""...""".stripMargin` in Scala works exactly like a d-string[^1]. Such strings are not passed around, waiting to have the method applied; it's basically always done at the definition site or not at all. [^1]: modulo the way Scala uses `|` to indicate indentation explicitly, but that's unrelated to newline-handling. ------------------------- methane | 2026-01-22 05:26:04 UTC | #99 [quote="H. Vetinari, post:98, topic:105519, username:h-vetinari"] I know what you mean, but that it’s an implicit method is more of an implementation detail. For the purpose of this discussion (and actual usage in the language), `"""...""".stripMargin` in Scala works exactly like a d-string . Such strings are not passed around, waiting to have the method applied; it’s basically always done at the definition site or not at all. [/quote] String method doesn't know where the string came from. Multiline literal, expressions, reading from file or socket, etc... Even though it was often used with string literals, it is designed to be usable with other strings as well. So stripMargin is like `textwrap.dedent()` rather than d-string. ------------------------- methane | 2026-01-22 05:33:16 UTC | #100 [quote="H. Vetinari, post:98, topic:105519, username:h-vetinari"] Both questions are deeply intertwined – once you forbid characters after the quote, the newline question disappears (but then the prohibition needs a rationale); if you allow characters there, then you need to discuss newline handling. [/quote] But you repeatedly insisted that you would not remove line breaks without the idea of allowing other characters.... Now that you already agree that it’s an obsession with foolish consistency that brings no benefit, I’ll end the framing war over it. I will add to the "Rejected Idea" the reason why I did not design the d-string to be exactly the same as in Julia. ------------------------- h-vetinari | 2026-01-22 06:04:43 UTC | #101 [quote="Inada Naoki, post:99, topic:105519, username:methane"] So stripMargin is like `textwrap.dedent()` rather than d-string. [/quote] Have you written a bit of Scala? I can assure you it's used equivalently to d-strings. And it's not equivalent to `textwrap.dedent` because the postfix method doesn't need parentheses around the whole string. [quote="Inada Naoki, post:100, topic:105519, username:methane"] But you repeatedly insisted that you would not remove line breaks without the idea of allowing other characters… [/quote] That's not what I wrote, but OK, simple misunderstanding. I've been saying -- with examples: [quote="H. Vetinari, post:44, topic:105519, username:h-vetinari"] ``` s = d"""foo ______bar ____baz ____""" ``` [/quote] -- that it's not self-evident why content after the opening quotes _must_ be forbidden (for the feature to function technically or semantically). [quote="Inada Naoki, post:100, topic:105519, username:methane"] Now that you already agree that it’s an obsession with foolish consistency that brings no benefit, I’ll end the framing war over it. [/quote] You're knocking down a strawman. I did say I understand why people want to strip the initial newline (and that this should IMO be done for all strings, not just d-strings). There's nothing foolish about having multiline strings behave regularly. There was no "framing war" from my POV though: I'm only asking you to document **in the PEP** why you're breaking a long-standing invariant. It's strange to me how you take that as aggression ("war"), but let me apologise for any offence given unintentionally. [quote="Inada Naoki, post:100, topic:105519, username:methane"] I will add to the “Rejected Idea” the reason why I did not design the d-string to be exactly the same as in Julia. [/quote] Thanks. ------------------------- methane | 2026-01-22 06:33:13 UTC | #102 [quote="H. Vetinari, post:101, topic:105519, username:h-vetinari"] Have you written a bit of Scala? I can assure you it’s used equivalently to d-strings. And it’s not equivalent to `textwrap.dedent` because the postfix method doesn’t need parentheses around the whole string. [/quote] Parentheses around the whole string are not important here. Unlike string methods, syntax can utilize information such as the type and position of quotes and escape sequences before they are processed. String methods do not even know whether the string was a literal or loaded from another file. There is no way to know whether the first newline comes immediately after the quote or text file starting with blank line. Since I have little experience with Scala, if `stripMargin` is a special method that obtains information not only from the string but also from the compiler, then I am mistaken. ------------------------- Jost | 2026-01-23 15:27:34 UTC | #103 What strikes me about this table is that there is the strong correlation between the two “opening” columns and the two “trailing” columns: * Languages that don’t allow content after opening `"""` always strip the opening `\n` (i.e., they effectively use `"""\n` as the opening marker); whereas languages that allow content after opening `"""` don’t tend to^[with Julia in some cases being the only exception] strip the opening `\n`. * Languages that don’t allow attaching trailing `"""` to content always strip the trailing `\n` (i.e., they effectively use `\n"""` as the trailing marker); whereas languages that allow allow attaching trailing `"""` to content don’t strip the trailing `\n`. In other words, the table could be simplified to: | **Language** | **Opening Marker** | **Trailing Marker** | |--- | --- | ---| | Java | `"""\n` | `"""` | | Julia | `"""\n`^[if line empty] or `"""`^[if line non-empty] | `"""` | | Swift | `"""\n` | `\n"""` | | C# | `"""\n` | `\n"""` | | PHP | `"""\n` | `\n"""` | | Scala | `"""` | `"""` | | Python | `"""` | `"""` | | This PEP | `"""\n` | `"""` | The vast majority of languages (Swift, C#, PHP, Scala and Python) are consistent in having symmetric opening/trailing markers for multiline strings; they just disagree on whether or not the markers include a `\n`. Java is the only language with asymmetric markers, while Julia is the only language where markers depend on the string content. My impression is that we largely agree that Julia’s handling is too complex. Thus, the remaining questions become: **Should we use symmetric or asymmetric opening/trailing markers?** When framed this way, I have a fairly strong preference for symmetric markers, both axiomatically^[Using asymmetric markers feels very wrong to me, almost as bad as mixing different types of brackets. There’s a good reason function calls don’t look like `print("Hello world"}` or list literals aren’t written as `l = [1, 2)`.] and for consistency with the vast majority of languages (including Python itself). **Should the symmetric markers include a `\n` or not?** Here, I have a slight preference for *not*, mainly for consistency with existing Python multiline strings. (This also matches the existing docstring style in PEP-8, as mentioned previously in this thread.) --- *A meta note:* *I’m not sure whether this is the right way of framing the issue or whether it is better to frame it in terms of common use cases for dedenting (as was done previously in this discussion). But while the previous framing left me confused and uncertain, this new framing gives me what looks like an obvious and easy-to-explain answer and there might be some value to that.* ------------------------- methane | 2026-01-24 14:52:18 UTC | #104 It feels unfair to mix statistics on symmetry by mixing multiline string syntax that can be used to write single-line strings without dedent and syntax optimized for writing multiline strings with dedent. In Scala and Python, strings that start with `"""` are also used to write single line strings that include quote characters, such as `"""_'"_"""`. In Julia’s case it is indeed complex, but when the goal is dedent one would mostly use `"""\n`. Allowing content immediately after `"""` is for the same reason as in Scala and Python. The d-string is specialized for multiline strings, Let’s rewrite the previous table selecting only syntax optimized for writing multiline strings. |**Language**|**Opening Marker**|**Trailing Marker**| | --- | --- | --- | |Java|`"""\n`|`"""`| |Julia|`"""\n` or `"""` |`"""`| |This PEP|`"""\n`|`"""`| |Swift|`"""\n`|`\n"""`| |C#|`"""\n`|`\n"""`| |[PHP](https://wiki.php.net/rfc/flexible_heredoc_nowdoc_syntaxes) |`<<