Skip to content

fix: handle missing training config - #376

Open
njzjz-bot wants to merge 1 commit into
deepmodeling:masterfrom
njzjz-bot:fix/issue-351-none-train-config
Open

fix: handle missing training config#376
njzjz-bot wants to merge 1 commit into
deepmodeling:masterfrom
njzjz-bot:fix/issue-351-none-train-config

Conversation

@njzjz-bot

Copy link
Copy Markdown

Summary

  • use the local empty-config fallback for backend and command defaults
  • cover RunDPTrain.execute() with config=None

Tests

  • PYTHONPATH=tests python -m unittest -v tests.op.test_run_dp_train.TestRunDPTrain.test_exec_v2_none_config
  • git diff --check

Closes #351

Coding agent: Codex
Codex version: codex-cli 0.149.0
Model: gpt-5.6-sol
Reasoning effort: xhigh

@dosubot dosubot Bot added size:XS This PR changes 0-9 lines, ignoring generated files. bug Something isn't working labels Aug 23, 2026
@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 59 minutes.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e2902f14-aeda-458d-b01e-16c316a8f7ef

📥 Commits

Reviewing files that changed from the base of the PR and between 6b01f29 and 67e3943.

📒 Files selected for processing (2)
  • dpgen2/op/run_dp_train.py
  • tests/op/test_run_dp_train.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Aug 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.43%. Comparing base (6b01f29) to head (67e3943).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #376   +/-   ##
=======================================
  Coverage   84.43%   84.43%           
=======================================
  Files         104      104           
  Lines        6110     6110           
=======================================
  Hits         5159     5159           
  Misses        951      951           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@wanghan-iapcm wanghan-iapcm left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix is correct as far as it goes, and I verified the new test is not vacuous: reverting the two lines gives AttributeError: 'NoneType' object has no attribute 'get' on test_exec_v2_none_config, with 26/26 green at c94879e. But I think this should be fixed one line higher up, because the current shape leaves a second, related bug in place on the exact lines being touched.

Please hoist normalize_config above these reads

impl is read from the raw config seven lines before RunDPTrain.normalize_config() runs. The dargs Argument("impl", ...) declares alias=["backend"], and dargs only resolves aliases inside normalize_value(). So:

{'backend': 'pytorch'}  ->  .get('impl', 'tensorflow')  ==  'tensorflow'
{'backend': 'pytorch'}  ->  normalize_config(...)['impl'] ==  'pytorch'

A caller who writes the documented alias gets a silent TensorFlow run: no --pt, TF init-frozen-model flags, frozen_model.pb. Nothing raises.

Normalizing first makes config=None, {}, and the backend alias all take one path, and lets the four .get(key, literal) reads become plain lookups — which also removes the hand-duplicated copies of "tensorflow" and "dp" that currently shadow the schema defaults and can silently drift from them:

# Normalize first so config=None, an empty dict, and aliases such as
# "backend" for "impl" all follow the same default path.
config = RunDPTrain.normalize_config(
    ip["config"] if ip["config"] is not None else {}
)
impl = config["impl"]
dp_command = config["command"].split()
assert impl in ["tensorflow", "pytorch"]
if impl == "pytorch":
    dp_command.append("--pt")
finetune_args = config["finetune_args"]
train_args = config["train_args"]

I applied exactly this on top of your branch and ran it. All 26 tests in tests/op/test_run_dp_train.py pass unchanged, including your new test_exec_v2_none_config, and the alias case is fixed:

config={'backend': 'pytorch'}  -> impl=pytorch     dp_command=['dp', '--pt']
config={'impl': 'pytorch'}     -> impl=pytorch     dp_command=['dp', '--pt']
config={}                      -> impl=tensorflow  dp_command=['dp']
config=None                    -> impl=tensorflow  dp_command=['dp']

There is no ordering constraint preventing this: nothing between the fallback assignment and the normalize_config call consumes config.

For context on why the file drifted into this shape - the fallback line is the original code, from 1ca9aaa (2022-02-05). The two raw reads were added underneath it much later, impl in f2e1d59 (#207, 2024-03-30) and command in 8fb287e (#257, 2024-09-03), each inserted directly below a fallback the author did not use. f2e1d59 added finetune_args = config.get(...) on the local variable in the very same hunk, so the file has been inconsistent with itself since that commit. f2e1d59 also introduced the backend alias, meaning the alias has never worked on this read path.

The backend alias gap is not reachable through the CLI, since normalize_args() resolves the whole input tree before submit.py reads config["train"]["config"]. It bites direct/library callers of the OP - which is precisely the caller this PR's new test is written to represent, so it seems worth closing in the same change rather than leaving a second trap on the same lines.

Also: the added comment is inaccurate

# Read all optional values from the normalized fallback so config=None
# follows the same default path as an empty configuration.

config is not normalized at that point - normalize_config runs afterwards. And it is not "all optional values": training_args() declares 15 optional arguments, of which 4 are read here and the other 11 are read later via bracket access, after normalization. This matters more than wording, because the belief the comment plants - that these reads already went through dargs defaulting and alias resolution - is exactly what makes the backend bug above invisible. Hoisting the call would make the comment true.

One last note, unrelated to the change itself: config=None is not reachable via the CLI either. "config": null under train is rejected at submit (ArgumentTypeError ... requires <dict> but None is not a dict), and omitting the key yields a full default dict. So this is a defensive fix for library callers, which is fine and does bring the file in line with the idiom already used in run_lmp.py, collect_run_caly.py and run_caly_dp_optim.py - just worth stating plainly in case the issue implied a live production crash.

Comment thread dpgen2/op/run_dp_train.py
dp_command = ip["config"].get("command", "dp").split()
# Read all optional values from the normalized fallback so config=None
# follows the same default path as an empty configuration.
impl = config.get("impl", "tensorflow")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This read happens before RunDPTrain.normalize_config() on line 195, and Argument("impl", ...) declares alias=["backend"], which dargs resolves only inside normalize_value(). So {"backend": "pytorch"} yields impl == "tensorflow" here, --pt is never appended, and the run silently uses TensorFlow.

Hoisting normalize_config above this block fixes the None case, the alias, and the duplicated "tensorflow"/"dp" defaults in one move. I ran that variant against your branch: 26/26 tests pass and {"backend": "pytorch"} correctly produces ['dp', '--pt'].

Comment thread dpgen2/op/run_dp_train.py
config = ip["config"] if ip["config"] is not None else {}
impl = ip["config"].get("impl", "tensorflow")
dp_command = ip["config"].get("command", "dp").split()
# Read all optional values from the normalized fallback so config=None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two inaccuracies here. config is not normalized at this point - normalize_config() runs on line 195, after these reads; these are raw .get() calls with hand-written literal defaults. And it is not "all optional values": 4 of the 15 optional arguments declared in training_args() are read here, the other 11 later via bracket access.

Worth correcting because this is the precise misconception behind the alias bug on line 188 - if the values really did come from the normalized config, backend would work.

Read training command defaults from the local fallback configuration so RunDPTrain accepts config=None consistently.

Closes deepmodeling#351

Coding-Agent: Codex
Codex-Version: codex-cli 0.149.1
Model: gpt-5.6-sol
Reasoning-Effort: xhigh
@njzjz-bot
njzjz-bot force-pushed the fix/issue-351-none-train-config branch from c94879e to 67e3943 Compare August 26, 2026 11:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size:XS This PR changes 0-9 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Code scan] Handle None config consistently in RunDPTrain.execute

2 participants