Skip to content

Resolve a registry name to its own class, never a subclass that inherits it - #213

Merged
yichao-liang merged 1 commit into
masterfrom
env-name-registered-class
Oct 9, 2026
Merged

yichao-liang merged 1 commit into
masterfrom
env-name-registered-class

Conversation

@yichao-liang

Copy link
Copy Markdown
Collaborator

Why

A local CI replay of the from-assets belief branch failed one test in shard 3, a test that passes on its own and touches nothing the branch changed:

tests/agent_sdk/test_preflight_audit.py::test_shadow_tools_pair_outcomes_without_changing_execution
  _make_approach -> get_gt_options("pybullet_boil") -> env.action_space
  pybullet.error: Not connected to physics server.

create_new_env, create_approach and the other name registries built the first concrete subclass whose get_name() matched, in the order of utils.get_all_subclasses.
That function returns a set, so its order follows class hashes and changes from process to process.
Every shard imports the whole suite at collection, and several test modules subclass a real class without renaming it.
Importing every test module gives 12 such names:

  • envs: pybullet_boil (two _ExposedBoilEnvs), pybullet_coffee, pybullet_fan, pybullet_grow, pybullet_blocks, pybullet_cover, tools, pybullet_balloons_residual_model;
  • approaches: nsrt_rl, gnn_metacontroller (test mocks);
  • STRIPS learners: backchaining (a test mock);
  • approaches: dummy, claimed by two unrelated test classes.

So in some processes create_new_env("pybullet_boil", do_cache=False) built test_skill_factories_integration._ExposedBoilEnv, which writes itself into the env cache on construction.
In the failing test, the first pass cached a test env that the run then disposed.
The second pass built the real class, which does not touch the cache, so get_or_create_env returned the disposed env.

What changes

  • utils.get_registered_subclass(base, name) resolves a name to the most general concrete subclass that claims it.
    It raises ValueError when unrelated classes claim the same name, and returns None for an unknown name.
  • Every registry that scanned get_all_subclasses by name uses it:
    • envs, approaches and approach wrappers;
    • explorers, perceivers and execution monitors;
    • bridge policies, competence models, STRIPS learners and refinement estimators;
    • the env-class lookups in the human-control approach and Domino's options.
      Each registry keeps its own error for an unknown name.
  • RealSceneGeometryMixin's docstring no longer gives shadowing pybullet_domino as its reason to stay out of BaseEnv, since an intermediate class can no longer shadow it.

The package itself has no shared names, so no production lookup changes.

Evidence

A probe imports test_skill_factories_integration the way a shard's collection does, then calls create_new_env("pybullet_boil", do_cache=False) in 8 fresh processes.
On master, 5 of the 8 built _ExposedBoilEnv and wrote it into the env cache.
On this branch all 8 build PyBulletBoilEnv and leave the cache alone.

Tests

  • New: test_get_registered_subclass covers an inherited name with the subclass listed first, an abstract named class with a concrete child, an unknown name and two unrelated claimants. test_env_creation_ignores_subclasses_that_inherit_the_name lists an instrumented CoverEnv subclass first and checks that create_new_env("cover") still builds CoverEnv. Both fail on master.
  • Local CI replay of this commit: static checks (yapf, isort, docformatter, mypy, pylint) and the 8 shards.
  • CI on this branch (run 37809757592): the 8 unit-test shards, coverage and the formatters pass. static-type-checking fails only with the two tenacity 9.2.1 errors that Keep mypy passing on tenacity 9.2's typed retry decorator #212 fixes; this branch needs a rebase after Keep mypy passing on tenacity 9.2's typed retry decorator #212 merges.

🤖 Generated with Claude Code

…its it

create_new_env, create_approach and the other name registries built the
first concrete subclass whose get_name() matched, in the order of
get_all_subclasses, which is a set: its order follows class hashes and
differs from process to process. Every test shard imports the whole
suite at collection, and test modules subclass real envs, approaches and
STRIPS learners without renaming them (_ExposedBoilEnv in
test_skill_factories_integration, _MockNSRTReinforcementLearningApproach,
...), so in some shard processes create_new_env("pybullet_boil",
do_cache=False) built the test's _ExposedBoilEnv, which registers itself
in the env cache on construction. When a later call in the same test
built the real class instead, get_or_create_env returned the disposed
test env, and test_preflight_audit's second pass failed with "Not
connected to physics server".

utils.get_registered_subclass resolves a name to the most general
concrete class that claims it and raises when unrelated classes claim
one name. Every registry that scanned get_all_subclasses by name uses it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant