mujoco icon indicating copy to clipboard operation
mujoco copied to clipboard

`mjpython` bug when installed with `uv` package manager

Open jonzamora opened this issue 1 year ago • 2 comments

Hi,

When i try to run a MuJoCo script with mjpython on my Macbook after installing mujoco with the uv package manager, I get an error message. To reproduce the error, I ran the following commands:

# Install `uv` on macOS and Linux.
$ curl -LsSf https://astral.sh/uv/install.sh | sh

# Create project and run mjpython
uv init mujoco-example
cd mujoco-example
uv add mujoco
source .venv/bin/activate
mjpython

The error says the following:

(.venv) jonzamora@mbp mujoco-example % mjpython
failed to dlopen path '/Users/jonzamora/Research/mujoco-example/.venv/bin/python': dlopen(/Users/jonzamora/Research/mujoco-example/.venv/bin/python, 0x000A): Library not loaded: @executable_path/../lib/libpython3.10.dylib
  Referenced from: <4C4C44EE-5555-3144-A1A0-28739E256A39> /Users/jonzamora/.local/share/uv/python/cpython-3.10.14-macos-aarch64-none/bin/python3.10
  Reason: tried: '/Users/jonzamora/Research/mujoco-example/.venv/lib/python3.10/site-packages/mujoco/MuJoCo (mjpython).app/Contents/lib/libpython3.10.dylib' (no such file), '/Users/jonzamora/Research/mujoco-example/.venv/bin/../lib/libpython3.10.dylib' (no such file), '/libpython3.10.dylib' (no such file)

Of course, you could run mjpython main.py, where main.py is a passive viewer rendering script like this:

import time

import mujoco
import mujoco.viewer

m = mujoco.MjModel.from_xml_path('/path/to/mjcf.xml')
d = mujoco.MjData(m)

with mujoco.viewer.launch_passive(m, d) as viewer:
  # Close the viewer automatically after 30 wall-seconds.
  start = time.time()
  while viewer.is_running() and time.time() - start < 30:
    step_start = time.time()

    # mj_step can be replaced with code that also evaluates
    # a policy and applies a control signal before stepping the physics.
    mujoco.mj_step(m, d)

    # Example modification of a viewer option: toggle contact points every two seconds.
    with viewer.lock():
      viewer.opt.flags[mujoco.mjtVisFlag.mjVIS_CONTACTPOINT] = int(d.time % 2)

    # Pick up changes to the physics state, apply perturbations, update options from GUI.
    viewer.sync()

    # Rudimentary time keeping, will drift relative to wall clock.
    time_until_next_step = m.opt.timestep - (time.time() - step_start)
    if time_until_next_step > 0:
      time.sleep(time_until_next_step)

Any help will be greatly appreciated, i'm sure others are also hoping to use a combination of mujoco and uv on their Macbook machines. Thanks!

My Setup

  • Operating system + Machine
    • macOS Sonoma, M3 Max Macbook Pro
  • MuJoCo version (and if the bug is new, the version where it used to work)
    • MuJoCo 3.2.2
  • For Python issues, what bindings are you using (i.e mujoco, dm_control, mujoco-py)?
    • mujoco

jonzamora avatar Aug 25 '24 09:08 jonzamora

For comparison, if i create a mamba / conda environment and pip install mujoco, then run mjpython, the expected behavior occurs:

(mujoco) jonzamora@mbp mujoco-example % mjpython
Python 3.10.14 | packaged by conda-forge | (main, Mar 20 2024, 12:51:49) [Clang 16.0.6 ] on darwin
Type "help", "copyright", "credits" or "license" for more information.

jonzamora avatar Aug 25 '24 09:08 jonzamora

I'm not familiar with uv but it seems that for whatever reason this section of code isn't working properly.

Would you be able to run get_executable_path() from mjpython.py from your non-working venv and tell me what you get?

saran-t avatar Oct 18 '24 10:10 saran-t

I'm facing similar issue when creating venv via Bazel and rules_uv.

ipdb> get_executable_path()
/Users/user/.venv/bin/python3
$ mjpython
failed to dlopen path '/Users/user/.venv/bin/python3': dlopen(/Users/user/.venv/bin/python3, 0x000A): Library not loaded: @executable_path/../lib/libpython3.12.dylib
  Referenced from: <4C4C44EA-5555-3144-A121-B75170B036A4> /private/var/tmp/_bazel_user/4e70935c28346adf27d1290baa381158/external/rules_python~~python~python_3_12_aarch64-apple-darwin/bin/python3.12
  Reason: tried: '/Users/user/.venv/lib/python3.12/site-packages/mujoco/MuJoCo_(mjpython).app/Contents/lib/libpython3.12.dylib' (no such file), '/Users/user/.venv/bin/../lib/libpython3.12.dylib' (no such file), '/libpython3.12.dylib' (no such file)

libpython3.12.dylib does indeed exist relative to the bazel python (in /private/var/tmp/_bazel_user/4e70935c28346adf27d1290baa381158/external/rules_python~~python~python_3_12_aarch64-apple-darwin/bin/../lib/libpython3.12.dylib) but not relative to .venv python (/Users/user/.venv/bin/../lib/libpython3.12.dylib)

