fix(envs): add __getattr__ to _LazyAsyncVectorEnv for attribute proxying#3331
fix(envs): add __getattr__ to _LazyAsyncVectorEnv for attribute proxying#3331reeceomahoney wants to merge 1 commit intohuggingface:mainfrom
Conversation
_LazyAsyncVectorEnv only explicitly delegates reset/step/call/get_attr, so accessing any other attribute (e.g. env.unwrapped.metadata) raises AttributeError. Add a __getattr__ fallback that materializes the underlying AsyncVectorEnv and forwards the lookup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Fixes _LazyAsyncVectorEnv acting as an incomplete proxy by forwarding unknown attribute access (e.g., unwrapped, metadata, spec) to the underlying lazily-materialized gym.vector.AsyncVectorEnv, preventing AttributeError during evaluation with video rendering.
Changes:
- Add
__getattr__to_LazyAsyncVectorEnvto call_ensure()and delegate attribute lookup to the wrapped env.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def __getattr__(self, name: str): | ||
| self._ensure() | ||
| return getattr(self._env, name) |
There was a problem hiding this comment.
Consider adding a small regression test for this proxying behavior (e.g., that accessing unwrapped/metadata on _LazyAsyncVectorEnv materializes the underlying env and forwards the attribute). A lightweight approach is to monkeypatch gym.vector.AsyncVectorEnv with a dummy object so the test doesn’t need to spawn subprocesses.
| def __getattr__(self, name: str): | ||
| self._ensure() | ||
| return getattr(self._env, name) |
There was a problem hiding this comment.
For type-safety and clearer failure modes, it’d be good to narrow the optional type after _ensure() (e.g., assert the underlying _env is not None) and consider annotating __getattr__ to return Any to reflect that it forwards arbitrary attributes.
Type / Scope
Summary / Motivation
_LazyAsyncVectorEnv(introduced in #3274) only explicitly delegatesreset,step,call, andget_attr. Accessing any other attribute on the wrapper — such asenv.unwrapped.metadata["render_fps"]ineval_policy— raisesAttributeError:This is hit whenever eval runs with video rendering enabled (
max_episodes_rendered > 0).Related issues
What changed
__getattr__to_LazyAsyncVectorEnvthat materializes the underlyingAsyncVectorEnvand forwards the attribute lookup. This is the standard proxy pattern and ensures all attributes (e.g.unwrapped,metadata,spec) work transparently.How was this tested
pre-commit run --files src/lerobot/envs/utils.py— all checks pass.lerobot-evalwith video rendering enabled; confirmed it no longer raisesAttributeError.Checklist (required before merge)
pre-commit run -a)pytest)Reviewer notes
src/lerobot/envs/utils.py.__getattr__is only called when normal attribute lookup fails, so the existing explicit methods (reset,step, etc.) are unaffected.