diff --git a/Doc/library/textwrap.rst b/Doc/library/textwrap.rst index a58b460fef409c..3c96c0e9cc0a38 100644 --- a/Doc/library/textwrap.rst +++ b/Doc/library/textwrap.rst @@ -102,6 +102,10 @@ functions should be good enough; otherwise, you should use an instance of print(repr(s)) # prints ' hello\n world\n ' print(repr(dedent(s))) # prints 'hello\n world\n' + .. versionchanged:: 3.14 + The :func:`!dedent` function now correctly normalizes blank lines containing + only whitespace characters. Previously, the implementation only normalized + blank lines containing tabs and spaces. .. function:: indent(text, prefix, predicate=None) diff --git a/Doc/requirements.txt b/Doc/requirements.txt index 716772b7f28d99..d0107744ecbe85 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -7,7 +7,7 @@ # won't suddenly cause build failures. Updating the version is fine as long # as no warnings are raised by doing so. # Keep this version in sync with ``Doc/conf.py``. -sphinx~=9.0.0 +sphinx<9.0.0 blurb diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 9459b73bcb502f..c12a1920b10722 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -2265,6 +2265,15 @@ pdb (Contributed by Tian Gao in :gh:`124533`.) +textwrap +-------- + +* Optimize the :func:`~textwrap.dedent` function, improving performance by + an average of 2.4x, with larger improvements for bigger inputs, + and fix a bug with incomplete normalization of blank lines with whitespace + characters other than space and tab. + + uuid ---- diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 2748b5941eade2..4a07258f8d6d07 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -196,7 +196,11 @@ def shouldRollover(self, record): if self.stream is None: # delay was set... self.stream = self._open() if self.maxBytes > 0: # are we rolling over? - pos = self.stream.tell() + try: + pos = self.stream.tell() + except io.UnsupportedOperation: + # gh-143237: Never rollover a named pipe. + return False if not pos: # gh-116263: Never rollover an empty file return False diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 8815426fc99c39..848084e6e36878 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -25,6 +25,7 @@ import codecs import configparser +import contextlib import copy import datetime import pathlib @@ -6369,6 +6370,32 @@ def test_should_not_rollover_non_file(self): self.assertFalse(rh.shouldRollover(self.next_rec())) rh.close() + @unittest.skipUnless(hasattr(os, "mkfifo"), 'requires os.mkfifo()') + def test_should_not_rollover_named_pipe(self): + # gh-143237 - test with non-seekable special file (named pipe) + filename = os_helper.TESTFN + self.addCleanup(os_helper.unlink, filename) + try: + os.mkfifo(filename) + except PermissionError as e: + self.skipTest('os.mkfifo(): %s' % e) + + data = 'not read' + def other_side(): + nonlocal data + with open(filename, 'rb') as f: + data = f.read() + + thread = threading.Thread(target=other_side) + with threading_helper.start_threads([thread]): + rh = logging.handlers.RotatingFileHandler( + filename, encoding="utf-8", maxBytes=1) + with contextlib.closing(rh): + m = self.next_rec() + self.assertFalse(rh.shouldRollover(m)) + rh.emit(m) + self.assertEqual(data.decode(), m.msg + os.linesep) + def test_should_rollover(self): with open(self.fn, 'wb') as f: f.write(b'\n') diff --git a/Misc/NEWS.d/next/Library/2025-12-28-20-28-05.gh-issue-143237.q1ymuA.rst b/Misc/NEWS.d/next/Library/2025-12-28-20-28-05.gh-issue-143237.q1ymuA.rst new file mode 100644 index 00000000000000..131bebcd984ac2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-28-20-28-05.gh-issue-143237.q1ymuA.rst @@ -0,0 +1 @@ +Fix support of named pipes in the rotating :mod:`logging` handlers.