I mean, we would be running eval $(opam env)
either way, I’m not wondering about that part. Just about the way it’s coded.
A process can’t change the environment of an other process. Then opam
can’t change the parent process environment (bash or Python).
However, the environment is inherited when a process create a new one. If your script changes its environment (with os.environ
) what he call get this environment.
Note also that eval
is an internal bash command which doesn’t create a subprocess, then can change the bash environment.
What you would like is a ocaml
proxy command which ask opam
which file to execute and set its environment. You have it with opam exec --switch my_switch command
. But just setting the environment takes 2s.
Got it. If this is true and I want build another ocaml project/pkg from within the main python process (regardless of how I build it, with opam install, opam pin, make, make -C etc even if I dispatch those from python) so that any of those build inherit the right opam switch env being set they we always need to update os.environ
.
Makes sense, thanks!
curious, can you explain what this does? How does it know what the process of the main python process is and change it’s env properly?
I’ve confirmed this. What I do is run opam switch set coq-8.10
from a python subprocess:
# opam_set_switch_via_opam_switch('coq-8.10')
result = subprocess.run(f'opam switch set {switch}'.split(), check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
which the docs says returns a completed process.
Then I compare the contents of the env variables of the subprocess by calling via another subprocess the cmd opam env
and compare it with the main python process by comparing it with os.environ
. I get that the two indeed don’t match:
# opam_env_dict: dict = get_variables_from_opam_env_output(py_prints_on=py_prints_on)
result = subprocess.run('opam env'.split(), capture_output=True, text=True)
# ... compare with os.environ
assert uutils.check_dict1_is_in_dict2(opam_env_dict, os.environ, verbose=True)
assert fails
--> k='OPAM_SWITCH_PREFIX' is in dict2 but with different value
dict1[k]='/Users/brandomiranda/.opam/coq-8.10'
dict2[k]='/Users/brandomiranda/.opam/test'
The only thing that confuses me is that it seems that subprocess has it’s own process that does remember things. I say this because I would have expected the new subprocess that calls opam env
to not be affected by the first opam switch set coq-8.10
but it seems it was affected. I expected the 2nd subprocess to spawned from the main python and be independent form the process that called opam switch set coq-8.10
.
code:
def check_os_env_has_the_vars_set_by_opam_env_test_():
py_prints_on: bool = False
import uutils
# -- opam switch set for subprocess & get output of opam env
opam_set_switch_via_opam_switch('test')
activate_switch: str = get_active_opam_switch_according_to_bash_opam_switch_cmd(py_prints_on=py_prints_on)
print(f'{activate_switch=}')
opam_set_switch_via_opam_switch('coq-8.10')
activate_switch: str = get_active_opam_switch_according_to_bash_opam_switch_cmd(py_prints_on=py_prints_on)
print(f'{activate_switch=}')
opam_env_dict: dict = get_variables_from_opam_env_output(py_prints_on=py_prints_on)
print(f'opam_env_dict=')
uutils.pprint_dict(opam_env_dict)
# -- get output of os.env
# - verify that subprocess indeed calls it's own process indepdent of python main. The fact they don't match means that the subprocess is calling it's own opam env. Mystery is why that subprocess is persistent despite docs saying it returns a completed process: https://stackoverflow.com/questions/74803306/what-is-the-difference-between-eval-opam-env-switch-switch-set-switch-a, https://discuss.ocaml.org/t/is-eval-opam-env-switch-switch-set-switch-equivalent-to-opam-switch-set-switch/10957/25
assert not uutils.check_dict1_is_in_dict2(opam_env_dict, os.environ,
verbose=True), f'Err: dict1 is opam env of subprocess so it should point to .opam/coq-8.10 while main python should point to .opam/test (assuming your terminal is indeeed set to opam switch called test)'
# seems that subprocess persists and still outputs .opam/coq-8.10
activate_switch: str = get_active_opam_switch_according_to_bash_opam_switch_cmd(py_prints_on=py_prints_on)
print(f'{activate_switch=}')
If you launch opam switch set {switch}
, you tell opam
to store in the .opam
directory the info about your desire to change the switch. Then this information is virtually shared among all your processes. Afterward, opam env
read this information and print the environment change needed by this switch.
The idea is you just type opam switch set {switch}
and let the shell execute automatically the eval
command according to the PROMPT_COMMAND value. Python has no such an automatic behaviour.
opam exec --switch my_switch command
won’t change the environment of your Python, but will execute the given command with a modified environment.
If your Python program only execute one command (make
), it can be handy. If multiple commands are executed, getting the environment each time will take some time.
Also beware, if your package executes ocaml
command through opam exec
, your environment will be replaced by the current switch selected by opam switch set
.
A complement. You have to way to change the switch:
eval $(opam env --switch my_switch --set-switch)
-
opam switch set my_switch
and afterwardeval $(opam env)
.
The two options are not strictly equivalent.
The first one change the switch through the environment (OPAMSWITCH variable), then its scope is limited by the current process and its descendence.
The second option change a file in your .opam
directory, then you can have a scope in all processes which just evalute opam env
. It is handy when the shell has a PROMPT_COMMAND option which evalute opam env
after each command. In this case, just an opam switch set my_switch
is enough which is handy.
When OPAMSWITCH differ from the (user) global switch, opam list
use the OPAMSWITCH and print a warning:
[NOTE] Current switch is set locally through the OPAMSWITCH variable.
The current global system switch is 4.11.0+flambda.
The right way depends if you want a transcient change or a global and remanent change.
The 2nd option opam switch set my_switch and afterward eval $(opam env)
I’ve tried running in python and it seems the main python process doesn’t change correctly (by observing the os.environ
). If I remember correctly neither do new process created from it using subprocess. I need to build multiple ocaml projects from within python so I assume I need to make sure those subprocess have the right ocaml switch. I was going to do either of these:
- I was going to write a big command as a string that made sure the new subprocess had the right switch. Or executed a main.sh file that did that already and then ran that file.
- Assuming your
opam switch set my_switch
does change some global thing (that doesn’t affect my main python process but it does affect the new subprocess I run as far as I can see if I remember correctly) – then I can just runopam switch set my_switch
in python. The only issue is I can’t runeval $(opam env)
in the new subprocess or Idk how to run evals in a new subprocess. Perhaps this step won’t work…
This is rather confusing. Why does opam switch set my_switch
change the switch already. Why do we need to run eval $(opam env)
too? Seems really confusing to me.
opam switch set
will only register a switch by modifying an opam file. Then opam
commands will be informed of a change. However, OCaml programs (ocaml, ocamlc, ocamlopt, …) and your shell (or programs like make
) aren’t designed to deal with this file, but use the environment (typically PATH to launch the right program, CAML_LD_LIBRARY_PATH to find dynamically some libraries). You also have MANPATH to have man
use the right documentation.
Then you need to “convert” the switch choice to a set of environment variables to have a consistent setup.
With some hooks, the eval $(opam env)
is implicit in an interactive shell.