saltstack / relenv

Re-producible and Re-relocatable Python Environments
Apache License 2.0
25 stars 16 forks source link

sys.path Sanitizing Breaks When Relenv is Within a Symlinked Directory #167

Closed MrFlynn closed 8 months ago

MrFlynn commented 11 months ago

When attempting to install salt using the onedir method I encountered a failure where some imports in the relenv Python environment could not be found (ex. runpy). After doing some digging, I was able to identify that the mechanism relenv uses to sanitize sys.path will remove valid entries if the Python interpreter is called from a path that contains a symlink anywhere along the path.

Steps to Reproduce

  1. Create a target directory and link it to a directory called parent: mkdir -p target/ && ln -sf target/ parent/.
  2. Create a new relenv inside the parent directory: cd parent/ && relenv create testenv.
  3. Go up one directory and launch the python interpreter inside the relenv with debugging enabled and observe that sys.path has been cleared.
    
    $ RELENV_DEBUG=true parent/testenv/bin/python3
    Unable to get the modules directory from openssl: version: Option unknown option -m
    version: Use -help for summary.

original sys.path was ['/root/parent/testenv/lib/python310.zip', '/root/parent/testenv/lib/python3.10', '/root/parent/testenv/lib/python3.10/lib-dynload', '/root/parent/testenv/lib/python3.10/site-packages'] new sys.path is [] After site customize wrapper Python 3.10.13 (main, Nov 8 2023, 12:14:32) [GCC 11.2.0] on linux Type "help", "copyright", "credits" or "license" for more information.

Root Cause

After digging into the code, I identified that the sanitize_sys_path function incorrectly removes entries in sys.path because it doesn't resolve symlinks properly. Specifically this code:

https://github.com/saltstack/relative-environment-for-python/blob/78b8dbf8d5d76612bc79e8ab8760ffc82223e072/relenv/common.py#L471-L472

A call to resolve() needs to happen before checking if the sys.path entry is relative to the valid prefix. Something like the following would resolve this issue.

__resolved_path = pathlib.Path(__path).resolve()
__resolved_path.relative_to(__valid_path_prefix)
__sys_path.append(str(__resolved_path))

Currently the lack of proper path resolving breaks installing salt in onedir mode if /opt is symlinked to another directory.

dwoz commented 11 months ago

@MrFlynn would you be willing to submit a PR for this?

MrFlynn commented 11 months ago

Sure, let me work something up and I'll link it here when I'm finished.

MrFlynn commented 11 months ago

@dwoz Created #168.

dwoz commented 8 months ago

Fixed in 0.15.0