diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a06ff2ec..8641f9ef 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -62,30 +62,16 @@ jobs: echo "MODEL_DIR=$MODEL_DIR" >> "$GITHUB_ENV" echo "model_dir=$MODEL_DIR" >> "$GITHUB_OUTPUT" - - name: Restore mesh cache - id: meshcache - uses: actions/cache@v4 - with: - path: ${{ steps.model.outputs.model_dir }} - key: ${{ runner.os }}-${{ matrix.os }}-${{ matrix.python-version }}-judo_meshes - - - name: Download meshes (cache miss) - if: steps.meshcache.outputs.cache-hit != 'true' + - name: Download meshes env: MODEL_DIR: ${{ steps.model.outputs.model_dir }} GITHUB_TOKEN: ${{ github.token }} run: | python - <<'PY' import os - from judo.utils.assets import download_and_extract_meshes - download_and_extract_meshes(extract_root=os.environ["MODEL_DIR"]) + from judo.utils.assets import retrieve_model_from_remote + retrieve_model_from_remote("cylinder_push", force=True) PY - - name: Show cache outcome - run: | - echo "::notice::mesh cache hit = ${{ steps.meshcache.outputs.cache-hit }}" - echo "::notice::MODEL_DIR = ${{ steps.model.outputs.model_dir }}" - ls -R "${{ steps.model.outputs.model_dir }}" | head - - name: Test with pytest run: pytest -rsx diff --git a/judo/__init__.py b/judo/__init__.py index 66b0ff63..aed54c88 100644 --- a/judo/__init__.py +++ b/judo/__init__.py @@ -3,4 +3,4 @@ from pathlib import Path PACKAGE_ROOT = Path(__file__).resolve().parent -MODEL_PATH = PACKAGE_ROOT / "models" +DESCRIPTION_CACHE_DIR = "~/.cache/judo_descriptions_cache" diff --git a/judo/cli.py b/judo/cli.py index 361c660d..ad47b85e 100644 --- a/judo/cli.py +++ b/judo/cli.py @@ -9,6 +9,8 @@ from hydra.core.config_store import ConfigStore from omegaconf import DictConfig +from judo.utils.assets import retrieve_description_path_from_remote + # suppress annoying warning from hydra warnings.filterwarnings( "ignore", @@ -29,6 +31,8 @@ @hydra.main(config_path=str(CONFIG_PATH), config_name="judo_dora_default", version_base="1.3") def main_app(cfg: DictConfig) -> None: """Main function to run judo via a hydra configuration yaml file.""" + # Include a download for the default asset to cache the description repository + retrieve_description_path_from_remote(cfg["task"], force=False) run(cfg) diff --git a/judo/controller/__init__.py b/judo/controller/__init__.py index f33be88b..ff96ce25 100644 --- a/judo/controller/__init__.py +++ b/judo/controller/__init__.py @@ -1,6 +1,5 @@ # Copyright (c) 2025 Robotics and AI Institute LLC. All rights reserved. -from judo import MODEL_PATH from judo.controller.controller import Controller, ControllerConfig, make_controller from judo.controller.overrides import ( set_default_caltech_leap_cube_overrides, @@ -10,13 +9,6 @@ set_default_leap_cube_down_overrides, set_default_leap_cube_overrides, ) -from judo.utils.assets import download_and_extract_meshes - -download_and_extract_meshes( - extract_root=str(MODEL_PATH), - repo="bdaiinstitute/judo", - asset_name="meshes.zip", -) set_default_caltech_leap_cube_overrides() set_default_cartpole_overrides() diff --git a/judo/models/xml/README.md b/judo/models/xml/README.md deleted file mode 100644 index dc4bbbbf..00000000 --- a/judo/models/xml/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Model Description Sources - -All model descriptions in this directory were either created at RAI or taken and modified from an external source. The table below documents the origin and license of each model description. - -| Model Name | Source | License | Notes | -|---------------|---------------------------------------------------------------------------------------------------------------------------------------------|-------------|-----------------------------------------------------------------| -| cylinder_push | Created at RAI | MIT | N/A | -| cartpole | Created at RAI | MIT | N/A | -| fr3_pick | [mujoco_menagerie – franka_fr3](https://github.com/google-deepmind/mujoco_menagerie/tree/main/franka_fr3) | Apache-2.0 | Modified | -| leap_cube | [mujoco_menagerie – leap_hand](https://github.com/google-deepmind/mujoco_menagerie/tree/main/leap_hand) | MIT | Modified. Includes all derivative `leap_cube` XMLs in this repo | diff --git a/judo/models/xml/caltech_leap_components/assets.xml b/judo/models/xml/caltech_leap_components/assets.xml deleted file mode 100644 index 46604981..00000000 --- a/judo/models/xml/caltech_leap_components/assets.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/judo/models/xml/caltech_leap_components/leap_rh.xml b/judo/models/xml/caltech_leap_components/leap_rh.xml deleted file mode 100644 index 6c455f9e..00000000 --- a/judo/models/xml/caltech_leap_components/leap_rh.xml +++ /dev/null @@ -1,269 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/judo/models/xml/caltech_leap_components/params_and_default.xml b/judo/models/xml/caltech_leap_components/params_and_default.xml deleted file mode 100644 index 9f3a4433..00000000 --- a/judo/models/xml/caltech_leap_components/params_and_default.xml +++ /dev/null @@ -1,186 +0,0 @@ - - - diff --git a/judo/models/xml/caltech_leap_components/params_and_default_sim.xml b/judo/models/xml/caltech_leap_components/params_and_default_sim.xml deleted file mode 100644 index 62ea2772..00000000 --- a/judo/models/xml/caltech_leap_components/params_and_default_sim.xml +++ /dev/null @@ -1,186 +0,0 @@ - - - diff --git a/judo/models/xml/caltech_leap_cube.xml b/judo/models/xml/caltech_leap_cube.xml deleted file mode 100644 index 20d6e812..00000000 --- a/judo/models/xml/caltech_leap_cube.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/judo/models/xml/caltech_leap_cube_sim.xml b/judo/models/xml/caltech_leap_cube_sim.xml deleted file mode 100644 index 99148c1d..00000000 --- a/judo/models/xml/caltech_leap_cube_sim.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/judo/models/xml/cartpole.xml b/judo/models/xml/cartpole.xml deleted file mode 100644 index 967d91c2..00000000 --- a/judo/models/xml/cartpole.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/judo/models/xml/cylinder_push.xml b/judo/models/xml/cylinder_push.xml deleted file mode 100644 index 1e75e2fb..00000000 --- a/judo/models/xml/cylinder_push.xml +++ /dev/null @@ -1,45 +0,0 @@ - - diff --git a/judo/models/xml/fr3_components/assets.xml b/judo/models/xml/fr3_components/assets.xml deleted file mode 100644 index ffd0b90d..00000000 --- a/judo/models/xml/fr3_components/assets.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/judo/models/xml/fr3_components/fr3.xml b/judo/models/xml/fr3_components/fr3.xml deleted file mode 100644 index d8ae83de..00000000 --- a/judo/models/xml/fr3_components/fr3.xml +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/judo/models/xml/fr3_components/params_and_default.xml b/judo/models/xml/fr3_components/params_and_default.xml deleted file mode 100644 index 494af3a9..00000000 --- a/judo/models/xml/fr3_components/params_and_default.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - diff --git a/judo/models/xml/fr3_pick.xml b/judo/models/xml/fr3_pick.xml deleted file mode 100644 index bb8e3403..00000000 --- a/judo/models/xml/fr3_pick.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/judo/models/xml/leap_components/assets.xml b/judo/models/xml/leap_components/assets.xml deleted file mode 100644 index 55461f7e..00000000 --- a/judo/models/xml/leap_components/assets.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/judo/models/xml/leap_components/cube_pips_coll.xml b/judo/models/xml/leap_components/cube_pips_coll.xml deleted file mode 100644 index c4098be4..00000000 --- a/judo/models/xml/leap_components/cube_pips_coll.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/judo/models/xml/leap_components/cube_pips_vis.xml b/judo/models/xml/leap_components/cube_pips_vis.xml deleted file mode 100644 index acafcdc8..00000000 --- a/judo/models/xml/leap_components/cube_pips_vis.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/judo/models/xml/leap_components/leap_hand.xml b/judo/models/xml/leap_components/leap_hand.xml deleted file mode 100644 index 9fb80bfb..00000000 --- a/judo/models/xml/leap_components/leap_hand.xml +++ /dev/null @@ -1,260 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/judo/models/xml/leap_components/params_and_default.xml b/judo/models/xml/leap_components/params_and_default.xml deleted file mode 100644 index 03eb572c..00000000 --- a/judo/models/xml/leap_components/params_and_default.xml +++ /dev/null @@ -1,164 +0,0 @@ - - - diff --git a/judo/models/xml/leap_components/params_and_default_sim.xml b/judo/models/xml/leap_components/params_and_default_sim.xml deleted file mode 100644 index c561c810..00000000 --- a/judo/models/xml/leap_components/params_and_default_sim.xml +++ /dev/null @@ -1,164 +0,0 @@ - - - diff --git a/judo/models/xml/leap_cube.xml b/judo/models/xml/leap_cube.xml deleted file mode 100644 index aaa6c2e2..00000000 --- a/judo/models/xml/leap_cube.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/judo/models/xml/leap_cube_palm_down.xml b/judo/models/xml/leap_cube_palm_down.xml deleted file mode 100644 index 98b69e9b..00000000 --- a/judo/models/xml/leap_cube_palm_down.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/judo/models/xml/leap_cube_palm_down_sim.xml b/judo/models/xml/leap_cube_palm_down_sim.xml deleted file mode 100644 index 9c2a9511..00000000 --- a/judo/models/xml/leap_cube_palm_down_sim.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/judo/models/xml/leap_cube_sim.xml b/judo/models/xml/leap_cube_sim.xml deleted file mode 100644 index 1389982f..00000000 --- a/judo/models/xml/leap_cube_sim.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/judo/tasks/caltech_leap_cube.py b/judo/tasks/caltech_leap_cube.py index 53cf442b..49ebdd5e 100644 --- a/judo/tasks/caltech_leap_cube.py +++ b/judo/tasks/caltech_leap_cube.py @@ -4,12 +4,10 @@ import numpy as np -from judo import MODEL_PATH from judo.gui import slider from judo.tasks.leap_cube import LeapCube, LeapCubeConfig +from judo.utils.assets import retrieve_model_from_remote -XML_PATH = str(MODEL_PATH / "xml/caltech_leap_cube.xml") -SIM_XML_PATH = str(MODEL_PATH / "xml/caltech_leap_cube_sim.xml") QPOS_HOME = np.array( [ 0.11, 0.005, 0.04, 1.0, 0.0, 0.0, 0.0, # cube @@ -34,9 +32,14 @@ class CaltechLeapCube(LeapCube): name: str = "caltech_leap_cube" config_t: type[CaltechLeapCubeConfig] = CaltechLeapCubeConfig - def __init__(self, model_path: str = XML_PATH, sim_model_path: str = SIM_XML_PATH) -> None: + def __init__(self, model_path: str | None = None, sim_model_path: str | None = None) -> None: """Initializes the LEAP cube rotation task.""" - super(LeapCube, self).__init__(model_path=model_path, sim_model_path=sim_model_path) + default_model_path, default_sim_model_path = retrieve_model_from_remote(self.name, force=False) + if model_path is None: + model_path = default_model_path + if sim_model_path is None: + sim_model_path = default_sim_model_path + super(LeapCube, self).__init__(model_path, sim_model_path=sim_model_path) self.goal_pos = np.array([0.11, 0.005, 0.03]) self.goal_quat = np.array([1.0, 0.0, 0.0, 0.0]) self.qpos_home = QPOS_HOME diff --git a/judo/tasks/cartpole.py b/judo/tasks/cartpole.py index a0ea97cc..c141a0cd 100644 --- a/judo/tasks/cartpole.py +++ b/judo/tasks/cartpole.py @@ -6,14 +6,12 @@ import mujoco import numpy as np -from judo import MODEL_PATH from judo.tasks.base import Task, TaskConfig from judo.tasks.cost_functions import ( quadratic_norm, smooth_l1_norm, ) - -XML_PATH = str(MODEL_PATH / "xml/cartpole.xml") +from judo.utils.assets import retrieve_model_from_remote @dataclass @@ -34,8 +32,11 @@ class Cartpole(Task[CartpoleConfig]): name: str = "cartpole" config_t: type[CartpoleConfig] = CartpoleConfig - def __init__(self, model_path: str = XML_PATH, sim_model_path: str | None = None) -> None: + def __init__(self, model_path: str | None = None, sim_model_path: str | None = None) -> None: """Initializes the cartpole task.""" + default_model_path, _ = retrieve_model_from_remote(self.name, force=False) + if model_path is None: + model_path = default_model_path super().__init__(model_path=model_path, sim_model_path=sim_model_path) self.reset() diff --git a/judo/tasks/cylinder_push.py b/judo/tasks/cylinder_push.py index be6e554a..671ae207 100644 --- a/judo/tasks/cylinder_push.py +++ b/judo/tasks/cylinder_push.py @@ -6,14 +6,12 @@ import mujoco import numpy as np -from judo import MODEL_PATH from judo.gui import slider from judo.tasks.base import Task, TaskConfig from judo.tasks.cost_functions import quadratic_norm +from judo.utils.assets import retrieve_model_from_remote from judo.utils.fields import np_1d_field -XML_PATH = str(MODEL_PATH / "xml/cylinder_push.xml") - @slider("w_pusher_proximity", 0.0, 5.0, 0.1) @dataclass @@ -42,8 +40,11 @@ class CylinderPush(Task[CylinderPushConfig]): name: str = "cylinder_push" config_t: type[CylinderPushConfig] = CylinderPushConfig - def __init__(self, model_path: str = XML_PATH, sim_model_path: str | None = None) -> None: + def __init__(self, model_path: str | None = None, sim_model_path: str | None = None) -> None: """Initializes the cylinder push task.""" + default_model_path, _ = retrieve_model_from_remote(self.name, force=False) + if model_path is None: + model_path = default_model_path super().__init__(model_path=model_path, sim_model_path=sim_model_path) self.reset() diff --git a/judo/tasks/fr3_pick.py b/judo/tasks/fr3_pick.py index 5551f2c8..86d56601 100644 --- a/judo/tasks/fr3_pick.py +++ b/judo/tasks/fr3_pick.py @@ -7,12 +7,11 @@ import mujoco import numpy as np -from judo import MODEL_PATH from judo.gui import slider from judo.tasks.base import Task, TaskConfig +from judo.utils.assets import retrieve_model_from_remote from judo.utils.fields import np_1d_field -XML_PATH = str(MODEL_PATH / "xml/fr3_pick.xml") QPOS_HOME = np.array( [ 0.7, 0, 0.02, 1, 0, 0, 0, # object @@ -107,8 +106,11 @@ class FR3Pick(Task[FR3PickConfig]): name: str = "fr3_pick" config_t: type[FR3PickConfig] = FR3PickConfig - def __init__(self, model_path: str = XML_PATH, sim_model_path: str | None = None) -> None: + def __init__(self, model_path: str | None = None, sim_model_path: str | None = None) -> None: """Initializes the LEAP cube rotation task.""" + default_model_path, _ = retrieve_model_from_remote("fr3", force=False) + if model_path is None: + model_path = default_model_path super().__init__(model_path=model_path, sim_model_path=sim_model_path) self.reset_command = np.array([0, 0, 0, -1.57079, 0, 1.57079, -0.7853, 0.0]) diff --git a/judo/tasks/leap_cube.py b/judo/tasks/leap_cube.py index 9c27b5f5..b0d5a22c 100644 --- a/judo/tasks/leap_cube.py +++ b/judo/tasks/leap_cube.py @@ -6,13 +6,11 @@ import mujoco import numpy as np -from judo import MODEL_PATH from judo.gui import slider from judo.tasks.base import Task, TaskConfig +from judo.utils.assets import retrieve_model_from_remote from judo.utils.math_utils import quat_diff, quat_diff_so3 -XML_PATH = str(MODEL_PATH / "xml/leap_cube.xml") -SIM_XML_PATH = str(MODEL_PATH / "xml/leap_cube_sim.xml") QPOS_HOME = np.array( [ 0.0, 0.03, 0.1, 1.0, 0.0, 0.0, 0.0, # cube @@ -42,10 +40,15 @@ class LeapCube(Task[LeapCubeConfig]): def __init__( self, - model_path: str = XML_PATH, - sim_model_path: str | None = SIM_XML_PATH, + model_path: str | None = None, + sim_model_path: str | None = None, ) -> None: """Initializes the LEAP cube rotation task.""" + default_model_path, default_sim_model_path = retrieve_model_from_remote(self.name, force=False) + if model_path is None: + model_path = default_model_path + if sim_model_path is None: + sim_model_path = default_sim_model_path super().__init__(model_path=model_path, sim_model_path=sim_model_path) self.goal_pos = np.array([0.0, 0.03, 0.1]) self.goal_quat = np.array([1.0, 0.0, 0.0, 0.0]) diff --git a/judo/tasks/leap_cube_down.py b/judo/tasks/leap_cube_down.py index 465ed046..35b0990e 100644 --- a/judo/tasks/leap_cube_down.py +++ b/judo/tasks/leap_cube_down.py @@ -4,12 +4,10 @@ import numpy as np -from judo import MODEL_PATH from judo.gui import slider from judo.tasks.leap_cube import LeapCube, LeapCubeConfig +from judo.utils.assets import retrieve_model_from_remote -XML_PATH = str(MODEL_PATH / "xml/leap_cube_palm_down.xml") -SIM_XML_PATH = str(MODEL_PATH / "xml/leap_cube_palm_down_sim.xml") QPOS_HOME = np.array( [ -0.04, -0.035, -0.065, 1.0, 0.0, 0.0, 0.0, # cube @@ -36,8 +34,13 @@ class LeapCubeDown(LeapCube): name: str = "leap_cube_down" config_t: type[LeapCubeDownConfig] = LeapCubeDownConfig - def __init__(self, model_path: str = XML_PATH, sim_model_path: str = SIM_XML_PATH) -> None: + def __init__(self, model_path: str | None = None, sim_model_path: str | None = None) -> None: """Initializes the LEAP cube rotation task.""" + default_model_path, default_sim_model_path = retrieve_model_from_remote("leap_cube_palm_down", force=False) + if model_path is None: + model_path = default_model_path + if sim_model_path is None: + sim_model_path = default_sim_model_path super(LeapCube, self).__init__(model_path=model_path, sim_model_path=sim_model_path) self.goal_pos = np.array([-0.04, -0.035, -0.065]) self.goal_quat = np.array([1.0, 0.0, 0.0, 0.0]) diff --git a/judo/utils/assets.py b/judo/utils/assets.py index 527d4fc2..f336f8f7 100644 --- a/judo/utils/assets.py +++ b/judo/utils/assets.py @@ -1,89 +1,187 @@ # Copyright (c) 2025 Robotics and AI Institute LLC. All rights reserved. - +import logging import os -import time -import zipfile +import shutil +from dataclasses import dataclass from pathlib import Path -import requests +from git import ( + GitCommandError, + InvalidGitRepositoryError, + NoSuchPathError, + Repo, +) + +from judo import DESCRIPTION_CACHE_DIR + + +@dataclass(frozen=True) +class RepoInfo: + """Information required to clone a description from a repository.""" + + url: str + description_path: str + remote_model_path: str + remote_model_file: str # File name representing the model + remote_sim_file: str | None = None # Optional simulation model in case it's not the same as the model + commit_hash: str | None = None + + +KNOWN_DESCRIPTIONS = { + "cylinder_push": RepoInfo( + url="https://github.com/bhung-bdai/judo_descriptions_test.git", + commit_hash=None, + description_path="judo_descriptions_test", + remote_model_path="cylinder_push", + remote_model_file="cylinder_push.xml", + ), + "cartpole": RepoInfo( + url="https://github.com/bhung-bdai/judo_descriptions_test.git", + commit_hash=None, + description_path="judo_descriptions_test", + remote_model_path="cartpole", + remote_model_file="cartpole.xml", + ), + "fr3": RepoInfo( + url="https://github.com/bhung-bdai/judo_descriptions_test.git", + commit_hash=None, + description_path="judo_descriptions_test", + remote_model_path="franka_fr3", + remote_model_file="fr3_pick.xml", + ), + "leap_cube": RepoInfo( + url="https://github.com/bhung-bdai/judo_descriptions_test.git", + commit_hash=None, + description_path="judo_descriptions_test", + remote_model_path="leap_hand", + remote_model_file="leap_cube.xml", + ), + "leap_cube_palm_down": RepoInfo( + url="https://github.com/bhung-bdai/judo_descriptions_test.git", + commit_hash=None, + description_path="judo_descriptions_test", + remote_model_path="leap_hand", + remote_model_file="leap_cube_palm_down.xml", + remote_sim_file="leap_cube_palm_down_sim.xml", + ), + "caltech_leap_cube": RepoInfo( + url="https://github.com/bhung-bdai/judo_descriptions_test.git", + commit_hash=None, + description_path="judo_descriptions_test", + remote_model_path="leap_hand", + remote_model_file="caltech_leap_cube.xml", + remote_sim_file="caltech_leap_cube_sim.xml", + ), +} + + +def delete_assets_cache(cache_dir: Path | None = None) -> None: + """Deletes the assets cache.""" + if cache_dir is None: + cache_dir = Path(os.environ.get("JUDO_DESCRIPTION_CACHE_DIR", DESCRIPTION_CACHE_DIR)).expanduser() + if not cache_dir.exists(): + raise FileNotFoundError(f"Cache directory {cache_dir} does not exist!") + shutil.rmtree(cache_dir) + + +def get_description_keys() -> list[str]: + """Returns a list of the keys of the known descriptions.""" + return list(KNOWN_DESCRIPTIONS.keys()) + + +def add_known_description(description_name: str, description_info: RepoInfo) -> None: + """Adds a new description via the RepoInfo to the known descriptions dictionary.""" + KNOWN_DESCRIPTIONS[description_name] = description_info + + +def get_description(description_info: RepoInfo, force: bool = False) -> Repo: + """Clone a description from the provided information and return the path to the cloned repository. + + Args: + description_info: The information about the description to clone. + force: Forcefully redownload the description, regardless of whether or not it already exists. + + Returns: + The path to the cloned description repository. + """ + assert isinstance(description_info, RepoInfo), "description_info must be a RepoInfo object!" + description_url = description_info.url + description_path = Path(description_info.description_path) + commit_hash = description_info.commit_hash + + # Find the directory hosting the cache + cache_dir = Path(os.environ.get("JUDO_DESCRIPTION_CACHE_DIR", DESCRIPTION_CACHE_DIR)).expanduser() + if commit_hash: + description_path = description_path / commit_hash + cache_path = cache_dir / description_path + + description_clone = None + if force: + if cache_path.exists(): + shutil.rmtree(cache_path) + cache_path.mkdir(parents=True, exist_ok=True) + logging.info(f"Cloning description from {description_url} into {cache_path}") + description_clone = Repo.clone_from(description_url, cache_path) + else: + try: + description_clone = Repo(cache_path) + except (InvalidGitRepositoryError, NoSuchPathError, GitCommandError): + # Deletes the existing cache path for this repo if it exists and recreates it + if cache_path.exists(): + shutil.rmtree(cache_path) + cache_path.mkdir(parents=True, exist_ok=True) + logging.info(f"Cloning description from {description_url} into {cache_path}") + description_clone = Repo.clone_from(description_url, cache_path) + + if description_clone is None: + raise FileNotFoundError(f"Failed to clone description from {description_url} into {cache_path}") + + if commit_hash: + try: + description_clone.git.checkout(commit_hash) + except GitCommandError: + description_clone.git.fetch("origin") + description_clone.git.checkout(commit_hash) + logging.info(f"Retrieved description from {description_url} to {description_clone.working_dir}") + return description_clone -def acquire_lock(lock_path: Path, timeout: int = 60, poll_interval: float = 0.1) -> None: - """Acquire a lock by creating a lock file atomically. - Raises TimeoutError if the lock can't be acquired in `timeout` seconds. - """ - start = time.time() - while True: - try: - # open with O_CREAT | O_EXCL to create file atomically, fail if exists - os.open(lock_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY) - return # acquired - except FileExistsError: - if time.time() - start > timeout: - raise TimeoutError(f"Timeout waiting for lock file {lock_path}") from None - time.sleep(poll_interval) +def retrieve_description_path_from_remote(description_name: str, force: bool = False) -> str: + """Gets the path to a description from a remote repository, downloaded and stored in cache. + Args: + description_name: The name of the description to retrieve. + force: Forcefully redownload the description, regardless of whether or not it already exists. -def release_lock(lock_path: Path) -> None: - """Remove the lock file to release the lock.""" + Returns: + The path to the description repository in the local cache. + """ try: - lock_path.unlink() - except FileNotFoundError: - pass - - -def download_and_extract_meshes( - extract_root: str, - repo: str = "bdaiinstitute/judo", - asset_name: str = "meshes.zip", - tag: str | None = None, -) -> None: - """Downloads meshes.zip from the latest public GitHub release and extracts it.""" - extract_path = Path(extract_root).expanduser() - meshes_path = extract_path / "meshes" - lock_path = extract_path / ".meshes_download.lock" + description_info = KNOWN_DESCRIPTIONS[description_name] + except KeyError as e: + raise ValueError(f"Description {description_name} not found in {get_description_keys()}!") from e + description_clone = get_description(description_info, force=force) + return f"{description_clone.working_dir}/{description_info.remote_model_path}" + + +def retrieve_model_from_remote(description_name: str, force: bool = False) -> tuple[str, str | None]: + """Gets the path to a model (and simulation file, if available) in a remote description, stored in cache. + Args: + description_name: The name of the description to retrieve. + force: Forcefully redownload the description, regardless of whether or not it already exists. + + Returns: + The path to the description repository in the local cache. + """ try: - acquire_lock(lock_path) # prevent race conditions resulting in multiple downloads - - # case: meshes already extracted - if meshes_path.exists(): - return - - # fetch latest release info - print("Mesh assets not detected! Downloading assets now...") - if tag is None: - api_url = f"https://api.github.com/repos/{repo}/releases/latest" - else: - api_url = f"https://api.github.com/repos/{repo}/releases/tags/{tag}" - response = requests.get(api_url) - response.raise_for_status() - release_data = response.json() - - # get the download URL for meshes.zip - asset_url = None - for asset in release_data.get("assets", []): - if asset["name"] == asset_name: - asset_url = asset["browser_download_url"] - break - if asset_url is None: - raise ValueError(f"{asset_name} not found in latest release of {repo}.") - - # download and extract - zip_path = meshes_path.with_suffix(".zip") - meshes_path.mkdir(parents=True, exist_ok=True) - with requests.get(asset_url, stream=True) as r: - r.raise_for_status() - with open(zip_path, "wb") as f: - for chunk in r.iter_content(chunk_size=8192): - f.write(chunk) - - # extract the zip file - with zipfile.ZipFile(zip_path, "r") as zip_ref: - zip_ref.extractall(extract_path) - if zip_path.exists(): - zip_path.unlink() # remove the zip file after extraction - - finally: - release_lock(lock_path) + description_info = KNOWN_DESCRIPTIONS[description_name] + except KeyError as e: + raise ValueError(f"Description {description_name} not found in {get_description_keys()}!") from e + description_model_path = retrieve_description_path_from_remote(description_name, force=force) + description_model_file = f"{description_model_path}/{description_info.remote_model_file}" + + if description_info.remote_sim_file is not None: + return (description_model_file, f"{description_model_path}/{description_info.remote_sim_file}") + return (description_model_file, None) diff --git a/pixi.lock b/pixi.lock index 6d557191..be62e29f 100644 --- a/pixi.lock +++ b/pixi.lock @@ -43,6 +43,8 @@ environments: - pypi: https://files.pythonhosted.org/packages/3f/d7/11e00e53ef9fb475ee80a9327af61a491d9f4ec10356be4a23dd6ff4ee47/embreex-2.17.7.post6-cp313-cp313-manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e7/98/87b5946356095738cb90a6df7b35ff69ac5750f6e783d5fbcc5cb3b6cbd7/etils-1.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7f/ba/de3630757c7d7fc2086aaf3994926d6b869d31586e4d0c14f1666af31b93/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl @@ -83,6 +85,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e4/82/08e4076df538fb56caa1d489588d880ec7c52d8273a606bb54d660528f7c/scipy-1.16.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/dd/87/1017c31e52370b2b79e4d29e07cbb590ab9e5e58cf7e2bdfe363765d6251/shapely-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/83/4f5b250220e1a5acd31345a5ec1c95a7769725d0d8135276f399f44062f8/svg_path-7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/56/b3/23eec760215910609914dd99aba23ce1c72a3bcbe046ee44f45adf740452/tifffile-2025.8.28-py3-none-any.whl @@ -128,6 +131,8 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/d6/aa06d1060e6526f57d0920ccc5554d83de7dc5cd07d1ea4f2be53b40bc05/embreex-2.17.7.post6-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e7/98/87b5946356095738cb90a6df7b35ff69ac5750f6e783d5fbcc5cb3b6cbd7/etils-1.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/21/71/13dd8a8d547809543d21de9438a3a76a8728fc7966d01ad9fb54599aebf5/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-macosx_10_6_intel.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl @@ -168,6 +173,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/93/0b/b5c99382b839854a71ca9482c684e3472badc62620287cbbdab499b75ce6/scipy-1.16.1-cp313-cp313-macosx_10_14_x86_64.whl - pypi: https://files.pythonhosted.org/packages/71/8e/2bc836437f4b84d62efc1faddce0d4e023a5d990bbddd3c78b2004ebc246/shapely-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/83/4f5b250220e1a5acd31345a5ec1c95a7769725d0d8135276f399f44062f8/svg_path-7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/56/b3/23eec760215910609914dd99aba23ce1c72a3bcbe046ee44f45adf740452/tifffile-2025.8.28-py3-none-any.whl @@ -213,6 +219,8 @@ environments: - pypi: https://files.pythonhosted.org/packages/78/62/0ffc083b1d5f7c95eb03b260d2622dfacb70d1153011d216260b71c8e7d9/dora_utils-0.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/98/87b5946356095738cb90a6df7b35ff69ac5750f6e783d5fbcc5cb3b6cbd7/etils-1.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f8/a2/45e6dceec1e0a0ffa8dd3c0ecf1e11d74639a55186243129160c6434d456/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl @@ -253,6 +261,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/eb/e5/69ab2771062c91e23e07c12e7d5033a6b9b80b0903ee709c3c36b3eb520c/scipy-1.16.1-cp313-cp313-macosx_12_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/12/a2/12c7cae5b62d5d851c2db836eadd0986f63918a91976495861f7c492f4a9/shapely-2.1.1-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/83/4f5b250220e1a5acd31345a5ec1c95a7769725d0d8135276f399f44062f8/svg_path-7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/56/b3/23eec760215910609914dd99aba23ce1c72a3bcbe046ee44f45adf740452/tifffile-2025.8.28-py3-none-any.whl @@ -300,6 +309,8 @@ environments: - pypi: https://files.pythonhosted.org/packages/78/62/0ffc083b1d5f7c95eb03b260d2622dfacb70d1153011d216260b71c8e7d9/dora_utils-0.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/98/87b5946356095738cb90a6df7b35ff69ac5750f6e783d5fbcc5cb3b6cbd7/etils-1.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/70/7f2f052ca20c3b69892818f2ee1fea53b037ea9145ff75b944ed1dc4ff82/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl @@ -340,6 +351,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/21/12/c0efd2941f01940119b5305c375ae5c0fcb7ec193f806bd8f158b73a1782/scipy-1.16.1-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/12/d9/6d13b8957a17c95794f0c4dfb65ecd0957e6c7131a56ce18d135c1107a52/shapely-2.1.1-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/83/4f5b250220e1a5acd31345a5ec1c95a7769725d0d8135276f399f44062f8/svg_path-7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/56/b3/23eec760215910609914dd99aba23ce1c72a3bcbe046ee44f45adf740452/tifffile-2025.8.28-py3-none-any.whl @@ -410,6 +422,8 @@ environments: - pypi: https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/34/2b07b72bee02a63241d654f5d8af87a2de977c59638eec41ca356ab915cd/furo-2025.7.19-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7f/ba/de3630757c7d7fc2086aaf3994926d6b869d31586e4d0c14f1666af31b93/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl @@ -467,6 +481,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e4/82/08e4076df538fb56caa1d489588d880ec7c52d8273a606bb54d660528f7c/scipy-1.16.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/dd/87/1017c31e52370b2b79e4d29e07cbb590ab9e5e58cf7e2bdfe363765d6251/shapely-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl @@ -538,6 +553,8 @@ environments: - pypi: https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/34/2b07b72bee02a63241d654f5d8af87a2de977c59638eec41ca356ab915cd/furo-2025.7.19-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/21/71/13dd8a8d547809543d21de9438a3a76a8728fc7966d01ad9fb54599aebf5/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-macosx_10_6_intel.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl @@ -595,6 +612,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/93/0b/b5c99382b839854a71ca9482c684e3472badc62620287cbbdab499b75ce6/scipy-1.16.1-cp313-cp313-macosx_10_14_x86_64.whl - pypi: https://files.pythonhosted.org/packages/71/8e/2bc836437f4b84d62efc1faddce0d4e023a5d990bbddd3c78b2004ebc246/shapely-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl @@ -666,6 +684,8 @@ environments: - pypi: https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/34/2b07b72bee02a63241d654f5d8af87a2de977c59638eec41ca356ab915cd/furo-2025.7.19-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f8/a2/45e6dceec1e0a0ffa8dd3c0ecf1e11d74639a55186243129160c6434d456/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl @@ -723,6 +743,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/eb/e5/69ab2771062c91e23e07c12e7d5033a6b9b80b0903ee709c3c36b3eb520c/scipy-1.16.1-cp313-cp313-macosx_12_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/12/a2/12c7cae5b62d5d851c2db836eadd0986f63918a91976495861f7c492f4a9/shapely-2.1.1-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl @@ -796,6 +817,8 @@ environments: - pypi: https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/34/2b07b72bee02a63241d654f5d8af87a2de977c59638eec41ca356ab915cd/furo-2025.7.19-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/70/7f2f052ca20c3b69892818f2ee1fea53b037ea9145ff75b944ed1dc4ff82/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl @@ -853,6 +876,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/21/12/c0efd2941f01940119b5305c375ae5c0fcb7ec193f806bd8f158b73a1782/scipy-1.16.1-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/12/d9/6d13b8957a17c95794f0c4dfb65ecd0957e6c7131a56ce18d135c1107a52/shapely-2.1.1-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl @@ -932,6 +956,8 @@ environments: - pypi: https://files.pythonhosted.org/packages/e7/98/87b5946356095738cb90a6df7b35ff69ac5750f6e783d5fbcc5cb3b6cbd7/etils-1.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/34/2b07b72bee02a63241d654f5d8af87a2de977c59638eec41ca356ab915cd/furo-2025.7.19-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7f/ba/de3630757c7d7fc2086aaf3994926d6b869d31586e4d0c14f1666af31b93/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl @@ -978,6 +1004,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e4/82/08e4076df538fb56caa1d489588d880ec7c52d8273a606bb54d660528f7c/scipy-1.16.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/dd/87/1017c31e52370b2b79e4d29e07cbb590ab9e5e58cf7e2bdfe363765d6251/shapely-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl @@ -1043,6 +1070,8 @@ environments: - pypi: https://files.pythonhosted.org/packages/e7/98/87b5946356095738cb90a6df7b35ff69ac5750f6e783d5fbcc5cb3b6cbd7/etils-1.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/34/2b07b72bee02a63241d654f5d8af87a2de977c59638eec41ca356ab915cd/furo-2025.7.19-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/21/71/13dd8a8d547809543d21de9438a3a76a8728fc7966d01ad9fb54599aebf5/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-macosx_10_6_intel.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl @@ -1089,6 +1118,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/93/0b/b5c99382b839854a71ca9482c684e3472badc62620287cbbdab499b75ce6/scipy-1.16.1-cp313-cp313-macosx_10_14_x86_64.whl - pypi: https://files.pythonhosted.org/packages/71/8e/2bc836437f4b84d62efc1faddce0d4e023a5d990bbddd3c78b2004ebc246/shapely-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl @@ -1154,6 +1184,8 @@ environments: - pypi: https://files.pythonhosted.org/packages/e7/98/87b5946356095738cb90a6df7b35ff69ac5750f6e783d5fbcc5cb3b6cbd7/etils-1.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/34/2b07b72bee02a63241d654f5d8af87a2de977c59638eec41ca356ab915cd/furo-2025.7.19-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f8/a2/45e6dceec1e0a0ffa8dd3c0ecf1e11d74639a55186243129160c6434d456/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl @@ -1200,6 +1232,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/eb/e5/69ab2771062c91e23e07c12e7d5033a6b9b80b0903ee709c3c36b3eb520c/scipy-1.16.1-cp313-cp313-macosx_12_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/12/a2/12c7cae5b62d5d851c2db836eadd0986f63918a91976495861f7c492f4a9/shapely-2.1.1-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl @@ -1267,6 +1300,8 @@ environments: - pypi: https://files.pythonhosted.org/packages/e7/98/87b5946356095738cb90a6df7b35ff69ac5750f6e783d5fbcc5cb3b6cbd7/etils-1.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/34/2b07b72bee02a63241d654f5d8af87a2de977c59638eec41ca356ab915cd/furo-2025.7.19-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/70/7f2f052ca20c3b69892818f2ee1fea53b037ea9145ff75b944ed1dc4ff82/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl @@ -1313,6 +1348,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/21/12/c0efd2941f01940119b5305c375ae5c0fcb7ec193f806bd8f158b73a1782/scipy-1.16.1-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/12/d9/6d13b8957a17c95794f0c4dfb65ecd0957e6c7131a56ce18d135c1107a52/shapely-2.1.1-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl @@ -1933,6 +1969,35 @@ packages: - pygments>=2.7 - accessible-pygments>=0.0.5 requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + name: gitdb + version: 4.0.12 + sha256: 67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf + requires_dist: + - smmap>=3.0.1,<6 + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl + name: gitpython + version: 3.1.45 + sha256: 8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77 + requires_dist: + - gitdb>=4.0.1,<5 + - typing-extensions>=3.10.0.2 ; python_full_version < '3.10' + - coverage[toml] ; extra == 'test' + - ddt>=1.1.1,!=1.4.3 ; extra == 'test' + - mock ; python_full_version < '3.8' and extra == 'test' + - mypy ; extra == 'test' + - pre-commit ; extra == 'test' + - pytest>=7.3.1 ; extra == 'test' + - pytest-cov ; extra == 'test' + - pytest-instafail ; extra == 'test' + - pytest-mock ; extra == 'test' + - pytest-sugar ; extra == 'test' + - typing-extensions ; python_full_version < '3.11' and extra == 'test' + - sphinx>=7.1.2,<7.2 ; extra == 'doc' + - sphinx-rtd-theme ; extra == 'doc' + - sphinx-autodoc-typehints ; extra == 'doc' + requires_python: '>=3.7' - pypi: https://files.pythonhosted.org/packages/21/71/13dd8a8d547809543d21de9438a3a76a8728fc7966d01ad9fb54599aebf5/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-macosx_10_6_intel.whl name: glfw version: 2.9.0 @@ -2168,9 +2233,10 @@ packages: - pypi: ./ name: judo-rai version: 0.0.4 - sha256: d4ac7cd64855d3dfdd89ad3fd54ad4706bc4095e670c15f516043b856aa37df3 + sha256: 3ab39d2dd098aafb4162eb27fd25175d942bebb9b20b088f031f539dfba3c757 requires_dist: - dora-utils + - gitpython>=3.1.18 - mujoco - numpy - pillow @@ -4236,6 +4302,11 @@ packages: version: 1.17.0 sha256: 4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*' +- pypi: https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl + name: smmap + version: 5.0.2 + sha256: b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e + requires_python: '>=3.7' - pypi: https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl name: sniffio version: 1.3.1 diff --git a/pyproject.toml b/pyproject.toml index afac3772..608034d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,7 @@ classifiers = [ dependencies = [ "dora-utils", # dora + qol utils for writing nodes + "GitPython >=3.1.18", # required to clone description assets from remote repositories "mujoco", "numpy", "pillow", # for displaying app logo @@ -178,4 +179,4 @@ judo-rai = { path = ".", editable = true } [tool.pixi.environments] default = { solve-group = "default" } docs = { features = ["docs"], solve-group = "default" } -dev = { features = ["dev", "docs"], solve-group = "default" } \ No newline at end of file +dev = { features = ["dev", "docs"], solve-group = "default" } diff --git a/tests/test_visualizer.py b/tests/test_visualizer.py index de1871f7..f6d2d390 100644 --- a/tests/test_visualizer.py +++ b/tests/test_visualizer.py @@ -7,7 +7,7 @@ from trimesh.visual import TextureVisuals from viser import ViserServer -from judo import MODEL_PATH +from judo.utils.assets import retrieve_model_from_remote from judo.visualizers.model import ( ViserMjModel, add_box, @@ -27,7 +27,7 @@ # Create a global ViserServer instance for use by the tests. viser_server = ViserServer() -model_path = str(MODEL_PATH / "xml/cylinder_push.xml") +model_path, _ = retrieve_model_from_remote("cylinder_push", force=False) spec = mujoco.MjSpec.from_file(model_path) model = spec.compile()