Skip to content

python: load_library() mutates os.environ['DYLD_LIBRARY_PATH'] on macOS (leaks to subprocesses, fragile guard) #3077

Description

@dgarnier

Where

python/MDSplus/version.py, load_library():

if isdarwin and not os.getenv('DYLD_LIBRARY_PATH'):
    if os.getenv('MDSPLUS_LIBRARY_PATH'):
        os.environ['DYLD_LIBRARY_PATH'] = os.getenv('MDSPLUS_LIBRARY_PATH')
    elif os.getenv('MDSPLUS_DIR'):
        os.environ['DYLD_LIBRARY_PATH'] = os.path.join(os.getenv('MDSPLUS_DIR'), 'lib')
    else:
        os.environ['DYLD_LIBRARY_PATH'] = '/usr/local/mdsplus/lib'
...
if isdarwin:
    for path in os.environ.get('DYLD_LIBRARY_PATH', '').split(':'):
        try:
            return C.CDLL('%s/lib%s.dylib' % (path, name))
        except:
            continue
    return C.CDLL('lib%s.dylib' % name)

The problem

On macOS the code sets os.environ['DYLD_LIBRARY_PATH'] after the process has started. macOS does not honor a late-set DYLD_LIBRARY_PATH for dlopen (SIP strips DYLD_* from protected interpreters; the dynamic loader reads it at launch). So the assignment looks like it is steering the loader but cannot.

Why it isn't visibly breaking today

The darwin branch does not rely on the loader honoring the variable — it reads the string back and builds the absolute "<path>/lib<name>.dylib" itself, then CDLLs the full path. The env var is effectively just a scratch variable carrying the search directory a few lines down. And the MDSplus C libraries locate their own dependencies via MDSPLUS_LIBRARY_PATH (explicit lookup in the C code), so nothing downstream needs DYLD_LIBRARY_PATH either.

Why it should still be fixed

  • Environment mutation leaks. Setting os.environ['DYLD_LIBRARY_PATH'] persists for the whole process and is inherited by any subprocess spawned afterwards, perturbing unrelated tools.
  • The not os.getenv('DYLD_LIBRARY_PATH') guard is fragile. If DYLD_LIBRARY_PATH is already set (to anything that does not contain the MDSplus lib dir), the setup block is skipped entirely: the loop iterates the pre-existing dirs, the explicit-path CDLLs fail, and it falls back to a bare lib<name>.dylib, which may not find the libraries. In that case MDSPLUS_LIBRARY_PATH is also ignored.

Suggested direction

Do not touch os.environ at all. Compute a local search-path list (MDSPLUS_LIBRARY_PATH -> $MDSPLUS_DIR/lib -> /usr/local/mdsplus/lib), unconditionally (not gated on DYLD_LIBRARY_PATH being unset), and build the explicit dylib paths from that local list. This removes the subprocess leak and the fragile guard while keeping the current explicit-absolute-path loading behavior.

Filed as a follow-up spun off from the python "local wheel" work; not a regression.

Metadata

Metadata

Assignees

No one assigned

    Labels

    api/pythonRelates to the Python APIos/macThis is present on or relates to Mac OSXrefactorRelates to refactoring or code quality improvements

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions