From 90c3018dec89cf68c7beac4c5491024f6bc01101 Mon Sep 17 00:00:00 2001 From: barneygale Date: Sat, 18 Nov 2023 18:23:31 +0000 Subject: [PATCH 1/3] GH-77621: Delay some imports from pathlib Import `contextlib`, `glob` and `re` only as required. --- Lib/pathlib.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 73f87b9acbf9d5..b0221afd553c6a 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -5,14 +5,11 @@ operating systems. """ -import contextlib import functools -import glob import io import ntpath import os import posixpath -import re import sys import warnings from _collections_abc import Sequence @@ -80,12 +77,13 @@ def _is_case_sensitive(pathmod): def _compile_pattern(pat, sep, case_sensitive): """Compile given glob pattern to a re.Pattern object (observing case sensitivity).""" - flags = re.NOFLAG if case_sensitive else re.IGNORECASE - regex = glob.translate(pat, recursive=True, include_hidden=True, seps=sep) + from glob import translate + regex = translate(pat, recursive=True, include_hidden=True, seps=sep) # The string representation of an empty path is a single dot ('.'). Empty # paths shouldn't match wildcards, so we consume it with an atomic group. regex = r'(\.\Z)?+' + regex - return re.compile(regex, flags).match + from re import compile, NOFLAG, IGNORECASE + return compile(regex, flags=NOFLAG if case_sensitive else IGNORECASE).match def _select_children(parent_paths, dir_only, follow_symlinks, match): @@ -989,7 +987,8 @@ def iterdir(self): def _scandir(self): # Emulate os.scandir(), which returns an object that can be used as a # context manager. This method is called by walk() and glob(). - return contextlib.nullcontext(self.iterdir()) + from contextlib import nullcontext + return nullcontext(self.iterdir()) def _make_child_relpath(self, name): path_str = str(self) From 6bfcf662d9c9a2962b31d9ac8cc17b9952414e0d Mon Sep 17 00:00:00 2001 From: barneygale Date: Tue, 21 Nov 2023 02:58:26 +0000 Subject: [PATCH 2/3] Add news entry --- .../next/Library/2023-11-21-02-58-14.gh-issue-77621.MYv5XS.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-11-21-02-58-14.gh-issue-77621.MYv5XS.rst diff --git a/Misc/NEWS.d/next/Library/2023-11-21-02-58-14.gh-issue-77621.MYv5XS.rst b/Misc/NEWS.d/next/Library/2023-11-21-02-58-14.gh-issue-77621.MYv5XS.rst new file mode 100644 index 00000000000000..f3e6efc389afca --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-21-02-58-14.gh-issue-77621.MYv5XS.rst @@ -0,0 +1,2 @@ +Slightly improve the import time of the :mod:`pathlib` module by deferring +some imports. Patch by Barney Gale. From 97f2836c2def9da8042ef379486f3e1d68d3f9d8 Mon Sep 17 00:00:00 2001 From: barneygale Date: Sat, 25 Nov 2023 17:24:58 +0000 Subject: [PATCH 3/3] Speed up `_compile_pattern()` Co-authored-by: Alex Waygood --- Lib/pathlib.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index b0221afd553c6a..a20c003f05fbe8 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -72,18 +72,23 @@ def _is_case_sensitive(pathmod): # Globbing helpers # +re = glob = None + @functools.lru_cache(maxsize=256) def _compile_pattern(pat, sep, case_sensitive): """Compile given glob pattern to a re.Pattern object (observing case sensitivity).""" - from glob import translate - regex = translate(pat, recursive=True, include_hidden=True, seps=sep) + global re, glob + if re is None: + import re, glob + + flags = re.NOFLAG if case_sensitive else re.IGNORECASE + regex = glob.translate(pat, recursive=True, include_hidden=True, seps=sep) # The string representation of an empty path is a single dot ('.'). Empty # paths shouldn't match wildcards, so we consume it with an atomic group. regex = r'(\.\Z)?+' + regex - from re import compile, NOFLAG, IGNORECASE - return compile(regex, flags=NOFLAG if case_sensitive else IGNORECASE).match + return re.compile(regex, flags=flags).match def _select_children(parent_paths, dir_only, follow_symlinks, match):