nepher 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nepher/__init__.py +36 -0
- nepher/api/__init__.py +6 -0
- nepher/api/client.py +384 -0
- nepher/api/endpoints.py +97 -0
- nepher/auth.py +150 -0
- nepher/cli/__init__.py +2 -0
- nepher/cli/commands/__init__.py +6 -0
- nepher/cli/commands/auth.py +37 -0
- nepher/cli/commands/cache.py +85 -0
- nepher/cli/commands/config.py +77 -0
- nepher/cli/commands/download.py +72 -0
- nepher/cli/commands/list.py +75 -0
- nepher/cli/commands/upload.py +69 -0
- nepher/cli/commands/view.py +310 -0
- nepher/cli/main.py +30 -0
- nepher/cli/utils.py +28 -0
- nepher/config.py +202 -0
- nepher/core.py +67 -0
- nepher/env_cfgs/__init__.py +7 -0
- nepher/env_cfgs/base.py +32 -0
- nepher/env_cfgs/manipulation/__init__.py +4 -0
- nepher/env_cfgs/navigation/__init__.py +45 -0
- nepher/env_cfgs/navigation/abstract_nav_cfg.py +159 -0
- nepher/env_cfgs/navigation/preset_nav_cfg.py +590 -0
- nepher/env_cfgs/navigation/usd_nav_cfg.py +644 -0
- nepher/env_cfgs/registry.py +31 -0
- nepher/loader/__init__.py +9 -0
- nepher/loader/base.py +27 -0
- nepher/loader/category_loaders/__init__.py +2 -0
- nepher/loader/preset_loader.py +80 -0
- nepher/loader/registry.py +63 -0
- nepher/loader/usd_loader.py +49 -0
- nepher/storage/__init__.py +8 -0
- nepher/storage/bundle.py +78 -0
- nepher/storage/cache.py +145 -0
- nepher/storage/manifest.py +80 -0
- nepher/utils/__init__.py +12 -0
- nepher/utils/fast_spawn_sampler.py +334 -0
- nepher/utils/free_zone_finder.py +239 -0
- nepher-0.1.0.dist-info/METADATA +235 -0
- nepher-0.1.0.dist-info/RECORD +45 -0
- nepher-0.1.0.dist-info/WHEEL +5 -0
- nepher-0.1.0.dist-info/entry_points.txt +2 -0
- nepher-0.1.0.dist-info/licenses/LICENSE +97 -0
- nepher-0.1.0.dist-info/top_level.txt +1 -0
nepher/loader/base.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base loader interface.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from nepher.core import Environment
|
|
7
|
+
from nepher.env_cfgs.base import BaseEnvCfg
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BaseLoader(ABC):
|
|
11
|
+
"""Base interface for environment loaders."""
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def load(self, env: Environment, scene_idx: int, category: str) -> BaseEnvCfg:
|
|
15
|
+
"""
|
|
16
|
+
Load scene config.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
env: Environment object
|
|
20
|
+
scene_idx: Scene index
|
|
21
|
+
category: Environment category
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Category-appropriate config class
|
|
25
|
+
"""
|
|
26
|
+
pass
|
|
27
|
+
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Preset environment loader.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import importlib.util
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional
|
|
9
|
+
from nepher.core import Environment
|
|
10
|
+
from nepher.loader.base import BaseLoader
|
|
11
|
+
from nepher.env_cfgs.registry import get_config_class
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def load_preset_module(preset_path: str, base_path: Optional[Path] = None):
|
|
15
|
+
"""Load preset module from path.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
preset_path: Path to preset file (e.g., "my_preset.py") or module path
|
|
19
|
+
base_path: Base directory for resolving relative file paths
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Preset config class
|
|
23
|
+
|
|
24
|
+
Raises:
|
|
25
|
+
FileNotFoundError: If preset file is not found
|
|
26
|
+
ImportError: If preset module cannot be loaded
|
|
27
|
+
ValueError: If no preset config class is found
|
|
28
|
+
"""
|
|
29
|
+
if preset_path.endswith(".py"):
|
|
30
|
+
if base_path:
|
|
31
|
+
file_path = base_path / preset_path
|
|
32
|
+
else:
|
|
33
|
+
file_path = Path(preset_path)
|
|
34
|
+
|
|
35
|
+
if not file_path.exists():
|
|
36
|
+
raise FileNotFoundError(f"Preset file not found: {file_path}")
|
|
37
|
+
|
|
38
|
+
module_name = f"nepher_preset_{file_path.stem}_{id(file_path)}"
|
|
39
|
+
|
|
40
|
+
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
|
41
|
+
if spec is None or spec.loader is None:
|
|
42
|
+
raise ImportError(f"Could not load preset from {file_path}")
|
|
43
|
+
|
|
44
|
+
module = importlib.util.module_from_spec(spec)
|
|
45
|
+
sys.modules[module_name] = module
|
|
46
|
+
spec.loader.exec_module(module)
|
|
47
|
+
|
|
48
|
+
for attr_name in dir(module):
|
|
49
|
+
attr = getattr(module, attr_name)
|
|
50
|
+
if (isinstance(attr, type) and
|
|
51
|
+
(attr_name.endswith("Cfg") or attr_name.endswith("PresetCfg")) and
|
|
52
|
+
attr_name != "PresetNavigationEnvCfg"):
|
|
53
|
+
return attr
|
|
54
|
+
|
|
55
|
+
raise ValueError(f"No preset config class found in {file_path}")
|
|
56
|
+
else:
|
|
57
|
+
parts = preset_path.split(".")
|
|
58
|
+
module_path = ".".join(parts[:-1])
|
|
59
|
+
class_name = parts[-1]
|
|
60
|
+
|
|
61
|
+
module = importlib.import_module(module_path)
|
|
62
|
+
return getattr(module, class_name)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class PresetLoader(BaseLoader):
|
|
66
|
+
"""Generic preset loader that returns category-specific configs."""
|
|
67
|
+
|
|
68
|
+
def load(self, env: Environment, scene_idx: int, category: str):
|
|
69
|
+
"""Load preset scene config."""
|
|
70
|
+
if scene_idx >= len(env.preset_scenes):
|
|
71
|
+
raise IndexError(f"Preset scene index {scene_idx} out of range")
|
|
72
|
+
|
|
73
|
+
scene = env.preset_scenes[scene_idx]
|
|
74
|
+
|
|
75
|
+
if not scene.preset:
|
|
76
|
+
raise ValueError("Preset scene missing preset path")
|
|
77
|
+
|
|
78
|
+
preset_class = load_preset_module(scene.preset, base_path=env.cache_path)
|
|
79
|
+
return preset_class()
|
|
80
|
+
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Loader registry and convenience functions.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional, Union
|
|
6
|
+
from nepher.core import Environment
|
|
7
|
+
from nepher.storage.cache import get_cache_manager
|
|
8
|
+
from nepher.storage.manifest import ManifestParser
|
|
9
|
+
from nepher.api.client import get_client
|
|
10
|
+
from nepher.loader.usd_loader import UsdLoader
|
|
11
|
+
from nepher.loader.preset_loader import PresetLoader
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def load_env(env_id: str, category: str) -> Environment:
|
|
15
|
+
"""
|
|
16
|
+
Load environment from cache or download if needed.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
env_id: Environment ID
|
|
20
|
+
category: Environment category
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Environment object
|
|
24
|
+
"""
|
|
25
|
+
cache_manager = get_cache_manager(category=category)
|
|
26
|
+
cache_path = cache_manager.get_env_cache_path(env_id)
|
|
27
|
+
|
|
28
|
+
if cache_manager.is_cached(env_id):
|
|
29
|
+
manifest_path = cache_path / "manifest.yaml"
|
|
30
|
+
return ManifestParser.parse(manifest_path)
|
|
31
|
+
|
|
32
|
+
raise FileNotFoundError(
|
|
33
|
+
f"Environment {env_id} not found in cache. Use download() first."
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def load_scene(env: Environment, scene: Union[str, int], category: str):
|
|
38
|
+
"""
|
|
39
|
+
Load scene config.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
env: Environment object
|
|
43
|
+
scene: Scene name or index
|
|
44
|
+
category: Environment category
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Category-appropriate config class
|
|
48
|
+
"""
|
|
49
|
+
scene_obj = env.get_scene(scene)
|
|
50
|
+
if not scene_obj:
|
|
51
|
+
raise ValueError(f"Scene {scene} not found in environment {env.id}")
|
|
52
|
+
|
|
53
|
+
if scene_obj.usd:
|
|
54
|
+
loader = UsdLoader()
|
|
55
|
+
scene_idx = env.scenes.index(scene_obj)
|
|
56
|
+
return loader.load(env, scene_idx, category)
|
|
57
|
+
elif scene_obj.preset:
|
|
58
|
+
loader = PresetLoader()
|
|
59
|
+
scene_idx = env.preset_scenes.index(scene_obj)
|
|
60
|
+
return loader.load(env, scene_idx, category)
|
|
61
|
+
else:
|
|
62
|
+
raise ValueError(f"Scene {scene} has no USD or preset path")
|
|
63
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
USD environment loader.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from nepher.core import Environment
|
|
7
|
+
from nepher.loader.base import BaseLoader
|
|
8
|
+
from nepher.loader.preset_loader import load_preset_module
|
|
9
|
+
from nepher.env_cfgs.registry import get_config_class
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class UsdLoader(BaseLoader):
|
|
13
|
+
"""Generic USD loader that returns category-specific configs."""
|
|
14
|
+
|
|
15
|
+
def load(self, env: Environment, scene_idx: int, category: str):
|
|
16
|
+
"""Load USD scene config.
|
|
17
|
+
|
|
18
|
+
If the scene has a Python scene file, it will be loaded and used to configure
|
|
19
|
+
the USD environment. Otherwise, a basic config is created from the manifest.
|
|
20
|
+
"""
|
|
21
|
+
if scene_idx >= len(env.scenes):
|
|
22
|
+
raise IndexError(f"Scene index {scene_idx} out of range")
|
|
23
|
+
|
|
24
|
+
scene = env.scenes[scene_idx]
|
|
25
|
+
|
|
26
|
+
if scene.scene:
|
|
27
|
+
scene_class = load_preset_module(scene.scene, base_path=env.cache_path)
|
|
28
|
+
cfg = scene_class()
|
|
29
|
+
|
|
30
|
+
if scene.usd and not cfg.usd_path:
|
|
31
|
+
cfg.usd_path = str(scene.usd)
|
|
32
|
+
if scene.omap_meta and not cfg.occupancy_map_yaml:
|
|
33
|
+
cfg.occupancy_map_yaml = str(scene.omap_meta)
|
|
34
|
+
if scene.name and not cfg.name:
|
|
35
|
+
cfg.name = scene.name
|
|
36
|
+
if scene.description and not cfg.description:
|
|
37
|
+
cfg.description = scene.description
|
|
38
|
+
|
|
39
|
+
return cfg
|
|
40
|
+
|
|
41
|
+
config_class = get_config_class(category=category, type="usd")
|
|
42
|
+
cfg = config_class()
|
|
43
|
+
cfg.usd_path = str(scene.usd) if scene.usd else None
|
|
44
|
+
cfg.occupancy_map_yaml = str(scene.omap_meta) if scene.omap_meta else None
|
|
45
|
+
cfg.name = scene.name
|
|
46
|
+
cfg.description = scene.description
|
|
47
|
+
|
|
48
|
+
return cfg
|
|
49
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Storage and cache management."""
|
|
2
|
+
|
|
3
|
+
from nepher.storage.cache import CacheManager, get_cache_manager
|
|
4
|
+
from nepher.storage.bundle import BundleManager
|
|
5
|
+
from nepher.storage.manifest import ManifestParser
|
|
6
|
+
|
|
7
|
+
__all__ = ["CacheManager", "get_cache_manager", "BundleManager", "ManifestParser"]
|
|
8
|
+
|
nepher/storage/bundle.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bundle validation and extraction.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import zipfile
|
|
6
|
+
import shutil
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional
|
|
9
|
+
from nepher.storage.manifest import ManifestParser
|
|
10
|
+
from nepher.core import Environment
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BundleManager:
|
|
14
|
+
"""Manages environment bundle operations."""
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def extract_bundle(zip_path: Path, dest_dir: Path) -> Environment:
|
|
18
|
+
"""
|
|
19
|
+
Extract and validate bundle.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
zip_path: Path to bundle ZIP file
|
|
23
|
+
dest_dir: Destination directory for extraction
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Environment object from manifest
|
|
27
|
+
"""
|
|
28
|
+
dest_dir.mkdir(parents=True, exist_ok=True)
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
with zipfile.ZipFile(zip_path, "r") as zip_ref:
|
|
32
|
+
zip_ref.extractall(dest_dir)
|
|
33
|
+
except zipfile.BadZipFile as e:
|
|
34
|
+
raise ValueError(f"Invalid ZIP file: {zip_path}") from e
|
|
35
|
+
except (PermissionError, OSError) as e:
|
|
36
|
+
raise RuntimeError(f"Cannot extract bundle to {dest_dir}: {e}") from e
|
|
37
|
+
|
|
38
|
+
manifest_path = dest_dir / "manifest.yaml"
|
|
39
|
+
if not manifest_path.exists():
|
|
40
|
+
raise ValueError(f"Manifest not found in bundle: {manifest_path}")
|
|
41
|
+
|
|
42
|
+
env = ManifestParser.parse(manifest_path)
|
|
43
|
+
|
|
44
|
+
for scene in env.scenes:
|
|
45
|
+
if scene.usd:
|
|
46
|
+
scene.usd = dest_dir / scene.usd
|
|
47
|
+
if scene.omap_meta:
|
|
48
|
+
scene.omap_meta = dest_dir / scene.omap_meta
|
|
49
|
+
|
|
50
|
+
env.cache_path = dest_dir
|
|
51
|
+
|
|
52
|
+
return env
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def validate_bundle(bundle_path: Path) -> bool:
|
|
56
|
+
"""
|
|
57
|
+
Validate bundle structure.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
bundle_path: Path to bundle (ZIP file or directory)
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
True if valid, False otherwise
|
|
64
|
+
"""
|
|
65
|
+
try:
|
|
66
|
+
if bundle_path.is_dir():
|
|
67
|
+
manifest_path = bundle_path / "manifest.yaml"
|
|
68
|
+
return manifest_path.exists()
|
|
69
|
+
|
|
70
|
+
if bundle_path.suffix.lower() == ".zip":
|
|
71
|
+
with zipfile.ZipFile(bundle_path, "r") as zip_ref:
|
|
72
|
+
namelist = zip_ref.namelist()
|
|
73
|
+
return "manifest.yaml" in namelist
|
|
74
|
+
|
|
75
|
+
return False
|
|
76
|
+
except Exception:
|
|
77
|
+
return False
|
|
78
|
+
|
nepher/storage/cache.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Cache management for downloaded environments.
|
|
3
|
+
|
|
4
|
+
Handles user-configurable cache directories and cache operations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import shutil
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import List, Optional, Dict, Any
|
|
10
|
+
from nepher.config import get_config
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CacheManager:
|
|
14
|
+
"""Manages local cache for environments."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, cache_dir: Optional[Path] = None, category: Optional[str] = None):
|
|
17
|
+
"""
|
|
18
|
+
Initialize cache manager.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
cache_dir: Override cache directory (from CLI flag)
|
|
22
|
+
category: Category for category-specific cache
|
|
23
|
+
"""
|
|
24
|
+
config = get_config()
|
|
25
|
+
self.cache_dir = cache_dir or config.get_cache_dir(category=category)
|
|
26
|
+
self.category = category
|
|
27
|
+
|
|
28
|
+
def get_env_cache_path(self, env_id: str) -> Path:
|
|
29
|
+
"""Get cache path for a specific environment."""
|
|
30
|
+
return self.cache_dir / env_id
|
|
31
|
+
|
|
32
|
+
def is_cached(self, env_id: str) -> bool:
|
|
33
|
+
"""Check if environment is cached."""
|
|
34
|
+
cache_path = self.get_env_cache_path(env_id)
|
|
35
|
+
return cache_path.exists() and (cache_path / "manifest.yaml").exists()
|
|
36
|
+
|
|
37
|
+
def list_cached(self) -> List[str]:
|
|
38
|
+
"""
|
|
39
|
+
List all cached environment IDs.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
List of cached environment ID strings
|
|
43
|
+
"""
|
|
44
|
+
if not self.cache_dir.exists():
|
|
45
|
+
return []
|
|
46
|
+
|
|
47
|
+
cached = []
|
|
48
|
+
try:
|
|
49
|
+
for item in self.cache_dir.iterdir():
|
|
50
|
+
if item.is_dir() and (item / "manifest.yaml").exists():
|
|
51
|
+
cached.append(item.name)
|
|
52
|
+
except (PermissionError, OSError) as e:
|
|
53
|
+
raise RuntimeError(f"Cannot access cache directory {self.cache_dir}: {e}") from e
|
|
54
|
+
|
|
55
|
+
return cached
|
|
56
|
+
|
|
57
|
+
def clear_cache(self, env_id: Optional[str] = None):
|
|
58
|
+
"""
|
|
59
|
+
Clear cache.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
env_id: Specific environment ID to clear, or None to clear all
|
|
63
|
+
|
|
64
|
+
Raises:
|
|
65
|
+
RuntimeError: If cache directory cannot be accessed
|
|
66
|
+
"""
|
|
67
|
+
try:
|
|
68
|
+
if env_id:
|
|
69
|
+
cache_path = self.get_env_cache_path(env_id)
|
|
70
|
+
if cache_path.exists():
|
|
71
|
+
shutil.rmtree(cache_path)
|
|
72
|
+
else:
|
|
73
|
+
if self.cache_dir.exists():
|
|
74
|
+
shutil.rmtree(self.cache_dir)
|
|
75
|
+
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
|
76
|
+
except (PermissionError, OSError) as e:
|
|
77
|
+
raise RuntimeError(f"Cannot clear cache: {e}") from e
|
|
78
|
+
|
|
79
|
+
def get_cache_info(self) -> Dict[str, Any]:
|
|
80
|
+
"""Get cache statistics."""
|
|
81
|
+
if not self.cache_dir.exists():
|
|
82
|
+
return {
|
|
83
|
+
"cache_dir": str(self.cache_dir),
|
|
84
|
+
"total_size": 0,
|
|
85
|
+
"env_count": 0,
|
|
86
|
+
"environments": [],
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
envs = self.list_cached()
|
|
90
|
+
total_size = 0
|
|
91
|
+
env_sizes = {}
|
|
92
|
+
|
|
93
|
+
for env_id in envs:
|
|
94
|
+
try:
|
|
95
|
+
env_path = self.get_env_cache_path(env_id)
|
|
96
|
+
size = sum(f.stat().st_size for f in env_path.rglob("*") if f.is_file())
|
|
97
|
+
env_sizes[env_id] = size
|
|
98
|
+
total_size += size
|
|
99
|
+
except (PermissionError, OSError):
|
|
100
|
+
continue
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
"cache_dir": str(self.cache_dir),
|
|
104
|
+
"total_size": total_size,
|
|
105
|
+
"env_count": len(envs),
|
|
106
|
+
"environments": [{"id": eid, "size": env_sizes[eid]} for eid in envs],
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
def migrate_cache(self, new_cache_dir: Path):
|
|
110
|
+
"""
|
|
111
|
+
Migrate cache to new location.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
new_cache_dir: New cache directory path
|
|
115
|
+
|
|
116
|
+
Raises:
|
|
117
|
+
RuntimeError: If migration fails
|
|
118
|
+
"""
|
|
119
|
+
if not self.cache_dir.exists():
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
new_cache_dir.mkdir(parents=True, exist_ok=True)
|
|
124
|
+
|
|
125
|
+
for env_id in self.list_cached():
|
|
126
|
+
old_path = self.get_env_cache_path(env_id)
|
|
127
|
+
new_path = new_cache_dir / env_id
|
|
128
|
+
shutil.move(str(old_path), str(new_path))
|
|
129
|
+
except (PermissionError, OSError, shutil.Error) as e:
|
|
130
|
+
raise RuntimeError(f"Cannot migrate cache to {new_cache_dir}: {e}") from e
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# Global cache manager instance
|
|
134
|
+
_cache_manager_instance: Optional[CacheManager] = None
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def get_cache_manager(
|
|
138
|
+
cache_dir: Optional[Path] = None, category: Optional[str] = None
|
|
139
|
+
) -> CacheManager:
|
|
140
|
+
"""Get global cache manager instance."""
|
|
141
|
+
global _cache_manager_instance
|
|
142
|
+
if _cache_manager_instance is None:
|
|
143
|
+
_cache_manager_instance = CacheManager(cache_dir=cache_dir, category=category)
|
|
144
|
+
return _cache_manager_instance
|
|
145
|
+
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manifest parsing for environment bundles.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import yaml
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Dict, Any, List, Optional
|
|
8
|
+
from nepher.core import Environment, Scene
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ManifestParser:
|
|
12
|
+
"""Parser for environment manifest files."""
|
|
13
|
+
|
|
14
|
+
@staticmethod
|
|
15
|
+
def parse(manifest_path: Path) -> Environment:
|
|
16
|
+
"""
|
|
17
|
+
Parse manifest YAML file.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
manifest_path: Path to manifest.yaml
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Environment object
|
|
24
|
+
"""
|
|
25
|
+
try:
|
|
26
|
+
with open(manifest_path, "r", encoding="utf-8") as f:
|
|
27
|
+
data = yaml.safe_load(f)
|
|
28
|
+
except FileNotFoundError:
|
|
29
|
+
raise FileNotFoundError(f"Manifest file not found: {manifest_path}")
|
|
30
|
+
except yaml.YAMLError as e:
|
|
31
|
+
raise ValueError(f"Invalid YAML in manifest file {manifest_path}: {e}") from e
|
|
32
|
+
|
|
33
|
+
env_id = data.get("id", manifest_path.parent.name)
|
|
34
|
+
name = data.get("name", env_id)
|
|
35
|
+
description = data.get("description")
|
|
36
|
+
category = data.get("category", "navigation")
|
|
37
|
+
version = data.get("version")
|
|
38
|
+
author = data.get("author")
|
|
39
|
+
benchmark = data.get("benchmark", False)
|
|
40
|
+
metadata = data.get("metadata", {})
|
|
41
|
+
|
|
42
|
+
env_type = "preset" if data.get("preset_scenes") else "usd"
|
|
43
|
+
|
|
44
|
+
scenes = []
|
|
45
|
+
for scene_data in data.get("scenes", []):
|
|
46
|
+
scene = Scene(
|
|
47
|
+
name=scene_data.get("scene_id") or scene_data.get("name", ""),
|
|
48
|
+
description=scene_data.get("description"),
|
|
49
|
+
usd=Path(scene_data["usd"]) if scene_data.get("usd") else None,
|
|
50
|
+
scene=scene_data.get("scene"), # Python scene file for USD scenes
|
|
51
|
+
omap_meta=Path(scene_data["omap_meta"]) if scene_data.get("omap_meta") else None,
|
|
52
|
+
metadata=scene_data.get("metadata"),
|
|
53
|
+
)
|
|
54
|
+
scenes.append(scene)
|
|
55
|
+
|
|
56
|
+
preset_scenes = []
|
|
57
|
+
for preset_data in data.get("preset_scenes", []):
|
|
58
|
+
scene = Scene(
|
|
59
|
+
name=preset_data.get("scene_id") or preset_data.get("name", ""),
|
|
60
|
+
description=preset_data.get("description"),
|
|
61
|
+
preset=preset_data.get("preset"),
|
|
62
|
+
metadata=preset_data.get("metadata"),
|
|
63
|
+
)
|
|
64
|
+
preset_scenes.append(scene)
|
|
65
|
+
|
|
66
|
+
return Environment(
|
|
67
|
+
id=env_id,
|
|
68
|
+
name=name,
|
|
69
|
+
description=description,
|
|
70
|
+
category=category,
|
|
71
|
+
type=env_type,
|
|
72
|
+
version=version,
|
|
73
|
+
author=author,
|
|
74
|
+
scenes=scenes,
|
|
75
|
+
preset_scenes=preset_scenes,
|
|
76
|
+
benchmark=benchmark,
|
|
77
|
+
metadata=metadata,
|
|
78
|
+
cache_path=manifest_path.parent,
|
|
79
|
+
)
|
|
80
|
+
|
nepher/utils/__init__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Utility functions for the Nepher platform."""
|
|
2
|
+
|
|
3
|
+
from nepher.utils.free_zone_finder import FreeZone, Rectangle, find_free_zones
|
|
4
|
+
from nepher.utils.fast_spawn_sampler import FastSpawnSampler, OccupancyMapConfig
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"FreeZone",
|
|
8
|
+
"Rectangle",
|
|
9
|
+
"find_free_zones",
|
|
10
|
+
"FastSpawnSampler",
|
|
11
|
+
"OccupancyMapConfig",
|
|
12
|
+
]
|