Skip to content

Commit

Permalink
Move normalize_path caching to an instance on UninstallPathSet
Browse files Browse the repository at this point in the history
  • Loading branch information
DefaultRyan authored and sbidoul committed Apr 10, 2023
1 parent 1bb8496 commit 58882a1
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 20 deletions.
16 changes: 10 additions & 6 deletions src/pip/_internal/req/req_uninstall.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from pip._internal.utils.logging import getLogger, indent_log
from pip._internal.utils.misc import ask, is_local, normalize_path, normalize_path_cached, renames, rmtree
from pip._internal.utils.temp_dir import AdjacentTempDirectory, TempDirectory
from pip._internal.utils.virtualenv import running_under_virtualenv

logger = getLogger(__name__)

Expand Down Expand Up @@ -312,22 +313,25 @@ def __init__(self, dist: BaseDistribution) -> None:
self._pth: Dict[str, UninstallPthEntries] = {}
self._dist = dist
self._moved_paths = StashedUninstallPathSet()
normalize_path_cached.cache_clear()
self._normalize_path_cached = functools.lru_cache(maxsize=256)(normalize_path)

def _permitted(self, path: str) -> bool:
"""
Return True if the given path is one we are permitted to
remove/modify, False otherwise.
"""
return is_local(path)
# aka is_local, but caching normalized sys.prefix
if not running_under_virtualenv():
return True
return path.startswith(self._normalize_path_cached(sys.prefix))

def add(self, path: str) -> None:
head, tail = os.path.split(path)

# we normalize the head to resolve parent directory symlinks, but not
# the tail, since we only want to uninstall symlinks, not their targets
path = os.path.join(normalize_path_cached(head), os.path.normcase(tail))
path = os.path.join(self._normalize_path_cached(head), os.path.normcase(tail))

if not os.path.exists(path):
return
Expand All @@ -342,7 +346,7 @@ def add(self, path: str) -> None:
self.add(cache_from_source(path))

def add_pth(self, pth_file: str, entry: str) -> None:
pth_file = normalize_path_cached(pth_file)
pth_file = self._normalize_path_cached(pth_file)
if self._permitted(pth_file):
if pth_file not in self._pth:
self._pth[pth_file] = UninstallPthEntries(pth_file)
Expand Down Expand Up @@ -435,7 +439,7 @@ def from_dist(cls, dist: BaseDistribution) -> "UninstallPathSet":
)
return cls(dist)

normalized_dist_location = normalize_path_cached(dist_location)
normalized_dist_location = normalize_path(dist_location)
if not dist.local:
logger.info(
"Not uninstalling %s at %s, outside environment %s",
Expand Down Expand Up @@ -532,7 +536,7 @@ def from_dist(cls, dist: BaseDistribution) -> "UninstallPathSet":
# above, so this only covers the setuptools-style editable.
with open(develop_egg_link) as fh:
link_pointer = os.path.normcase(fh.readline().strip())
normalized_link_pointer = normalize_path_cached(link_pointer)
normalized_link_pointer = paths_to_remove._normalize_path_cached(link_pointer)
assert os.path.samefile(
normalized_link_pointer, normalized_dist_location
), (
Expand Down
15 changes: 1 addition & 14 deletions src/pip/_internal/utils/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import contextlib
import errno
import functools
import getpass
import hashlib
import io
Expand Down Expand Up @@ -295,17 +294,6 @@ def normalize_path(path: str, resolve_symlinks: bool = True) -> str:
return os.path.normcase(path)


@functools.lru_cache(maxsize=128)
def normalize_path_cached(path: str, resolve_symlinks: bool = True) -> str:
"""
Cache the results of normalize_path when called frequently during certain
operations. Separate function because it is probably unsafe to
cache normalize_path in the general case, e.g. symlinks can be changed
while the process is running.
"""
return normalize_path(path, resolve_symlinks)


def splitext(path: str) -> Tuple[str, str]:
"""Like os.path.splitext, but take off .tar too"""
base, ext = posixpath.splitext(path)
Expand Down Expand Up @@ -343,8 +331,7 @@ def is_local(path: str) -> bool:
"""
if not running_under_virtualenv():
return True
# Safe to call cached because sys.prefix shouldn't change
return path.startswith(normalize_path_cached(sys.prefix))
return path.startswith(normalize_path(sys.prefix))


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

0 comments on commit 58882a1

Please sign in to comment.