From a5c7a885804203ca560c8bdd19eb4a5beee2152a Mon Sep 17 00:00:00 2001 From: RyanPCo Date: Sat, 12 Sep 2026 11:57:32 -0400 Subject: [PATCH] build: pyproject + uv.lock as the dependency spec; fix the wheel - Delete setup.py (superseded by [project]) and environment.yaml (a wrapper around requirements.txt). requirements.txt stays untouched: the Eva robot Dockerfile installs it (it has drifted from the lock: torch 2.6.0 / transformers 4.57.3 vs 2.7.1 / 4.53.2, but that image is out of scope). requirements-ray.txt stays: the Ray cluster yamls install it. - packages.find: include = ["egomimic*"]. The wheel had shipped external/ (lerobot, rpl_vision_utils, scale) and tests/ as top-level packages. - package-data: hydra_configs/**/*.yaml and resources/* (0 of 76 yamls and no URDF/XML were in the wheel, so only `pip install -e .` ever worked). - Declare scipy (imported by 7 modules, arrived transitively); uv.lock updated. - openpi is documented, not declared: the fork's lerobot pin needs av>=14.2, which conflicts with av==12.0.0, so an extra cannot resolve. README now gives the no-deps install + transformers_replace patch the lab venv actually uses. - README install section points at pyproject; .gitignore build/. - tests/unit/test_packaging.py builds the wheel via the build backend (from a clean build/, removed again afterwards) and checks it ships every hydra yaml and the resources, and nothing outside egomimic/. Also verified by installing the wheel into a fresh venv. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01UHDiN8yfERcsiFdz4mXuZi --- .gitignore | 4 ++ README.md | 20 ++++++++- environment.yaml | 9 ---- pyproject.toml | 17 ++++++++ setup.py | 13 ------ tests/unit/test_packaging.py | 79 ++++++++++++++++++++++++++++++++++++ uv.lock | 2 + 7 files changed, 120 insertions(+), 24 deletions(-) delete mode 100644 environment.yaml delete mode 100644 setup.py create mode 100644 tests/unit/test_packaging.py diff --git a/.gitignore b/.gitignore index 601d99dbc..8f3d36184 100644 --- a/.gitignore +++ b/.gitignore @@ -49,4 +49,8 @@ scratch/ sample_data/ external_ckpts/ external/MANO/ + +# setuptools build output +build/ +dist/ docs/ diff --git a/README.md b/README.md index a27d1698a..e35f999f1 100644 --- a/README.md +++ b/README.md @@ -40,16 +40,32 @@ git clone git@github.com:GaTech-RL2/EgoVerse.git cd EgoVerse uv venv emimic --python 3.11 source emimic/bin/activate -uv pip install -r requirements.txt uv pip install -e . uv run pre-commit install ``` +`pyproject.toml` (locked in `uv.lock`) is the dependency spec for the +package. Three things live outside it: `requirements.txt` (used only by the +Eva robot `Dockerfile`), `requirements-ray.txt` (the Ray data-processing +image) and `scale_sensor_fusion_io`, needed only by the Scale converter under +`external/scale/scripts` (`uv pip install scale-sensor-fusion-io`). + +For the Pi0.5 policy also install the `openpi` fork from the submodule. It is +installed without its own dependencies (its `lerobot` pin needs `av>=14.2`, +which conflicts with our `av==12.0.0`), then its `transformers` patch is copied in: +``` +git submodule update --init external/openpi +uv pip install --no-deps -e external/openpi +cp -r external/openpi/src/openpi/models_pytorch/transformers_replace/* emimic/lib/python3.11/site-packages/transformers/ +``` +Re-run the `cp` after any `uv sync` or `transformers` reinstall, which silently +undoes the patch. See `pi05.md` for details. + ### Conda ``` git clone --recursive git@github.com:GaTech-RL2/EgoVerse.git cd EgoVerse -conda env create -f environment.yaml +conda create -n emimic python=3.11 conda activate emimic pip install -e . pre-commit install diff --git a/environment.yaml b/environment.yaml deleted file mode 100644 index 1e59ef14b..000000000 --- a/environment.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: emimic -channels: - - conda-forge - - defaults -dependencies: - - python=3.11 - - pip - - pip: - - -r requirements.txt diff --git a/pyproject.toml b/pyproject.toml index e4a75e479..dfc1fc4e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,10 +64,27 @@ dependencies = [ "scaleapi", "openai", "scikit-learn>=1.8.0", + "scipy", ] +# Not declared: `openpi` (egomimic/algo/pi.py). The fork at external/openpi pins +# lerobot -> av>=14.2, which conflicts with av==12.0.0 above, so it cannot be a +# resolvable extra. Install it without its dependencies, as the lab venv does: +# uv pip install --no-deps -e external/openpi +# then apply openpi's `transformers_replace` patch (README, "Installation"). +# `uv sync` reinstalls transformers and silently undoes that patch. +# +# Also not declared: `scale_sensor_fusion_io` (PyPI: scale-sensor-fusion-io), +# imported only by the Scale converter under external/scale/scripts, which is +# not part of this package; install it by hand when running that converter. +# The Ray data-processing image keeps its own requirements-ray.txt. + [tool.setuptools.packages.find] where = ["."] +include = ["egomimic*"] + +[tool.setuptools.package-data] +egomimic = ["hydra_configs/**/*.yaml", "resources/*"] [tool.pytest.ini_options] testpaths = ["tests"] diff --git a/setup.py b/setup.py deleted file mode 100644 index c0f9411e6..000000000 --- a/setup.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python - -from setuptools import find_packages, setup - -setup( - name="egomimic", - version="1.0", - description="Egomimic Scripts Package", - author="EgoMimic-Team", - author_email="", - url="https://egomimic.github.io", - packages=find_packages(), # Automatically discovers all packages -) diff --git a/tests/unit/test_packaging.py b/tests/unit/test_packaging.py new file mode 100644 index 000000000..dd035fc21 --- /dev/null +++ b/tests/unit/test_packaging.py @@ -0,0 +1,79 @@ +"""A non-editable install must ship the training stack and nothing else. + +Builds the wheel with the configured build backend (no pip / uv needed) and +inspects its contents: Hydra configs and robot resources must be inside, +``external/`` and ``tests/`` must not be. +""" + +import shutil +import subprocess +import sys +import zipfile +from pathlib import Path + +import pytest + +REPO = Path(__file__).resolve().parents[2] + + +@pytest.fixture(scope="module") +def wheel_files(tmp_path_factory) -> list[str]: + out = tmp_path_factory.mktemp("wheel") + # setuptools reuses a stale build/lib (deleted modules would linger in the + # wheel); build from a clean slate and leave the checkout as we found it. + shutil.rmtree(REPO / "build", ignore_errors=True) + code = ( + "import sys; from setuptools import build_meta; " + f"print(build_meta.build_wheel({str(out)!r}))" + ) + proc = subprocess.run( + [sys.executable, "-c", code], + cwd=REPO, + capture_output=True, + text=True, + timeout=600, + ) + shutil.rmtree(REPO / "build", ignore_errors=True) + assert proc.returncode == 0, proc.stderr[-3000:] + wheel = next(out.glob("*.whl")) + with zipfile.ZipFile(wheel) as zf: + return zf.namelist() + + +def test_wheel_does_not_ship_external_or_tests(wheel_files): + stray = sorted( + {n.split("/")[0] for n in wheel_files} + - {"egomimic"} + - { + n.split("/")[0] + for n in wheel_files + if n.split("/")[0].endswith(".dist-info") + } + ) + assert stray == [], f"top-level entries other than egomimic in the wheel: {stray}" + + +def test_wheel_ships_hydra_configs(wheel_files): + packaged = { + n[len("egomimic/hydra_configs/") :] + for n in wheel_files + if n.startswith("egomimic/hydra_configs/") and n.endswith(".yaml") + } + on_disk = { + str(p.relative_to(REPO / "egomimic/hydra_configs")) + for p in (REPO / "egomimic/hydra_configs").rglob("*.yaml") + } + assert "train_zarr_cartesian.yaml" in packaged + assert "paths/default.yaml" in packaged + assert ( + on_disk - packaged == set() + ), f"hydra yamls missing from wheel: {sorted(on_disk - packaged)}" + + +def test_wheel_ships_robot_resources(wheel_files): + # model_x5.xml's meshdir points at egomimic/robot/eva/stanford_repo, which + # is not packaged, so the MJCF only loads from a checkout. Shipped anyway: + # the URDFs beside it are self-contained and the robot code resolves the + # XML relative to egomimic.__file__. + assert "egomimic/resources/model_x5.xml" in wheel_files + assert "egomimic/resources/model_arx.urdf" in wheel_files diff --git a/uv.lock b/uv.lock index 6ead625d1..d5fbbc34b 100644 --- a/uv.lock +++ b/uv.lock @@ -1065,6 +1065,7 @@ dependencies = [ { name = "s5cmd" }, { name = "scaleapi" }, { name = "scikit-learn" }, + { name = "scipy" }, { name = "simplejpeg" }, { name = "sqlalchemy" }, { name = "submitit" }, @@ -1125,6 +1126,7 @@ requires-dist = [ { name = "s5cmd" }, { name = "scaleapi" }, { name = "scikit-learn", specifier = ">=1.8.0" }, + { name = "scipy" }, { name = "simplejpeg" }, { name = "sqlalchemy" }, { name = "submitit" },