Process_create not inheriting PATH when launched from Mac OS UI?

I am trying to build a rudimentary test that launches Python, which runs some code, which launches “swipl”. swipl is on the path as evidenced by running “swipl” from the bash shell and getting it to run. This is currently on macos catalina, but needs to work cross platform.

The Python code that gets run uses subprocess.Popen(["swipl"]) (i.e. no path provided) to use the user’s path to find swipl.

It works fine if I launch SWI Prolog from the bash command line using ? swipl and then run the test using ?- test_language_server. in Prolog.

It fails when I launch SWI Prolog from the mac os UI by clicking on the icon in the task bar (or whatever that bar on the bottom of the mac is called) and run the test from there. The failure in Python is that Python subprocess.Popen() can’t find swipl on the path.

Any ideas what is going on? It seems like running from the Mac shell UI is not inheriting the user PATH environment variable and thus not sending to process_create. If true, any workaround?

:- use_module(language_server).

test_language_server :-

run_test_script(Script, Status):-
    process_create(path(python3), [Script],
        [stdin(std), stdout(pipe(Out)), stderr(pipe(Out)), process(PID)]),
    read_lines(Out, Lines),
    process_wait(PID, Status).

:- begin_tests(py_language_server, []).

    run_test_script('/Users/ericzinda/Enlistments/swiplserver/swiplserver/', Status),
    assertion(Status == exit(0)).

:- end_tests(py_language_server).

This depends on how you’re setting your PATH variable. Most likely you’re doing so inside one of the profile scripts read by bash (.profile, .bash_profile, .bashrc, etc.). In that case the change will only take effect inside bash and any programs started from inside bash. When you run a macOS application from the GUI, no shell is involved, so PATH customizations in the shell profile won’t affect GUI applications.

Adding PATH entries on macOS so that they are also visible to GUI applications seems to be a bit difficult - see this Server Fault (Stack Exchange) question. You might have luck configuring your system this way, but I wouldn’t depend on it - most other users will also have their PATH customized via the shell and not visible to GUI applications.

Perhaps a better solution would be to manually prepend the directory of the running SWI-Prolog binary to the PATH when calling Python. Something like this would probably work (not tested very much):

path_separator(Sep) :-
    (   current_prolog_flag(windows, true)
    ->  Sep = (;)
    ;   Sep = (:) % Assume Unix-like by default

path_prepend(Path, ToPrepend, Prepended) :-
    atom_concat(ToPrepend, Sep, ToPrependSep),
    atom_concat(ToPrependSep, Path, Prepended).

path_prepend_swipl(Path, Prepended) :-
    current_prolog_flag(executable, SwiplFile),
    file_directory_name(SwiplFile, SwiplDir),
    path_prepend(Path, SwiplDir, Prepended).

run_test_script(Script, Status) :-
    getenv('PATH', Path),
    path_prepend_swipl(Path, PathWithSwipl),
    process_create(path(python3), [Script],
        [stdin(std), stdout(pipe(Out)), stderr(pipe(Out)), environment(['PATH'=PathWithSwipl]), process(PID)]),
    % ...

Or if you want to avoid the complicated PATH manipulation, you can instead pass the executable path to your Python script as an argument, and use that full path in subprocess.Popen instead of just "swipl".

OK, that’s what I was afraid of. Thanks for the sample code! I’ll think about which workaround is best…