Skip to content

Commit d46fd99

Browse files
DefaultRyansbidoul
authored andcommitted
cache normalize_path in req_uninstall and is_local
1 parent 5d4a974 commit d46fd99

File tree

2 files changed

+20
-6
lines changed

2 files changed

+20
-6
lines changed

src/pip/_internal/req/req_uninstall.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from pip._internal.utils.compat import WINDOWS
1212
from pip._internal.utils.egg_link import egg_link_path_from_location
1313
from pip._internal.utils.logging import getLogger, indent_log
14-
from pip._internal.utils.misc import ask, is_local, normalize_path, renames, rmtree
14+
from pip._internal.utils.misc import ask, is_local, normalize_path, normalize_path_cached, renames, rmtree
1515
from pip._internal.utils.temp_dir import AdjacentTempDirectory, TempDirectory
1616

1717
logger = getLogger(__name__)
@@ -312,6 +312,7 @@ def __init__(self, dist: BaseDistribution) -> None:
312312
self._pth: Dict[str, UninstallPthEntries] = {}
313313
self._dist = dist
314314
self._moved_paths = StashedUninstallPathSet()
315+
normalize_path_cached.cache_clear()
315316

316317
def _permitted(self, path: str) -> bool:
317318
"""
@@ -326,7 +327,7 @@ def add(self, path: str) -> None:
326327

327328
# we normalize the head to resolve parent directory symlinks, but not
328329
# the tail, since we only want to uninstall symlinks, not their targets
329-
path = os.path.join(normalize_path(head), os.path.normcase(tail))
330+
path = os.path.join(normalize_path_cached(head), os.path.normcase(tail))
330331

331332
if not os.path.exists(path):
332333
return
@@ -341,7 +342,7 @@ def add(self, path: str) -> None:
341342
self.add(cache_from_source(path))
342343

343344
def add_pth(self, pth_file: str, entry: str) -> None:
344-
pth_file = normalize_path(pth_file)
345+
pth_file = normalize_path_cached(pth_file)
345346
if self._permitted(pth_file):
346347
if pth_file not in self._pth:
347348
self._pth[pth_file] = UninstallPthEntries(pth_file)
@@ -434,7 +435,7 @@ def from_dist(cls, dist: BaseDistribution) -> "UninstallPathSet":
434435
)
435436
return cls(dist)
436437

437-
normalized_dist_location = normalize_path(dist_location)
438+
normalized_dist_location = normalize_path_cached(dist_location)
438439
if not dist.local:
439440
logger.info(
440441
"Not uninstalling %s at %s, outside environment %s",
@@ -531,7 +532,7 @@ def from_dist(cls, dist: BaseDistribution) -> "UninstallPathSet":
531532
# above, so this only covers the setuptools-style editable.
532533
with open(develop_egg_link) as fh:
533534
link_pointer = os.path.normcase(fh.readline().strip())
534-
normalized_link_pointer = normalize_path(link_pointer)
535+
normalized_link_pointer = normalize_path_cached(link_pointer)
535536
assert os.path.samefile(
536537
normalized_link_pointer, normalized_dist_location
537538
), (

src/pip/_internal/utils/misc.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import contextlib
55
import errno
6+
import functools
67
import getpass
78
import hashlib
89
import io
@@ -294,6 +295,17 @@ def normalize_path(path: str, resolve_symlinks: bool = True) -> str:
294295
return os.path.normcase(path)
295296

296297

298+
@functools.lru_cache
299+
def normalize_path_cached(path: str, resolve_symlinks: bool = True) -> str:
300+
"""
301+
Cache the results of normalize_path when called frequently during certain
302+
operations. Separate function because it is probably unsafe to
303+
cache normalize_path in the general case, e.g. symlinks can be changed
304+
while the process is running.
305+
"""
306+
return normalize_path(str, resolve_symlinks)
307+
308+
297309
def splitext(path: str) -> Tuple[str, str]:
298310
"""Like os.path.splitext, but take off .tar too"""
299311
base, ext = posixpath.splitext(path)
@@ -331,7 +343,8 @@ def is_local(path: str) -> bool:
331343
"""
332344
if not running_under_virtualenv():
333345
return True
334-
return path.startswith(normalize_path(sys.prefix))
346+
# Safe to call cached because sys.prefix shouldn't change
347+
return path.startswith(normalize_path_cached(sys.prefix))
335348

336349

337350
def write_output(msg: Any, *args: Any) -> None:

0 commit comments

Comments
 (0)