From d5f2b6765a585235f14f4ee6673eb49f50d6663c Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Fri, 2 Sep 2022 08:51:36 +0200 Subject: [PATCH 01/18] Get rid of a default argument in a private API --- src/towncrier/_writer.py | 4 +++- src/towncrier/test/test_write.py | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/towncrier/_writer.py b/src/towncrier/_writer.py index f167391d..a904a46b 100644 --- a/src/towncrier/_writer.py +++ b/src/towncrier/_writer.py @@ -11,7 +11,7 @@ def append_to_newsfile( - directory, filename, start_string, top_line, content, single_file=True + directory, filename, start_string, top_line, content, single_file ): news_file = os.path.join(directory, filename) @@ -35,6 +35,8 @@ def append_to_newsfile( f.write(existing_content.pop(0).rstrip().encode("utf8")) if start_string: f.write(("\n\n" + start_string + "\n").encode("utf8")) + else: + xxxx f.write(content.encode("utf8")) if existing_content: diff --git a/src/towncrier/test/test_write.py b/src/towncrier/test/test_write.py index 628b0dec..5e06accc 100644 --- a/src/towncrier/test/test_write.py +++ b/src/towncrier/test/test_write.py @@ -109,6 +109,7 @@ def test_append_at_top(self): wrap=True, versiondata={"name": "MyProject", "version": "1.0", "date": "never"}, ), + True, ) with open(os.path.join(tempdir, "NEWS.rst")) as f: @@ -221,6 +222,8 @@ def test_append_at_top_with_hint(self): wrap=True, versiondata={"name": "MyProject", "version": "1.0", "date": "never"}, ), + True, + ) with open(os.path.join(tempdir, "NEWS.rst")) as f: @@ -259,6 +262,7 @@ def test_multiple_file_no_start_string(self): start_string=None, top_line="", content=content, + single_file=True, ) with open(os.path.join(tempdir, "NEWS.rst")) as f: From 15e3b9e373cf1c64221d64eb3c85a8fdd5f61248 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Fri, 2 Sep 2022 09:32:44 +0200 Subject: [PATCH 02/18] Fix typo --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 4b5cf77c..f0103aaa 100644 --- a/README.rst +++ b/README.rst @@ -158,7 +158,7 @@ as the option 'title_format', already exists in newsfile, ``ValueError`` will be you "already produced newsfiles for this version". If ``single_file`` is set to ``false`` instead, each versioned ``towncrier build`` will generate a -separate newsfile, whose name is formatted as the patten given by option ``filename``. +separate newsfile, whose name is formatted as the pattern given by option ``filename``. For example, if ``filename="{version}-notes.rst"``, then the release note with version "7.8.9" will be written to the file "7.8.9-notes.rst". If the newsfile already exists, its content will be overwriten with new release note, without throwing a ``ValueError`` warning. From 8e3348558de3fa3c3f629e4d84ecd079c5568fd7 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Fri, 2 Sep 2022 09:33:19 +0200 Subject: [PATCH 03/18] Extract incomprehensible if-conditional into comprehensible function --- src/towncrier/_writer.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/towncrier/_writer.py b/src/towncrier/_writer.py index a904a46b..bfa34429 100644 --- a/src/towncrier/_writer.py +++ b/src/towncrier/_writer.py @@ -16,15 +16,7 @@ def append_to_newsfile( news_file = os.path.join(directory, filename) - if single_file: - if not os.path.exists(news_file): - existing_content = "" - else: - with open(news_file, encoding="utf8") as f: - existing_content = f.read() - existing_content = existing_content.split(start_string, 1) - else: - existing_content = [""] + existing_content = _load_existing_content(news_file, start_string, single_file) if top_line and top_line in existing_content[-1]: raise ValueError("It seems you've already produced newsfiles for this version?") @@ -35,11 +27,22 @@ def append_to_newsfile( f.write(existing_content.pop(0).rstrip().encode("utf8")) if start_string: f.write(("\n\n" + start_string + "\n").encode("utf8")) - else: - xxxx f.write(content.encode("utf8")) if existing_content: if existing_content[0]: f.write(b"\n\n") f.write(existing_content[0].lstrip().encode("utf8")) + + +def _load_existing_content(news_file, start_string, single_file): + if not single_file: + # Per-release news files always start empty. + return [""] + + if not os.path.exists(news_file): + # Non-existent files are equivalent to empty files. + return [""] + + with open(news_file, encoding="utf8") as f: + return f.read().split(start_string, 1) From 6ebe1edcb37c09c613b6ef82acab5cd27d0a3e11 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Fri, 2 Sep 2022 10:36:46 +0200 Subject: [PATCH 04/18] Make the return value a tuple & use meaningful var names --- src/towncrier/_writer.py | 53 +++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/src/towncrier/_writer.py b/src/towncrier/_writer.py index bfa34429..c936e4ea 100644 --- a/src/towncrier/_writer.py +++ b/src/towncrier/_writer.py @@ -13,36 +13,55 @@ def append_to_newsfile( directory, filename, start_string, top_line, content, single_file ): + """ + Write *content* to *directory*/*filename* behind *start_string*. + Double-check *top_line* (i.e. the release header) is not already in the + file. + + if *single_file* is True, add it to an existing file, otherwise create a + fresh one. + """ news_file = os.path.join(directory, filename) - existing_content = _load_existing_content(news_file, start_string, single_file) + header, old_body = _load_existing_content(news_file, start_string, single_file) - if top_line and top_line in existing_content[-1]: + if top_line and top_line in old_body: raise ValueError("It seems you've already produced newsfiles for this version?") - with open(os.path.join(directory, filename), "wb") as f: - - if len(existing_content) > 1: - f.write(existing_content.pop(0).rstrip().encode("utf8")) + with open(news_file, "wb") as f: + if header: + f.write(header.encode("utf8")) if start_string: f.write(("\n\n" + start_string + "\n").encode("utf8")) + else: + xxx f.write(content.encode("utf8")) - if existing_content: - if existing_content[0]: - f.write(b"\n\n") - f.write(existing_content[0].lstrip().encode("utf8")) + if old_body: + f.write(b"\n\n") + f.write(old_body.lstrip().encode("utf8")) def _load_existing_content(news_file, start_string, single_file): - if not single_file: - # Per-release news files always start empty. - return [""] + """ + Try to read *news_file* and split it into header (everything before + *start_string*) and the old body (everything after *start_string*). - if not os.path.exists(news_file): - # Non-existent files are equivalent to empty files. - return [""] + If there's no *start_string*, return empty header. + + Empty file and per-release files have neither. + """ + if not single_file or not os.path.exists(news_file): + # Per-release news files always start empty. + # Non-existent files have no existing content. + return ("", "") with open(news_file, encoding="utf8") as f: - return f.read().split(start_string, 1) + content = f.read() + + t = content.split(start_string, 1) + if len(t) == 2: + return t[0].rstrip(), t[1] + + return "", content From 30760160da5ccf53a86971ea736058cf4794f9e7 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Fri, 2 Sep 2022 10:42:57 +0200 Subject: [PATCH 05/18] Use unicode instead of encoding everywhere --- src/towncrier/_writer.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/towncrier/_writer.py b/src/towncrier/_writer.py index c936e4ea..8034f248 100644 --- a/src/towncrier/_writer.py +++ b/src/towncrier/_writer.py @@ -29,18 +29,18 @@ def append_to_newsfile( if top_line and top_line in old_body: raise ValueError("It seems you've already produced newsfiles for this version?") - with open(news_file, "wb") as f: + with open(news_file, "w", encoding="utf8") as f: if header: - f.write(header.encode("utf8")) + f.write(header) if start_string: - f.write(("\n\n" + start_string + "\n").encode("utf8")) + f.write("\n\n" + start_string + "\n") else: xxx - f.write(content.encode("utf8")) + f.write(content) if old_body: - f.write(b"\n\n") - f.write(old_body.lstrip().encode("utf8")) + f.write("\n\n") + f.write(old_body.lstrip()) def _load_existing_content(news_file, start_string, single_file): @@ -55,7 +55,7 @@ def _load_existing_content(news_file, start_string, single_file): if not single_file or not os.path.exists(news_file): # Per-release news files always start empty. # Non-existent files have no existing content. - return ("", "") + return "", "" with open(news_file, encoding="utf8") as f: content = f.read() From 429929c7703daec5c94f754f6beb8e5d1032a61e Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Fri, 2 Sep 2022 10:48:41 +0200 Subject: [PATCH 06/18] Move body stripping into loading; fix test It makes no sense to consider a whitespace-only block as true. --- src/towncrier/_writer.py | 9 ++++----- src/towncrier/test/test_build.py | 4 +++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/towncrier/_writer.py b/src/towncrier/_writer.py index 8034f248..c0273e97 100644 --- a/src/towncrier/_writer.py +++ b/src/towncrier/_writer.py @@ -34,13 +34,12 @@ def append_to_newsfile( f.write(header) if start_string: f.write("\n\n" + start_string + "\n") - else: - xxx f.write(content) + if old_body: f.write("\n\n") - f.write(old_body.lstrip()) + f.write(old_body) def _load_existing_content(news_file, start_string, single_file): @@ -62,6 +61,6 @@ def _load_existing_content(news_file, start_string, single_file): t = content.split(start_string, 1) if len(t) == 2: - return t[0].rstrip(), t[1] + return t[0].rstrip(), t[1].lstrip() - return "", content + return "", content.lstrip() diff --git a/src/towncrier/test/test_build.py b/src/towncrier/test/test_build.py index c847a68f..7b585496 100644 --- a/src/towncrier/test/test_build.py +++ b/src/towncrier/test/test_build.py @@ -717,6 +717,7 @@ def do_build_once_with(version, fragment_file, fragment): "01-01-2001", "--yes", ], + catch_exceptions=False, ) # not git repository, manually remove fragment file Path(f"newsfragments/{fragment_file}").unlink() @@ -1010,7 +1011,7 @@ def test_start_string(self): with open("newsfragments/123.feature", "w") as f: f.write("Adds levitation") with open("NEWS.rst", "w") as f: - f.write("a line\n\nanother\n\nRelease notes start marker\n") + f.write("a line\n\nanother\n\nRelease notes start marker\na footer!\n") result = runner.invoke( _main, @@ -1045,6 +1046,7 @@ def test_start_string(self): - Adds levitation (#123) + a footer! """ ) From f264eb33187b63907d4e9b37ae103d72a7467dc0 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Fri, 2 Sep 2022 11:08:00 +0200 Subject: [PATCH 07/18] Refactor rendering into seperate function Not only more readable, but also potentially unit-testtable using StringIO. --- src/towncrier/_writer.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/towncrier/_writer.py b/src/towncrier/_writer.py index c0273e97..44ca345d 100644 --- a/src/towncrier/_writer.py +++ b/src/towncrier/_writer.py @@ -24,22 +24,29 @@ def append_to_newsfile( """ news_file = os.path.join(directory, filename) - header, old_body = _load_existing_content(news_file, start_string, single_file) + header, prev_body = _load_existing_content(news_file, start_string, single_file) - if top_line and top_line in old_body: + if top_line and top_line in prev_body: raise ValueError("It seems you've already produced newsfiles for this version?") with open(news_file, "w", encoding="utf8") as f: - if header: - f.write(header) - if start_string: - f.write("\n\n" + start_string + "\n") + _write_news(f, header, start_string, content, prev_body) - f.write(content) - if old_body: - f.write("\n\n") - f.write(old_body) +def _write_news(f, header, start_string, new_content, old_body): + """ + Write complete news into *f*. + """ + if header: + f.write(header) + # If we have a header, we also have a start_string, because the header + # is computed by splitting the file at start_string. + f.write(f"\n\n{start_string}\n") + + f.write(new_content) + + if old_body: + f.write(f"\n\n{old_body}") def _load_existing_content(news_file, start_string, single_file): From 7db8047007ba0405a2fda38da029a573ebacc4e0 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Fri, 2 Sep 2022 11:17:14 +0200 Subject: [PATCH 08/18] Better function name --- src/towncrier/_writer.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/towncrier/_writer.py b/src/towncrier/_writer.py index 44ca345d..88cfaea0 100644 --- a/src/towncrier/_writer.py +++ b/src/towncrier/_writer.py @@ -24,7 +24,9 @@ def append_to_newsfile( """ news_file = os.path.join(directory, filename) - header, prev_body = _load_existing_content(news_file, start_string, single_file) + header, prev_body = _figure_out_existing_content( + news_file, start_string, single_file + ) if top_line and top_line in prev_body: raise ValueError("It seems you've already produced newsfiles for this version?") @@ -49,7 +51,7 @@ def _write_news(f, header, start_string, new_content, old_body): f.write(f"\n\n{old_body}") -def _load_existing_content(news_file, start_string, single_file): +def _figure_out_existing_content(news_file, start_string, single_file): """ Try to read *news_file* and split it into header (everything before *start_string*) and the old body (everything after *start_string*). From 310a6322e8e79538b1da5c52d4f60f6e2ab8c612 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Fri, 2 Sep 2022 11:18:29 +0200 Subject: [PATCH 09/18] Add newsfragment --- src/towncrier/newsfragments/419.misc | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/towncrier/newsfragments/419.misc diff --git a/src/towncrier/newsfragments/419.misc b/src/towncrier/newsfragments/419.misc new file mode 100644 index 00000000..e69de29b From 604fa78802bae2b3bb4c3d3bc33997ee02ad04b6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 2 Sep 2022 09:19:05 +0000 Subject: [PATCH 10/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/towncrier/test/test_write.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/towncrier/test/test_write.py b/src/towncrier/test/test_write.py index 5e06accc..914a1904 100644 --- a/src/towncrier/test/test_write.py +++ b/src/towncrier/test/test_write.py @@ -223,7 +223,6 @@ def test_append_at_top_with_hint(self): versiondata={"name": "MyProject", "version": "1.0", "date": "never"}, ), True, - ) with open(os.path.join(tempdir, "NEWS.rst")) as f: From 8e1051f9301edeb77e70e53fc2fbe810e318945c Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Fri, 2 Sep 2022 14:48:34 +0200 Subject: [PATCH 11/18] Make invalid state unrepresantable --- src/towncrier/_writer.py | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/towncrier/_writer.py b/src/towncrier/_writer.py index 88cfaea0..65f571ea 100644 --- a/src/towncrier/_writer.py +++ b/src/towncrier/_writer.py @@ -32,23 +32,13 @@ def append_to_newsfile( raise ValueError("It seems you've already produced newsfiles for this version?") with open(news_file, "w", encoding="utf8") as f: - _write_news(f, header, start_string, content, prev_body) + if header: + f.write(header) + f.write(content) -def _write_news(f, header, start_string, new_content, old_body): - """ - Write complete news into *f*. - """ - if header: - f.write(header) - # If we have a header, we also have a start_string, because the header - # is computed by splitting the file at start_string. - f.write(f"\n\n{start_string}\n") - - f.write(new_content) - - if old_body: - f.write(f"\n\n{old_body}") + if prev_body: + f.write(f"\n\n{prev_body}") def _figure_out_existing_content(news_file, start_string, single_file): @@ -70,6 +60,6 @@ def _figure_out_existing_content(news_file, start_string, single_file): t = content.split(start_string, 1) if len(t) == 2: - return t[0].rstrip(), t[1].lstrip() + return f"{t[0].rstrip()}\n\n{start_string}\n", t[1].lstrip() return "", content.lstrip() From 70c473edf52a3f22b71048c3564a9e6a2ac5a62e Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Sat, 3 Sep 2022 06:10:40 +0200 Subject: [PATCH 12/18] Update src/towncrier/test/test_write.py Co-authored-by: Adi Roiban --- src/towncrier/test/test_write.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/towncrier/test/test_write.py b/src/towncrier/test/test_write.py index 914a1904..9296900f 100644 --- a/src/towncrier/test/test_write.py +++ b/src/towncrier/test/test_write.py @@ -109,7 +109,7 @@ def test_append_at_top(self): wrap=True, versiondata={"name": "MyProject", "version": "1.0", "date": "never"}, ), - True, + single_file=True, ) with open(os.path.join(tempdir, "NEWS.rst")) as f: From 41b724e1c1b19db0f4deaf975825106654c86cf9 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Sat, 3 Sep 2022 06:10:55 +0200 Subject: [PATCH 13/18] Update src/towncrier/test/test_write.py Co-authored-by: Adi Roiban --- src/towncrier/test/test_write.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/towncrier/test/test_write.py b/src/towncrier/test/test_write.py index 9296900f..40ecab86 100644 --- a/src/towncrier/test/test_write.py +++ b/src/towncrier/test/test_write.py @@ -222,7 +222,7 @@ def test_append_at_top_with_hint(self): wrap=True, versiondata={"name": "MyProject", "version": "1.0", "date": "never"}, ), - True, + single_file=True, ) with open(os.path.join(tempdir, "NEWS.rst")) as f: From 10ccc6192799158d4984bc4b13d1ec065aaf4f70 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Sat, 3 Sep 2022 06:30:30 +0200 Subject: [PATCH 14/18] Disable maxDiff --- src/towncrier/test/test_write.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/towncrier/test/test_write.py b/src/towncrier/test/test_write.py index 40ecab86..0b03e1cd 100644 --- a/src/towncrier/test/test_write.py +++ b/src/towncrier/test/test_write.py @@ -18,6 +18,8 @@ class WritingTests(TestCase): + maxDiff = None + def test_append_at_top(self): fragments = OrderedDict( From 87492b74439ec7c5532be8b9ad4ceaaab627e03e Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Sat, 3 Sep 2022 06:40:55 +0200 Subject: [PATCH 15/18] Fix typo --- src/towncrier/test/test_build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/towncrier/test/test_build.py b/src/towncrier/test/test_build.py index 7b585496..5e9dadc7 100644 --- a/src/towncrier/test/test_build.py +++ b/src/towncrier/test/test_build.py @@ -781,7 +781,7 @@ def test_bullet_points_false(self): """ When all_bullets is false, subsequent lines are not indented. - The automatic ticket number inserted by towcier will allign with the + The automatic ticket number inserted by towncrier will align with the manual bullet. """ runner = CliRunner() From e13c5672956adbcf0b49d57d0792723db3768ba6 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Sat, 3 Sep 2022 07:58:03 +0200 Subject: [PATCH 16/18] Fix reformat artifact --- src/towncrier/test/test_build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/towncrier/test/test_build.py b/src/towncrier/test/test_build.py index 5e9dadc7..1ec446e8 100644 --- a/src/towncrier/test/test_build.py +++ b/src/towncrier/test/test_build.py @@ -795,7 +795,7 @@ def test_bullet_points_false(self): ) os.mkdir("newsfragments") with open("newsfragments/123.feature", "w") as f: - f.write("wow!\n" "~~~~\n" "\n" "No indentation at all.") + f.write("wow!\n~~~~\n\nNo indentation at all.") with open("newsfragments/124.bugfix", "w") as f: f.write("#. Numbered bullet list.") with open("newsfragments/125.removal", "w") as f: From 710407e1093424d680a7f1f5743e36b0f372a7ec Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Sat, 3 Sep 2022 08:00:56 +0200 Subject: [PATCH 17/18] Fix newlines and add explanations --- src/towncrier/_writer.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/towncrier/_writer.py b/src/towncrier/_writer.py index 65f571ea..ed033fee 100644 --- a/src/towncrier/_writer.py +++ b/src/towncrier/_writer.py @@ -31,7 +31,10 @@ def append_to_newsfile( if top_line and top_line in prev_body: raise ValueError("It seems you've already produced newsfiles for this version?") - with open(news_file, "w", encoding="utf8") as f: + # Leave newlines alone. This probably leads to inconsistent newlines, + # because we've loaded existing content with universal newlines, but that's + # the original behavior. + with open(news_file, "w", encoding="utf8", newline="") as f: if header: f.write(header) @@ -55,6 +58,8 @@ def _figure_out_existing_content(news_file, start_string, single_file): # Non-existent files have no existing content. return "", "" + # If we didn't use universal newlines here, we wouldn't find *start_string* + # which usually contains a `\n`. with open(news_file, encoding="utf8") as f: content = f.read() From e8acd1becb39c39546175d7f3e7c17bf58b67940 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Sat, 3 Sep 2022 09:14:11 +0200 Subject: [PATCH 18/18] Use idiomatic pathlib --- src/towncrier/_writer.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/towncrier/_writer.py b/src/towncrier/_writer.py index ed033fee..e58535b6 100644 --- a/src/towncrier/_writer.py +++ b/src/towncrier/_writer.py @@ -6,8 +6,7 @@ affecting existing content. """ - -import os +from pathlib import Path def append_to_newsfile( @@ -22,7 +21,7 @@ def append_to_newsfile( if *single_file* is True, add it to an existing file, otherwise create a fresh one. """ - news_file = os.path.join(directory, filename) + news_file = Path(directory) / filename header, prev_body = _figure_out_existing_content( news_file, start_string, single_file @@ -34,7 +33,7 @@ def append_to_newsfile( # Leave newlines alone. This probably leads to inconsistent newlines, # because we've loaded existing content with universal newlines, but that's # the original behavior. - with open(news_file, "w", encoding="utf8", newline="") as f: + with news_file.open("w", encoding="utf8", newline="") as f: if header: f.write(header) @@ -53,14 +52,14 @@ def _figure_out_existing_content(news_file, start_string, single_file): Empty file and per-release files have neither. """ - if not single_file or not os.path.exists(news_file): + if not single_file or not news_file.exists(): # Per-release news files always start empty. # Non-existent files have no existing content. return "", "" # If we didn't use universal newlines here, we wouldn't find *start_string* # which usually contains a `\n`. - with open(news_file, encoding="utf8") as f: + with news_file.open(encoding="utf8") as f: content = f.read() t = content.split(start_string, 1)