From 7fba99eadb3349a6d49d02f13b1fddf44c674393 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Thu, 5 Jan 2023 22:11:50 +0000 Subject: [PATCH] gh-100562: improve performance of `pathlib.Path.absolute()` (GH-100563) Increase performance of the `absolute()` method by calling `os.getcwd()` directly, rather than using the `Path.cwd()` class method. This avoids constructing an extra `Path` object (and the parsing/normalization that comes with it). Decrease performance of the `cwd()` class method by calling the `Path.absolute()` method, rather than using `os.getcwd()` directly. This involves constructing an extra `Path` object. We do this to maintain a longstanding pattern where `os` functions are called from only one place, which allows them to be more readily replaced by users. As `cwd()` is generally called at most once within user programs, it's a good bargain. ```shell # before $ ./python -m timeit -s 'from pathlib import Path; p = Path("foo", "bar")' 'p.absolute()' 50000 loops, best of 5: 9.04 usec per loop # after $ ./python -m timeit -s 'from pathlib import Path; p = Path("foo", "bar")' 'p.absolute()' 50000 loops, best of 5: 5.02 usec per loop ``` Automerge-Triggered-By: GH:AlexWaygood --- Lib/pathlib.py | 12 +++++++----- .../2022-12-28-00-28-43.gh-issue-100562.Hic0Z0.rst | 3 +++ 2 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-12-28-00-28-43.gh-issue-100562.Hic0Z0.rst diff --git a/Lib/pathlib.py b/Lib/pathlib.py index b959e85d18406a..a0678f61b63211 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -748,10 +748,12 @@ def __exit__(self, t, v, tb): @classmethod def cwd(cls): - """Return a new path pointing to the current working directory - (as returned by os.getcwd()). - """ - return cls(os.getcwd()) + """Return a new path pointing to the current working directory.""" + # We call 'absolute()' rather than using 'os.getcwd()' directly to + # enable users to replace the implementation of 'absolute()' in a + # subclass and benefit from the new behaviour here. This works because + # os.path.abspath('.') == os.getcwd(). + return cls().absolute() @classmethod def home(cls): @@ -825,7 +827,7 @@ def absolute(self): """ if self.is_absolute(): return self - return self._from_parts([self.cwd()] + self._parts) + return self._from_parts([os.getcwd()] + self._parts) def resolve(self, strict=False): """ diff --git a/Misc/NEWS.d/next/Library/2022-12-28-00-28-43.gh-issue-100562.Hic0Z0.rst b/Misc/NEWS.d/next/Library/2022-12-28-00-28-43.gh-issue-100562.Hic0Z0.rst new file mode 100644 index 00000000000000..56a4265897199a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-28-00-28-43.gh-issue-100562.Hic0Z0.rst @@ -0,0 +1,3 @@ +Improve performance of :meth:`pathlib.Path.absolute` by nearly 2x. This comes +at the cost of a performance regression in :meth:`pathlib.Path.cwd`, which is +generally used less frequently in user code.