oopsie-data-tools 0.2.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.
- oopsie_data_tools/__init__.py +7 -0
- oopsie_data_tools/annotation_tool/__init__.py +5 -0
- oopsie_data_tools/annotation_tool/annotation_schema.py +266 -0
- oopsie_data_tools/annotation_tool/annotator_server.py +850 -0
- oopsie_data_tools/annotation_tool/episode_recorder.py +648 -0
- oopsie_data_tools/annotation_tool/rollout_annotator.py +333 -0
- oopsie_data_tools/annotation_tool/ui/annotator.html +2240 -0
- oopsie_data_tools/cli.py +894 -0
- oopsie_data_tools/init_wizard.py +329 -0
- oopsie_data_tools/skill/SKILL.md +98 -0
- oopsie_data_tools/skill/reference/conversion.md +257 -0
- oopsie_data_tools/skill/reference/format.md +112 -0
- oopsie_data_tools/skill/reference/robot-profile.md +104 -0
- oopsie_data_tools/skill/reference/setup.md +216 -0
- oopsie_data_tools/skill/reference/troubleshooting.md +97 -0
- oopsie_data_tools/test/__init__.py +1 -0
- oopsie_data_tools/test/conftest.py +174 -0
- oopsie_data_tools/test/fixtures/__init__.py +0 -0
- oopsie_data_tools/test/fixtures/make_invalid.py +633 -0
- oopsie_data_tools/test/fixtures/make_valid.py +406 -0
- oopsie_data_tools/test/test_annotation_completeness.py +154 -0
- oopsie_data_tools/test/test_annotation_schema.py +190 -0
- oopsie_data_tools/test/test_annotator_server.py +240 -0
- oopsie_data_tools/test/test_annotator_server_guards.py +146 -0
- oopsie_data_tools/test/test_bulk_inference_end_to_end.py +112 -0
- oopsie_data_tools/test/test_cli_config.py +111 -0
- oopsie_data_tools/test/test_cli_tools.py +438 -0
- oopsie_data_tools/test/test_contributor_config.py +38 -0
- oopsie_data_tools/test/test_conversion_utils_annotations.py +75 -0
- oopsie_data_tools/test/test_credentials_location.py +106 -0
- oopsie_data_tools/test/test_diversity.py +33 -0
- oopsie_data_tools/test/test_episode_recorder.py +335 -0
- oopsie_data_tools/test/test_episode_video_paths.py +173 -0
- oopsie_data_tools/test/test_hf_upload.py +234 -0
- oopsie_data_tools/test/test_init_wizard.py +193 -0
- oopsie_data_tools/test/test_install_skill.py +120 -0
- oopsie_data_tools/test/test_migrate_taxonomy_v2.py +255 -0
- oopsie_data_tools/test/test_new_profile.py +84 -0
- oopsie_data_tools/test/test_paths.py +137 -0
- oopsie_data_tools/test/test_python38_compat.py +79 -0
- oopsie_data_tools/test/test_record_step_purity.py +127 -0
- oopsie_data_tools/test/test_robot_setup.py +244 -0
- oopsie_data_tools/test/test_rollout_annotator.py +134 -0
- oopsie_data_tools/test/test_rotation_utils.py +126 -0
- oopsie_data_tools/test/test_validate.py +494 -0
- oopsie_data_tools/utils/__init__.py +1 -0
- oopsie_data_tools/utils/claude_skill.py +96 -0
- oopsie_data_tools/utils/contributor_config.py +91 -0
- oopsie_data_tools/utils/conversion_utils.py +220 -0
- oopsie_data_tools/utils/h5.py +60 -0
- oopsie_data_tools/utils/h5_inspect.py +167 -0
- oopsie_data_tools/utils/hf_limits.py +22 -0
- oopsie_data_tools/utils/hf_upload.py +235 -0
- oopsie_data_tools/utils/log.py +35 -0
- oopsie_data_tools/utils/migrate_taxonomy_v2.py +215 -0
- oopsie_data_tools/utils/paths.py +162 -0
- oopsie_data_tools/utils/restructure.py +402 -0
- oopsie_data_tools/utils/robot_profile/__init__.py +1 -0
- oopsie_data_tools/utils/robot_profile/robot_profile.py +240 -0
- oopsie_data_tools/utils/robot_profile/rotation_utils.py +119 -0
- oopsie_data_tools/utils/robot_profile/template.py +108 -0
- oopsie_data_tools/utils/validation/__init__.py +5 -0
- oopsie_data_tools/utils/validation/annotation_completeness.py +67 -0
- oopsie_data_tools/utils/validation/diversity.py +93 -0
- oopsie_data_tools/utils/validation/episode_data.py +54 -0
- oopsie_data_tools/utils/validation/episode_loader.py +201 -0
- oopsie_data_tools/utils/validation/episode_validator.py +315 -0
- oopsie_data_tools/utils/validation/errors.py +17 -0
- oopsie_data_tools/utils/validation/validation_utils.py +88 -0
- oopsie_data_tools-0.2.0.dist-info/METADATA +131 -0
- oopsie_data_tools-0.2.0.dist-info/RECORD +74 -0
- oopsie_data_tools-0.2.0.dist-info/WHEEL +4 -0
- oopsie_data_tools-0.2.0.dist-info/entry_points.txt +2 -0
- oopsie_data_tools-0.2.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""Shared pytest fixtures for oopsie_data_tools tests.
|
|
2
|
+
|
|
3
|
+
Session-scoped fixtures generate HDF5 + MP4 files once per test session into a
|
|
4
|
+
pytest-managed temporary directory and are cleaned up automatically at the end.
|
|
5
|
+
|
|
6
|
+
tmp_path_factory (session-scoped) – shared fixtures used across many tests
|
|
7
|
+
tmp_path (function-scoped, built-in) – per-test isolation
|
|
8
|
+
|
|
9
|
+
File generation delegates to the canonical fixture generators so that the
|
|
10
|
+
validator tests always exercise the exact same files as other test modules.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
import pytest
|
|
18
|
+
|
|
19
|
+
from oopsie_data_tools.test.fixtures.make_invalid import (
|
|
20
|
+
_MAKERS_SIMPLE,
|
|
21
|
+
_MAKERS_WITH_VIDEO,
|
|
22
|
+
_write_video,
|
|
23
|
+
)
|
|
24
|
+
from oopsie_data_tools.test.fixtures.make_valid import (
|
|
25
|
+
make_failure,
|
|
26
|
+
make_legacy_v1,
|
|
27
|
+
make_multi_camera,
|
|
28
|
+
make_no_annotations,
|
|
29
|
+
make_success,
|
|
30
|
+
make_unannotated,
|
|
31
|
+
write_valid_episode, # re-exported for per-test use in test modules
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
__all__ = ["write_valid_episode"]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@pytest.fixture(autouse=True)
|
|
38
|
+
def _isolate_user_environment(tmp_path_factory, monkeypatch):
|
|
39
|
+
"""Keep every test out of the developer's real home, config and working directory.
|
|
40
|
+
|
|
41
|
+
Three confirmed leaks this closes:
|
|
42
|
+
|
|
43
|
+
* ``cli.main`` exports ``$OOPSIE_CONFIG_DIR``. ``monkeypatch.delenv`` records nothing
|
|
44
|
+
when the variable is absent, so there was nothing to undo and the value outlived the
|
|
45
|
+
test that set it, pointing later tests at a deleted directory.
|
|
46
|
+
* ``test_robot_setup`` resolves profiles through the cwd and
|
|
47
|
+
``$OOPSIE_ROBOT_PROFILES_DIR``, so it failed if either was set, or if pytest ran from
|
|
48
|
+
a directory that happened to contain ``robot_profiles/``.
|
|
49
|
+
* ``init_wizard.advise_persisting_config_dir`` appends an export line to ``~/.zshrc``.
|
|
50
|
+
Nothing stopped that from being the developer's real one.
|
|
51
|
+
"""
|
|
52
|
+
home = tmp_path_factory.mktemp("home")
|
|
53
|
+
monkeypatch.setenv("HOME", str(home))
|
|
54
|
+
monkeypatch.setenv("XDG_CONFIG_HOME", str(home / ".config"))
|
|
55
|
+
for var in ("OOPSIE_CONFIG_DIR", "OOPSIE_ROBOT_PROFILES_DIR"):
|
|
56
|
+
# Set before deleting so monkeypatch always has a value to restore, whether or not
|
|
57
|
+
# the variable existed and whatever the test under it does.
|
|
58
|
+
monkeypatch.setenv(var, str(home / "unset"))
|
|
59
|
+
monkeypatch.delenv(var)
|
|
60
|
+
monkeypatch.chdir(tmp_path_factory.mktemp("cwd"))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@pytest.fixture(scope="session", autouse=True)
|
|
64
|
+
def _test_contributor_config():
|
|
65
|
+
"""Supply a test lab_id everywhere the contributor config is read.
|
|
66
|
+
|
|
67
|
+
Lets the suite run on a fresh checkout without a filled-in
|
|
68
|
+
``configs/contributor_config.yaml`` (whose blank lab_id would otherwise fail
|
|
69
|
+
``EpisodeRecorder`` construction), without touching the committed file.
|
|
70
|
+
|
|
71
|
+
Every module that binds the name at import gets patched: ``hf_upload`` and
|
|
72
|
+
``init_wizard`` also do ``from ... import read_contributor_config``, so rebinding two
|
|
73
|
+
hand-picked modules left them reading the developer's real config.
|
|
74
|
+
"""
|
|
75
|
+
from _pytest.monkeypatch import MonkeyPatch
|
|
76
|
+
|
|
77
|
+
import oopsie_data_tools.annotation_tool.episode_recorder as _recorder
|
|
78
|
+
import oopsie_data_tools.init_wizard as _init_wizard
|
|
79
|
+
import oopsie_data_tools.utils.contributor_config as _cc
|
|
80
|
+
import oopsie_data_tools.utils.hf_upload as _hf_upload
|
|
81
|
+
|
|
82
|
+
def _fake(config_path=None) -> tuple[str, str]:
|
|
83
|
+
return ("test_lab", "test_token")
|
|
84
|
+
|
|
85
|
+
patch = MonkeyPatch()
|
|
86
|
+
for module in (_cc, _recorder, _hf_upload, _init_wizard):
|
|
87
|
+
patch.setattr(module, "read_contributor_config", _fake)
|
|
88
|
+
try:
|
|
89
|
+
yield
|
|
90
|
+
finally:
|
|
91
|
+
patch.undo()
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# ---------------------------------------------------------------------------
|
|
95
|
+
# Session-scoped fixtures: valid episodes
|
|
96
|
+
# ---------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@pytest.fixture(scope="session")
|
|
100
|
+
def valid_episode(tmp_path_factory) -> Path:
|
|
101
|
+
"""Fully valid oopsiedata_format_v1 episode, minimally annotated as a success.
|
|
102
|
+
|
|
103
|
+
It carries an annotation despite ``make_unannotated``'s name: validation through the
|
|
104
|
+
CLI always checks annotations, so a general-purpose "valid episode" must have one.
|
|
105
|
+
"""
|
|
106
|
+
d = tmp_path_factory.mktemp("valid")
|
|
107
|
+
make_unannotated(d)
|
|
108
|
+
return d / "episode_unannotated.h5"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@pytest.fixture(scope="session")
|
|
112
|
+
def episode_without_annotations(tmp_path_factory) -> Path:
|
|
113
|
+
"""Structurally valid episode with no ``episode_annotations`` group at all."""
|
|
114
|
+
d = tmp_path_factory.mktemp("no_annotations")
|
|
115
|
+
make_no_annotations(d)
|
|
116
|
+
return d / "episode_no_annotations.h5"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@pytest.fixture(scope="session")
|
|
120
|
+
def valid_success_episode(tmp_path_factory) -> Path:
|
|
121
|
+
"""Valid episode annotated as success."""
|
|
122
|
+
d = tmp_path_factory.mktemp("valid_success")
|
|
123
|
+
make_success(d)
|
|
124
|
+
return d / "episode_success.h5"
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@pytest.fixture(scope="session")
|
|
128
|
+
def valid_failure_episode(tmp_path_factory) -> Path:
|
|
129
|
+
"""Valid episode annotated as failure."""
|
|
130
|
+
d = tmp_path_factory.mktemp("valid_failure")
|
|
131
|
+
make_failure(d)
|
|
132
|
+
return d / "episode_failure.h5"
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@pytest.fixture(scope="session")
|
|
136
|
+
def legacy_v1_episode(tmp_path_factory) -> Path:
|
|
137
|
+
"""Valid episode still carrying the taxonomy v1 annotation schema."""
|
|
138
|
+
d = tmp_path_factory.mktemp("legacy_v1")
|
|
139
|
+
make_legacy_v1(d)
|
|
140
|
+
return d / "episode_legacy_v1.h5"
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@pytest.fixture(scope="session")
|
|
144
|
+
def valid_session_dir(tmp_path_factory) -> Path:
|
|
145
|
+
"""Session directory containing all four valid episode types."""
|
|
146
|
+
d = tmp_path_factory.mktemp("valid_session")
|
|
147
|
+
make_unannotated(d)
|
|
148
|
+
make_success(d)
|
|
149
|
+
make_failure(d)
|
|
150
|
+
make_multi_camera(d)
|
|
151
|
+
return d
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# ---------------------------------------------------------------------------
|
|
155
|
+
# Session-scoped fixture: invalid episodes
|
|
156
|
+
# ---------------------------------------------------------------------------
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@pytest.fixture(scope="session")
|
|
160
|
+
def invalid_fixtures(tmp_path_factory) -> dict[str, Path]:
|
|
161
|
+
"""Dict mapping fixture stem → Path for every invalid HDF5 file.
|
|
162
|
+
|
|
163
|
+
Generated by the same make_invalid module used for manual inspection,
|
|
164
|
+
so tests and the generator stay in sync automatically.
|
|
165
|
+
"""
|
|
166
|
+
d = tmp_path_factory.mktemp("invalid")
|
|
167
|
+
|
|
168
|
+
for maker in _MAKERS_SIMPLE:
|
|
169
|
+
maker(d)
|
|
170
|
+
|
|
171
|
+
for maker in _MAKERS_WITH_VIDEO:
|
|
172
|
+
maker(d, _write_video)
|
|
173
|
+
|
|
174
|
+
return {p.stem: p for p in d.glob("invalid_*.h5")}
|
|
File without changes
|