Match prim path expressions as whole-path regular expressions - #6841
Draft
ooctipus wants to merge 1 commit into
Draft
Match prim path expressions as whole-path regular expressions#6841ooctipus wants to merge 1 commit into
ooctipus wants to merge 1 commit into
Conversation
ooctipus
force-pushed
the
octi/prim-path-real-regex-matcher
branch
15 times, most recently
from
August 2, 2026 00:45
48d4f06 to
18795e8
Compare
find_matching_prims matched each '/'-separated token against prim names
at that depth, while find_first_matching_prim compiled the same argument
as one regex over the full path. Both take a parameter named
prim_path_regex and document the same contract, so an expression such as
'/World/Robot/.*link2' returned nothing from the first and a depth-2 prim
from the second.
Match the whole path in both, and have find_first_matching_prim delegate.
Whole-path matching does not imply traversing the whole stage: the
expression's longest literal prefix gives the traversal root, and when no
wildcard can span a '/', the separator count bounds the descent. On a
93k-prim stage a per-environment query visits about 1k prims.
Two changes follow, because '.*' had been carrying structure rather than
meaning what regex says it means.
make_clone_plan derived an asset's destination template by substituting
'.*' for '{}' in the configured prim path. str.replace is positionally
blind, so a second wildcard below the environment slot produced a
template with two slots that raised IndexError when formatted; the
environment root was hardcoded besides, so a non-default namespace
excluded every asset from the plan. Take the slot from the environment
template instead, which CloneCfg now carries directly: a template always
yields a regex, whereas recovering a template from a regex requires
guessing which part of the text is the wildcard.
With '.*' free to mean what regex says, the environment namespace spells
its slot '[^/]+' so it cannot match across a '/' and select a prim nested
deeper under an environment. path.match accepts a character class in the
clone slot for that, since the text '[^/]+' contains a '/' and so cannot
match the one-segment alternative.
ooctipus
force-pushed
the
octi/prim-path-real-regex-matcher
branch
from
August 2, 2026 05:48
18795e8 to
66b4930
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
find_matching_primsmatches each/-separated token against prim names at that depth.find_first_matching_primcompiles the same argument as one regex over the full path. Both take a parameter namedprim_path_regexand document the identical contract:On one stage, one expression:
This makes both match the whole path as a plain Python regular expression, and has
find_first_matching_primdelegate tofind_matching_prims. Two further changes follow, because.*had been carrying structure rather than meaning what regex says.Whole-path matching does not mean traversing the whole stage
The expression bounds the walk: its longest literal prefix gives the traversal root, and when no wildcard can span a
/, the separator count is an exact depth bound. On a synthetic 512-env stage (93,186 prims):env_[^/]+/Robotenv_[^/]+/Robot/link[0-9]+env_[^/]+/Robot/.*Cost scales with what was asked for: unbounded only when the expression itself is unbounded.
The clone slot comes from the template, not from the text
make_clone_planderived an asset's destination template withprim_path.replace(".*", "{}").str.replaceis positionally blind, so a second wildcard below the environment slot produces two slots:Latent today only because an unrelated
hasattr(cfg, "spawn")filter excludes the sensor cfgs that would reach it. The environment root was hardcoded in the same function, so a non-default namespace silently excluded every asset from the plan.CloneCfgnow carriesclone_templateand the plan splits the cfg path at the known environment depth. A template always yields a regex; recovering a template from a regex requires guessing which part of the text is the wildcard.CloneCfg.clone_regexis removed — its value isclone_template.format("[^/]+"), so the two can no longer disagree.The environment slot is segment-safe
With
.*free to mean what regex says, the namespace spells its slot[^/]+:path.matchaccepts a character class in the clone slot for this, since the literal text[^/]+contains a/and so cannot match the one-segment alternative. Both spellings still resolve, so no existing caller changes.Breaking change
Expressions relying on a trailing token meaning "direct children only" should spell it
[^/]+. Classifying every production path literal:Seven of the eight reach the namespace through
{ENV_REGEX_NS}and are covered by the slot change; they are contact-sensor body patterns ({ENV_REGEX_NS}/Robot/foot_.*) whose targets are leaf prims.CloneCfg.clone_regexis removed rather than deprecated — worth a maintainer's call whether that needs a shim.Note green tests are weak evidence here: the trailing-wildcard class changes meaning unconditionally, and the mid-pattern class only on stage shapes the suite does not construct.
Not in this change
resolve_matching_prims_from_sourcestill has a legacy no-clone-plan branch that infers an environment boundary heuristically and returns a different shape than the plan branch. 23 ad-hoc multi-instance test scenes (/World/Env_.*/Anymal) depend on that inference, so replacing it is a separate decision. Until it goes, the ~17expr.replace(".*", "*")calls that build physics-view globs must stay — they are no-ops whenever a clone plan exists, but the legacy branch can still emit.*.Type of change
Checklist
pre-commitchecks with./isaaclab.sh --formatCONTRIBUTORS.mdor my name already exists there