Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions relenv/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,72 @@ def relative_interpreter(root_dir, scripts_dir, interpreter):
return relscripts / relinterp


def makepath(*paths):
"""
Make a normalized path name from paths.
"""
dir = os.path.join(*paths)
try:
dir = os.path.abspath(dir)
except OSError:
pass
return dir, os.path.normcase(dir)


def addpackage(sitedir, name):
"""
Add editable package to path.
"""
import io
import stat

fullname = os.path.join(sitedir, name)
paths = []
try:
st = os.lstat(fullname)
except OSError:
return
if (getattr(st, "st_flags", 0) & stat.UF_HIDDEN) or (
getattr(st, "st_file_attributes", 0) & stat.FILE_ATTRIBUTE_HIDDEN
):
# print(f"Skipping hidden .pth file: {fullname!r}")
return
# print(f"Processing .pth file: {fullname!r}")
try:
# locale encoding is not ideal especially on Windows. But we have used
# it for a long time. setuptools uses the locale encoding too.
f = io.TextIOWrapper(io.open_code(fullname), encoding="locale")
except OSError:
return
with f:
for n, line in enumerate(f):
if line.startswith("#"):
continue
if line.strip() == "":
continue
try:
if line.startswith(("import ", "import\t")):
exec(line)
continue
line = line.rstrip()
dir, dircase = makepath(sitedir, line)
if dircase not in paths and os.path.exists(dir):
paths.append(dir)
except Exception:
print(
"Error processing line {:d} of {}:\n".format(n + 1, fullname),
file=sys.stderr,
)
import traceback

for record in traceback.format_exception(*sys.exc_info()):
for line in record.splitlines():
print(" " + line, file=sys.stderr)
print("\nRemainder of file ignored", file=sys.stderr)
break
return paths


def sanitize_sys_path(sys_path_entries):
"""
Sanitize `sys.path` to only include paths relative to the onedir environment.
Expand All @@ -565,4 +631,10 @@ def sanitize_sys_path(sys_path_entries):
if "PYTHONPATH" in os.environ:
for __path in os.environ["PYTHONPATH"].split(os.pathsep):
__sys_path.append(__path)
for known_path in __sys_path[:]:
for _ in pathlib.Path(known_path).glob("__editable__.*.pth"):
paths = addpackage(known_path, _)
for p in paths:
if p not in __sys_path:
__sys_path.append(p)
return __sys_path
76 changes: 76 additions & 0 deletions tests/test_verify_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,28 @@
]


EXTRAS_PY = """
import pathlib
import sys


def setup(pth_file_path):
# Discover the extras-<py-major>.<py-minor> directory
extras_parent_path = pathlib.Path(pth_file_path).resolve().parent.parent
if not sys.platform.startswith("win"):
extras_parent_path = extras_parent_path.parent

extras_path = str(extras_parent_path / "extras-{}.{}".format(*sys.version_info))

if extras_path in sys.path and sys.path[0] != extras_path:
# The extras directory must come first
sys.path.remove(extras_path)

if extras_path not in sys.path:
sys.path.insert(0, extras_path)
"""


@pytest.fixture(scope="module")
def arch():
return build_arch()
Expand Down Expand Up @@ -1454,3 +1476,57 @@ def test_install_pyinotify_w_latest_pip(pipexec, build, minor_version):
)
assert p.returncode == 0, "Failed install pyinotify"
assert (extras / "pyinotify.py").exists()


@pytest.mark.skip_unless_on_linux
def test_install_editable_package(pipexec, pyexec, build, minor_version, tmp_path):
os.chdir(tmp_path)
env = os.environ.copy()
env["RELENV_BUILDENV"] = "yes"
p = subprocess.run(
[
"git",
"clone",
"https://github.com/salt-extensions/saltext-zabbix.git",
"--depth",
"1",
],
env=env,
)
assert p.returncode == 0
p = subprocess.run([str(pipexec), "install", "-e", "saltext-zabbix"], env=env)
assert p.returncode == 0
p = subprocess.run([str(pyexec), "-c", "import saltext.zabbix"], env=env)
assert p.returncode == 0


@pytest.mark.skip_unless_on_linux
def test_install_editable_package_in_extras(
pipexec, pyexec, build, minor_version, tmp_path
):
sitepkgs = pathlib.Path(build) / "lib" / f"python{minor_version}" / "site-packages"

(sitepkgs / "_extras.pth").write_text("import _extras; _extras.setup(__file__)")
(sitepkgs / "_extras.py").write_text(EXTRAS_PY)
extras = pathlib.Path(build) / f"extras-{minor_version}"
extras.mkdir()
os.chdir(tmp_path)
env = os.environ.copy()
env["RELENV_BUILDENV"] = "yes"
p = subprocess.run(
[
"git",
"clone",
"https://github.com/salt-extensions/saltext-zabbix.git",
"--depth",
"1",
],
env=env,
)
assert p.returncode == 0
p = subprocess.run(
[str(pipexec), "install", f"--target={extras}", "-e", "saltext-zabbix"], env=env
)
assert p.returncode == 0
p = subprocess.run([str(pyexec), "-c", "import saltext.zabbix"], env=env)
assert p.returncode == 0
Loading