$  ls "/private/var/tmp/_bazel_user/4e70935c28346adf27d1290baa381158/external/rules_python~~python~python_3_12_aarch64-apple-darwin/bin/../lib/libpython3.12.dylib"
/private/var/tmp/_bazel_user/4e70935c28346adf27d1290baa381158/external/rules_python~~python~python_3_12_aarch64-apple-darwin/bin/../lib/libpython3.12.dylib

I'm not sure what the right solution here would be. I tried poking around get_executable_path and here are a couple of ways I found to reference the Bazel python location:

$ diff -ruN .venv/bin/mjpython-old .venv/bin/mjpython
--- .venv/bin/mjpython-old
+++ .venv/bin/mjpython
@@ -28,6 +28,7 @@
 import re
 import subprocess
 import sys
+from sysconfig import get_config_var

 if platform.system() != 'Darwin':
   raise RuntimeError('This script only works on macOS')
@@ -68,6 +69,9 @@
       capture_output=True,
       check=True,
   ).stdout.decode()
+
+  project_base = get_config_var("projectbase")
+
   for line in otool_out.split('\n'):
     m = pattern.search(line)
     if m is not None:
@@ -75,6 +79,11 @@
       if new_path not in dyld_fallback_paths:
         dyld_fallback_paths.insert(0, new_path)

+      if project_base is not None:
+        new_path = os.path.dirname(os.path.join(project_base, m.group(1)))
+        if new_path not in dyld_fallback_paths:
+          dyld_fallback_paths.insert(0, new_path)
+
   os.environ['DYLD_FALLBACK_LIBRARY_PATH'] = ':'.join(dyld_fallback_paths)

   # argv[0] is currently the path to this script.

Alternatively one could use os.path.dirname(sysconfig.get_path('stdlib')), which, at least in my case points to the right place.

There are four values in sysconfig that reference the Bazel path:

ipdb> get_config_var("installed_base")
'/private/var/tmp/_bazel_user/4e70935c28346adf27d1290baa381158/external/rules_python~~python~python_3_12_aarch64-apple-darwin'
ipdb> get_config_var("installed_platbase")
'/private/var/tmp/_bazel_user/4e70935c28346adf27d1290baa381158/external/rules_python~~python~python_3_12_aarch64-apple-darwin'
ipdb> get_config_var("projectbase")
'/private/var/tmp/_bazel_user/4e70935c28346adf27d1290baa381158/external/rules_python~~python~python_3_12_aarch64-apple-darwin/bin'
ipdb> get_config_var("srcdir")
'/private/var/tmp/_bazel_user/4e70935c28346adf27d1290baa381158/external/rules_python~~python~python_3_12_aarch64-apple-darwin/lib/python3.12/config-3.12-darwin'

I'm not sure if any of these are good to rely on.

In case someone else faces this, here's how I've managed to work around this:

$ DYLD_FALLBACK_LIBRARY_PATH=./bazel-bin/create_venv.runfiles/rules_python\~\~python\~python_3_12_aarch64-apple-darwin/lib \
mjpython

Where create_venv.runfiles should be replaced with the venv creation target name (e.g. [your-target].runfiles).

hartikainen avatar Jan 02 '25 14:01 hartikainen

Here is a quick hack for this problem:

  # Find the path to LIBDIR and LDLIBRARY (dylib file) on your system:
  PYTHON_LIB_DIR=$(python3 -c 'from distutils.sysconfig import get_config_var; print(get_config_var("LIBDIR"))')
  PYTHON_LIB_NAME=$(python3 -c 'from distutils.sysconfig import get_config_var; print(get_config_var("LDLIBRARY"))')
  # Soft link it inside your virtual environment:
  ln -s "$PYTHON_LIB_DIR/$PYTHON_LIB_NAME" ./.venv/lib/$PYTHON_LIB_NAME

hello-robot-shehab avatar Mar 25 '25 23:03 hello-robot-shehab

Can confirm the issue persists (uv==0.8.4, python==3.12.10, mujoco=3.3.4).

Here is the same command from shehab modified for fish shell:

# Find the path to LIBDIR and LDLIBRARY (dylib file) on your system:
set PYTHON_LIB_DIR (python3 -c 'from distutils.sysconfig import get_config_var; print(get_config_var("LIBDIR"))')
set PYTHON_LIB_NAME (python3 -c 'from distutils.sysconfig import get_config_var; print(get_config_var("LDLIBRARY"))')

# Soft link it inside your virtual environment:
ln -s "$PYTHON_LIB_DIR/$PYTHON_LIB_NAME" ./.venv/lib/$PYTHON_LIB_NAME

yunho-c avatar Aug 02 '25 16:08 yunho-c

For anybody meeting this issue, I was able to get around it on Mac OS by ensure my uv venv python version matches the system version (as verified by python --version), for example uv venv --python 3.13.

ETeissonniere avatar Aug 31 '25 01:08 ETeissonniere