Skip to content

Commit

Permalink
Fix paths
Browse files Browse the repository at this point in the history
Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
  • Loading branch information
gaborbernat committed Jun 13, 2023
1 parent 680e03d commit 914f8bc
Show file tree
Hide file tree
Showing 19 changed files with 89 additions and 95 deletions.
4 changes: 2 additions & 2 deletions src/virtualenv/activation/activator.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

import os
from abc import ABCMeta, abstractmethod
from pathlib import Path


class Activator(metaclass=ABCMeta):
Expand All @@ -13,7 +13,7 @@ def __init__(self, options) -> None:
:param options: the parsed options as defined within :meth:`add_parser_arguments`
"""
self.flag_prompt = os.path.basename(os.getcwd()) if options.prompt == "." else options.prompt
self.flag_prompt = Path.cwd().parent if options.prompt == "." else options.prompt

@classmethod
def supports(cls, interpreter): # noqa: ARG003
Expand Down
11 changes: 6 additions & 5 deletions src/virtualenv/activation/python/activate_this.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,25 @@
import os
import site
import sys
from pathlib import Path

try:
abs_file = os.path.abspath(__file__)
abs_file = Path(__file__)
except NameError as exc:
msg = "You must use exec(open(this_file).read(), {'__file__': this_file}))"
raise AssertionError(msg) from exc

bin_dir = os.path.dirname(abs_file)
base = bin_dir[: -len("__BIN_NAME__") - 1] # strip away the bin part from the __file__, plus the path separator
bin_dir = abs_file.parent
base = str(bin_dir)[: -len("__BIN_NAME__") - 1] # strip away the bin part from the __file__, plus the path separator

# prepend bin to PATH (this file is inside the bin directory)
os.environ["PATH"] = os.pathsep.join([bin_dir, *os.environ.get("PATH", "").split(os.pathsep)])
os.environ["PATH"] = os.pathsep.join([str(bin_dir), *os.environ.get("PATH", "").split(os.pathsep)])
os.environ["VIRTUAL_ENV"] = base # virtual env is right above bin directory

# add the virtual environments libraries to the host python import mechanism
prev_length = len(sys.path)
for lib in "__LIB_FOLDERS__".split(os.pathsep):
path = os.path.realpath(os.path.join(bin_dir, lib))
path = str(bin_dir / lib)
site.addsitedir(path.decode("utf-8") if "__DECODE_PATH__" else path)
sys.path[:] = sys.path[prev_length:] + sys.path[0:prev_length]

Expand Down
11 changes: 6 additions & 5 deletions src/virtualenv/app_data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import logging
import os
from pathlib import Path

from platformdirs import user_data_dir

Expand All @@ -29,20 +30,20 @@ def make_app_data(folder, **kwargs):

if folder is None:
folder = _default_app_data_dir(env)
folder = os.path.abspath(folder)
folder = Path(folder).absolute()

if is_read_only:
return ReadOnlyAppData(folder)
return ReadOnlyAppData(str(folder))

if not os.path.isdir(folder):
if not folder.is_dir():
try:
os.makedirs(folder)
folder.mkdir(parents=True)
logging.debug("created app data folder %s", folder)
except OSError as exception:
logging.info("could not create app data folder %s due to %r", folder, exception)

if os.access(folder, os.W_OK):
return AppDataDiskFolder(folder)
return AppDataDiskFolder(str(folder))
logging.debug("app data folder %s has no write access", folder)
return TempAppData()

Expand Down
4 changes: 2 additions & 2 deletions src/virtualenv/app_data/read_only.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

import os.path
from pathlib import Path

from virtualenv.util.lock import NoOpFileLock

Expand All @@ -11,7 +11,7 @@ class ReadOnlyAppData(AppDataDiskFolder):
can_update = False

def __init__(self, folder: str) -> None:
if not os.path.isdir(folder):
if not Path(folder).exists():
msg = f"read-only app data directory {folder} does not exist"
raise RuntimeError(msg)
super().__init__(folder)
Expand Down
7 changes: 4 additions & 3 deletions src/virtualenv/create/creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from .pyenv_cfg import PyEnvCfg

HERE = Path(os.path.abspath(__file__)).parent
HERE = Path(__file__).parent
DEBUG_SCRIPT = HERE / "debug.py"


Expand Down Expand Up @@ -138,7 +138,8 @@ def non_write_able(dest, value):
if value.exists() and value.is_file():
msg = f"the destination {value} already exists and is a file"
raise ArgumentTypeError(msg)
dest = Path(os.path.abspath(str(value))).resolve() # on Windows absolute does not imply resolve so use both
# on Windows absolute does not imply resolve so use both
dest = Path(os.path.abspath(str(value))).resolve() # noqa: PTH100
value = dest
while dest:
if dest.exists():
Expand All @@ -162,7 +163,7 @@ def run(self):

def set_pyenv_cfg(self):
self.pyenv_cfg.content = OrderedDict()
self.pyenv_cfg["home"] = os.path.dirname(os.path.abspath(self.interpreter.system_executable))
self.pyenv_cfg["home"] = str(Path(self.interpreter.system_executable).absolute().parent)
self.pyenv_cfg["implementation"] = self.interpreter.implementation
self.pyenv_cfg["version_info"] = ".".join(str(i) for i in self.interpreter.version_info)
self.pyenv_cfg["virtualenv"] = __version__
Expand Down
6 changes: 3 additions & 3 deletions src/virtualenv/create/via_global_ref/_virtualenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

from __future__ import annotations

import os
import sys
from contextlib import suppress
from pathlib import Path

VIRTUALENV_PATCH_FILE = os.path.join(__file__)
VIRTUALENV_PATCH_FILE = Path(__file__)


def patch_dist(dist):
Expand All @@ -24,7 +24,7 @@ def parse_config_files(self, *args, **kwargs):
install = self.get_option_dict("install")

if "prefix" in install: # the prefix governs where to install the libraries
install["prefix"] = VIRTUALENV_PATCH_FILE, os.path.abspath(sys.prefix)
install["prefix"] = VIRTUALENV_PATCH_FILE, str(Path(sys.prefix).absolute())
for base in ("purelib", "platlib", "headers", "scripts", "data"):
key = f"install_{base}"
if key in install: # do not allow global configs to hijack venv paths
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def do_file(file, offset=0, size=maxint):

assert len(what) >= len(value) # noqa: S101

with open(at_path, "r+b") as f:
with Path(at_path).open("r+b") as f:
do_file(f)

return mach_o_change
Expand Down
29 changes: 15 additions & 14 deletions src/virtualenv/discovery/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging
import os
import sys
from pathlib import Path

from virtualenv.info import IS_WIN

Expand Down Expand Up @@ -74,13 +75,13 @@ def propose_interpreters(spec, try_first_with, app_data, env=None): # noqa: C90
# 0. try with first
env = os.environ if env is None else env
for py_exe in try_first_with:
path = os.path.abspath(py_exe)
path = Path(py_exe).absolute()
try:
os.lstat(path) # Windows Store Python does not work with os.path.exists, but does for os.lstat
os.lstat(str(path)) # Windows Store Python does not work with os.path.exists, but does for os.lstat
except OSError:
pass
else:
yield PythonInfo.from_exe(os.path.abspath(path), app_data, env=env), True
yield PythonInfo.from_exe(str(Path(path).absolute()), app_data, env=env), True

# 1. if it's a path and exists
if spec.path is not None:
Expand All @@ -90,7 +91,7 @@ def propose_interpreters(spec, try_first_with, app_data, env=None): # noqa: C90
if spec.is_abs:
raise
else:
yield PythonInfo.from_exe(os.path.abspath(spec.path), app_data, env=env), True
yield PythonInfo.from_exe(str(Path(spec.path).absolute()), app_data, env=env), True
if spec.is_abs:
return
else:
Expand All @@ -112,7 +113,7 @@ def propose_interpreters(spec, try_first_with, app_data, env=None): # noqa: C90
for candidate, match in possible_specs(spec):
found = check_path(candidate, path_str)
if found is not None:
exe = os.path.abspath(found)
exe = str(Path(found).absolute())
if exe not in tested_exes:
tested_exes.add(exe)
interpreter = PathPythonInfo.from_exe(exe, app_data, raise_on_error=False, env=env)
Expand All @@ -127,7 +128,7 @@ def get_paths(env):
path = os.confstr("CS_PATH")
except (AttributeError, ValueError):
path = os.defpath
return [] if not path else [p for p in path.split(os.pathsep) if os.path.exists(p)]
return [] if not path else [p for p in path.split(os.pathsep) if Path(p).exists()]


class LazyPathDump:
Expand All @@ -142,8 +143,8 @@ def __repr__(self) -> str:
content += " with =>"
for file_name in os.listdir(self.path):
try:
file_path = os.path.join(self.path, file_name)
if os.path.isdir(file_path) or not os.access(file_path, os.X_OK):
file_path = Path(self.path) / file_name
if file_path.is_dir() or not os.access(file_path, os.X_OK):
continue
except OSError:
pass
Expand All @@ -153,13 +154,13 @@ def __repr__(self) -> str:


def check_path(candidate, path):
_, ext = os.path.splitext(candidate)
if sys.platform == "win32" and ext != ".exe":
candidate = candidate + ".exe"
if os.path.isfile(candidate):
candidate_path = Path(candidate)
if sys.platform == "win32" and candidate_path.suffix != ".exe":
candidate_path = candidate_path.with_name(f"{candidate_path.name}.exe")
if candidate_path.is_file():
return candidate
candidate = os.path.join(path, candidate)
if os.path.isfile(candidate):
candidate_path = Path(path) / candidate_path
if candidate_path.exists():
return candidate
return None

Expand Down
4 changes: 2 additions & 2 deletions src/virtualenv/discovery/cached_py_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def _get_via_file_cache(cls, app_data, path, exe, env):
if of_path == path_text and of_st_mtime == path_modified:
py_info = cls._from_dict(of_content.copy())
sys_exe = py_info.system_executable
if sys_exe is not None and not os.path.exists(sys_exe):
if sys_exe is not None and not Path(sys_exe).exists():
py_info_store.remove()
py_info = None
else:
Expand All @@ -92,7 +92,7 @@ def gen_cookie():


def _run_subprocess(cls, exe, app_data, env):
py_info_script = Path(os.path.abspath(__file__)).parent / "py_info.py"
py_info_script = Path(__file__).parent / "py_info.py"
# Cookies allow to split the serialized stdout output generated by the script collecting the info from the output
# generated by something else. The right way to deal with it is to create an anonymous pipe and pass its descriptor
# to the child and output to it. But AFAIK all of them are either not cross-platform or too big to implement and are
Expand Down
40 changes: 21 additions & 19 deletions src/virtualenv/discovery/py_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import sysconfig
import warnings
from collections import OrderedDict, namedtuple
from pathlib import Path
from string import digits

VersionInfo = namedtuple("VersionInfo", ["major", "minor", "micro", "releaselevel", "serial"])
Expand All @@ -33,7 +34,7 @@ class PythonInfo:

def __init__(self) -> None: # noqa: PLR0915
def abs_path(v):
return None if v is None else os.path.abspath(v) # unroll relative elements from path (e.g. ..)
return None if v is None else str(Path(v).absolute()) # unroll relative elements from path (e.g. ..)

# qualifies the python
self.platform = sys.platform
Expand Down Expand Up @@ -136,7 +137,7 @@ def _fast_get_system_executable(self):
base_executable = getattr(sys, "_base_executable", None) # some platforms may set this to help us
if base_executable is not None: # noqa: SIM102 # use the saved system executable if present
if sys.executable != base_executable: # we know we're in a virtual environment, cannot be us
if os.path.exists(base_executable):
if Path(base_executable).exists():
return base_executable
# Python may return "python" because it was invoked from the POSIX virtual environment
# however some installs/distributions do not provide a version-less "python" binary in
Expand All @@ -147,11 +148,11 @@ def _fast_get_system_executable(self):
major, minor = self.version_info.major, self.version_info.minor
if self.os == "posix" and (major, minor) >= (3, 11):
# search relative to the directory of sys._base_executable
base_dir = os.path.dirname(base_executable)
base_dir = Path(base_executable).parent
for base_executable in [
os.path.join(base_dir, exe) for exe in (f"python{major}", f"python{major}.{minor}")
str(base_dir / exe) for exe in (f"python{major}", f"python{major}.{minor}")
]:
if os.path.exists(base_executable):
if Path(base_executable).exists():
return base_executable
return None # in this case we just can't tell easily without poking around FS and calling them, bail
# if we're not in a virtual environment, this is already a system python, so return the original executable
Expand Down Expand Up @@ -239,11 +240,11 @@ def system_include(self):
for k, v in self.sysconfig_vars.items()
},
)
if not os.path.exists(path): # some broken packaging don't respect the sysconfig, fallback to distutils path
if not Path(path).exists(): # some broken packaging don't respect the sysconfig, fallback to distutils path
# the pattern include the distribution name too at the end, remove that via the parent call
fallback = os.path.join(self.prefix, os.path.dirname(self.install_path("headers")))
if os.path.exists(fallback):
path = fallback
fallback = Path(self.prefix) / Path(self.install_path("headers")).parent
if fallback.exists():
path = str(fallback)
return path

@property
Expand Down Expand Up @@ -309,14 +310,15 @@ def clear_cache(cls, app_data):
def satisfies(self, spec, impl_must_match): # noqa: C901
"""Check if a given specification can be satisfied by the this python interpreter instance."""
if spec.path:
if self.executable == os.path.abspath(spec.path):
return True # if the path is a our own executable path we're done
if self.executable == Path(spec.path).resolve():
return True # if the path is our own executable path we're done
if not spec.is_abs:
# if path set, and is not our original executable name, this does not match
basename = os.path.basename(self.original_executable)
basename = Path(self.original_executable).name
spec_path = spec.path
if sys.platform == "win32":
basename, suffix = os.path.splitext(basename)
bn = Path(basename)
basename, suffix = bn.stem, bn.suffix
if spec_path.endswith(suffix):
spec_path = spec_path[: -len(suffix)]
if basename != spec_path:
Expand Down Expand Up @@ -463,10 +465,10 @@ def discover_exe(self, app_data, prefix, exact=True, env=None): # noqa: FBT002
raise RuntimeError(msg)

def _check_exe(self, app_data, folder, name, exact, discovered, env): # noqa: PLR0913
exe_path = os.path.join(folder, name)
if not os.path.exists(exe_path):
exe_path = Path(folder) / name
if not exe_path.exists():
return None
info = self.from_exe(exe_path, app_data, resolve_to_host=False, raise_on_error=False, env=env)
info = self.from_exe(str(exe_path), app_data, resolve_to_host=False, raise_on_error=False, env=env)
if info is None: # ignore if for some reason we can't query
return None
for item in ["implementation", "architecture", "version_info"]:
Expand Down Expand Up @@ -513,15 +515,15 @@ def _find_possible_folders(self, inside_folder):
executables[os.path.realpath(self.original_executable)] = None
executables[self.original_executable] = None
for exe in executables:
base = os.path.dirname(exe)
base = str(Path(exe).parent)
# following path pattern of the current
if base.startswith(self.prefix):
relative = base[len(self.prefix) :]
candidate_folder[f"{inside_folder}{relative}"] = None

# or at root level
candidate_folder[inside_folder] = None
return [i for i in candidate_folder if os.path.exists(i)]
return [i for i in candidate_folder if Path(i).exists()]

def _find_possible_exe_names(self):
name_candidate = OrderedDict()
Expand All @@ -536,7 +538,7 @@ def _find_possible_exe_names(self):

def _possible_base(self):
possible_base = OrderedDict()
basename = os.path.splitext(os.path.basename(self.executable))[0].rstrip(digits)
basename = Path(self.executable).stem.rstrip(digits)
possible_base[basename] = None
possible_base[self.implementation] = None
# python is always the final option as in practice is used by multiple implementation as exe name
Expand Down
Loading

0 comments on commit 914f8bc

Please sign in to comment.