Repository navigation
Let xmlchange and append_user_nl take an explicit caseroot - #51
Merged
Merged
Conversation
Both find the case only through the global CASEROOT/NINST config vars, so a tool that edits an existing case after creation must first make it the "current" one. Add keyword-only caseroot= (and ninst= for append_user_nl), falling back to the config vars so CaseCreator's calls are unchanged. Run ./xmlchange with an argument list instead of a shell string, and include stderr and the caseroot when it fails.
With an argument list and no shell, a caseroot without ./xmlchange raises FileNotFoundError instead of returning 127. Wrap it so every failure to run xmlchange is a RuntimeError, as it was with the shell.
alperaltuntas
approved these changes
Oct 27, 2026
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.
Why
case_tools.xmlchangeandcase_tools.append_user_nlfind the case only through theCASEROOTandNINSTconfig variables. That suits the GUI, which configures one caseper session, but not code that drives visualCaseGen from a script:
cvarsis process-global, andinitialize()resets it. A script that sets up two cases in one Python process can apply xmlchanges
and user_nl edits only to whichever case was configured last. Tools built on
CaseCreator(CrocoDash is one) keep changing a case after creating it, so they needto say which case they mean.
xmlchangebuilds./xmlchange VAR=valas a string andruns it with
shell=True. A value with a space is split into two arguments, and theshell expands a
$VARreference (usually to an empty string) before CIME sees it.RuntimeErrorsays only"Error running ./xmlchange ...", not what CIME reported.
What
New keyword-only arguments:
xmlchange(var, val, do_exec=True, is_non_local=False, out=None, *, caseroot=None)append_user_nl(model, var_val_pairs, do_exec, comment=None, log_title=True, out=None, *, caseroot=None, ninst=None)Each falls back to its config variable when not given. Every existing call (all of
CaseCreator's are positional) behaves as before.xmlchangeruns without a shell. It runs["./xmlchange", f"{var}={val}"], plus--non-localwhen asked, withcwd=caseroot.shlex.join. It is unchanged for ordinary values andquoted when a value needs it, so the Show Commands output can still be pasted
into a shell.
Errors name the caseroot and include CIME's stderr.
No other function changes.
run_case_setupstill readsCASEROOT.Tests
tests/1_unit/test_case_tools.pyhas one test, which needs no CESM tree:xmlchangescript intmp_pathrecords its arguments;CASEROOT/NINSTconfig variables removed,xmlchange(..., caseroot=tmp_path)runs intmp_pathand receivesCASE_DESC=two wordsas one argument;append_user_nl(..., caseroot=tmp_path, ninst=2)writesuser_nl_mom_0002.tests/1_unit/test_cores_case_creator.pyalready importscase_creator, and with itcase_tools, so this import adds no CESM requirement to 1_unit.stub
./xmlchange.Notes for reviewers
$are no longer interpreted,so a caller that pre-quoted a value for the shell would now pass the quotes through.
CaseCreatorpasses only paths, grid names and integers, so its calls are unaffected.Commas are still
xmlchange's own separator between settings, as before../xmlchangenow raisesFileNotFoundErrorinstead ofRuntimeError.caserootwithoutninststill readsNINSTfrom the config variables, and raises aKeyErrorif it is undefined. Thealternative, assuming one instance, would silently write a
user_nl_<model>file thata multi-instance case ignores.
case_tools.pyis not black-formatted upstream. Only the changed lines are, to keepthe diff small.
Possible follow-ups in visualCaseGen
caseroot=from CaseCreator.CaseCreatormakes all 22 of itsxmlchangeandappend_user_nlcalls through the global CASEROOT. Passing the caseroot explicitlywould keep those writes pointed at the case being created, even if the GUI's
configuration variables change while it runs.
caseroot=, a small non-GUI functioncould apply visualCaseGen's grid, namelist and xml settings to an existing case, or to
several cases in one process.
🤖 Generated with Claude Code