Match parameter tokens exactly in get_used_parameters - #478
Conversation
_get_used_parameters built the search pattern as
r"\{}\({}\.*\w*\)".format(token, key), where \.* is zero-or-more
literal dots and \w* is zero-or-more word characters. For a parameter
named N that regex also matches $(NP), so a step whose command uses
only $(NP) reports both N and NP as used.
get_used_parameters feeds Study.get_tasks_per_step, which uses the
result to decide whether a step is expanded across parameter
combinations and to build the combination workspace/label. So a
prefix collision (common in HPC specs: N vs NP, X vs X2) can attach
the wrong parameters to a step or, when a command literally contains a
$(N...)-shaped token, expand a step into extra instances.
Require the character after the key to be ")" or ".<suffix>", and
re.escape the key. Exact, $(key.label), and $(key.name) forms still
match. Added a regression test.
Signed-off-by: Arpit Jain <arpitjain099@gmail.com>
|
Well, that is a great catch; am surprised this hasn't blown up on anybody before! |
Just to be clear, are you seeing it miss tests that should be able to run on your machine? There are several markers for the tests that require live schedulers (slurm, flux, ...) which skip things on purpose since they don't make sense if you're not on a machine with a scheduler; if you're seeing others getting skipped let us know. |
jwhite242
left a comment
There was a problem hiding this comment.
Looks pretty good, only a few other minor things needed:
- tick the dev version in the pyproject.toml (sorry, don't have any ci/bot helpers to smooth this one out yet)
- add a changelog fragment as detailed here: https://github.com/llnl/maestrowf/blob/develop/CONTRIBUTING.md
| elif isinstance(item, str): | ||
| for key in self.parameters.keys(): | ||
| _ = r"\{}\({}\.*\w*\)".format(self.token, key) | ||
| _ = r"\{}\({}(\.\w+)?\)".format(self.token, re.escape(key)) |
There was a problem hiding this comment.
Thinking this 're.escape' should probably get added to the variables and such as well (though I won't push for it here), so thanks for pointing that one out
There was a problem hiding this comment.
Makes sense. I'll cover the variables and the other token expansions in the separate PR I mentioned, since they use the same \{}\({}\...\) construction and would trip on the same regex metacharacters. I'll tag you on it.
There was a problem hiding this comment.
Quick follow-up before I opened that PR. I went through the rest of the token handling and it turns out _get_used_parameters (what this PR fixes) is the only place that compiles a parameter name into a regex. The actual substitution of parameters, labels, names, and variables all runs through str.replace() (Combination.substitute in parameters.py, Variable.substitute in variable.py), which is a literal match, so a regex metacharacter in a name has no effect there and there's nothing to escape. The other regexes in the tree (WSREGEX, ALL_COMBOS, the \w+ validators) are static, with the name as the subject rather than part of the pattern.
So I don't think a separate PR is needed after all, and I was wrong earlier to say the same construction showed up in the other expansions. Happy to be corrected if I've missed a spot.
There was a problem hiding this comment.
Sounds good, and thanks for digging into that!
Signed-off-by: Arpit Jain <arpitjain099@gmail.com>
|
Both done in e38ead4. Version ticked to Changelog fragment added at On the One note on verification: I could not run the suite locally, |
Sure, if you want to I'll be happy to review it!
oh, that's interesting; skip the poetry install part maybe (i.e. running |
|
Thanks, that explains the failure. I was running the tests without the editable install, so And yeah, I'll open the separate PR for the other token expansions. It's the same |
_get_used_parametersbuilds its search pattern as:Here
\.*is "zero or more literal dots" and\w*is "zero or more word characters", so after the parameter name it happily consumes more characters. For a parameter namedNthe pattern matches$(NP), so a step whose command uses only$(NP)reports bothNandNPas used:get_used_parametersfeedsStudy.get_tasks_per_step, which uses the result to decide whether a step is expanded across parameter combinations and to build the combination workspace/label names. So a prefix collision (common in HPC specs:N/NP,X/X2,SIZE/SIZE_B) can attach the wrong parameters to a step, and a step whose command literally contains a$(N...)-shaped token can be expanded into extra instances.Fix: require the character after the key to be
)or.<suffix>, andre.escapethe key:$(key),$(key.label), and$(key.name)still match;$(NP)no longer matches keyN.Added
tests/test_used_parameters.py. I verified the fix directly under Python 3.13 ($(NP)-only step yields{"NP"}; the exact/.label/.nameforms still resolve; the old pattern returns{"N","NP"}). Note: the fullpytestsuite does not collect on my machine because the repo'sconftest.pyapplies a mark to a fixture, which pytest 8 rejects, unrelated to this change.