Skip to content

fix: resolve two ValueError bugs in config_loader._get_explicit_cli_args#3

Merged
George930502 merged 5 commits into
QuantumNoLab:mainfrom
thc1006:fix/config-loader-cli-args
Mar 26, 2026
Merged

fix: resolve two ValueError bugs in config_loader._get_explicit_cli_args#3
George930502 merged 5 commits into
QuantumNoLab:mainfrom
thc1006:fix/config-loader-cli-args

Conversation

@thc1006
Copy link
Copy Markdown
Member

@thc1006 thc1006 commented Mar 26, 2026

TLDR

Fixes two bugs in experiments/config_loader.py that crash all experiment scripts on startup.

Closes #1

What changed

_get_explicit_cli_args uses a sentinel-based probe parser to detect which CLI arguments were explicitly provided. Two argparse API misuses caused immediate ValueError on any experiment script invocation:

Bug 1 -- positional arg with duplicate dest

# Before: dest passed both as positional name AND kwarg
probe.add_argument(action.dest, dest=action.dest, ...)
# ValueError: dest supplied twice for positional argument

Fix: only add dest to kwargs for optional arguments (those with option_strings). Positional arguments infer dest from their name.

Bug 2 -- store_true with nargs=0

# Before: store_true has action.nargs=0, which passed the None check
if action.nargs is not None:
    kwargs["nargs"] = action.nargs  # nargs=0 is invalid
# ValueError: nargs for store actions must be != 0

Fix: detect store_true, store_false, store_const, and count actions via isinstance and preserve them with action= kwarg instead of passing nargs=0.

Test plan

  • 13 new tests in tests/test_config_loader.py
  • Full test suite: 219 passed, 0 failed
  • ruff check + ruff format clean

Bug 1: positional arguments passed dest both as first arg and as kwarg,
raising "dest supplied twice for positional argument".

Bug 2: store_true actions have nargs=0, which was unconditionally passed
to add_argument, raising "nargs for store actions must be != 0".

Fix: only add dest to kwargs for optional args; detect store_true,
store_false, store_const, and count actions via isinstance and preserve
them with action= instead of nargs=0.

Closes QuantumNoLab#1

Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 26, 2026 05:01
pyyaml is in the "configs" extra, not "dev". CI installs .[dev,pyscf]
so yaml would be missing, causing a collection error. importorskip
gracefully skips the module when yaml is unavailable.

Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes experiment startup crashes by correcting argparse “probe parser” reconstruction in experiments/config_loader.py, and adds regression tests to ensure explicit-CLI detection and config-merge precedence behave as intended.

Changes:

  • Reworks _get_explicit_cli_args to avoid argparse ValueErrors for positional args (duplicate dest) and store_* flags (nargs=0 misuse).
  • Adds a new tests/test_config_loader.py test suite covering the reported regressions and merge precedence (CLI > YAML > defaults).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
experiments/config_loader.py Fixes argparse probe-parser construction to prevent startup crashes and correctly detect explicit CLI args.
tests/test_config_loader.py Adds regression + precedence tests for _get_explicit_cli_args and load_config.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +173 to +186
kwargs: dict[str, Any] = {"default": sentinel}

# Actions with nargs=0 (store_true, store_false, store_const, count)
# cannot pass nargs as a kwarg — preserve them via action= instead.
if isinstance(action, argparse._StoreTrueAction): # noqa: SLF001
kwargs["action"] = "store_true"
elif isinstance(action, argparse._StoreFalseAction): # noqa: SLF001
kwargs["action"] = "store_false"
elif isinstance(action, argparse._StoreConstAction): # noqa: SLF001
kwargs["action"] = "store_const"
kwargs["const"] = action.const
elif isinstance(action, argparse._CountAction): # noqa: SLF001
kwargs["action"] = "count"
else:
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

action='count' cannot use a non-None sentinel default: argparse increments the existing value, so when the flag is provided it will attempt sentinel + 1 and raise TypeError. This means _get_explicit_cli_args will currently break for any parser that includes a count flag. Consider handling _CountAction separately by using a safe default (e.g. None or 0) and updating the explicit-detection logic accordingly (e.g. treat None/0 as “not provided” for count flags).

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +18
import pytest

# config_loader requires pyyaml, which lives in the "configs" extra
pytest.importorskip("yaml")

Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

This test module imports config_loader, which imports yaml (PyYAML). CI installs deps via pip install -e ".[dev,pyscf]", and pyyaml isn’t included in those extras, so these tests will raise ModuleNotFoundError: yaml and fail. Either add PyYAML to the dev/test dependencies, or guard these tests with an early pytest.importorskip("yaml") (before importing config_loader) if YAML support is intentionally optional.

Copilot uses AI. Check for mistakes.
thc1006 added 3 commits March 26, 2026 13:16
- Add structured logging (debug: explicit args, merged config;
  info: YAML file loaded; debug: empty YAML)
- Add 5 new tests: _load_yaml error paths (FileNotFoundError,
  empty YAML, non-dict YAML, valid YAML), namespace sync
- Total: 18 tests covering all code paths in config_loader.py

Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
action="count" increments default+1, so sentinel (object) would raise
TypeError. No experiment script uses count, so remove the branch and
document the limitation. Addresses Copilot review on PR QuantumNoLab#3.

Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
- Add store_const detected/not-detected tests (covers line 191-193)
- Strengthen defaults test to verify all 3 _DEFAULTS keys
- Total: 20 tests, all code paths in config_loader.py now covered

Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
@George930502 George930502 merged commit a8bb521 into QuantumNoLab:main Mar 26, 2026
4 checks passed
@thc1006 thc1006 deleted the fix/config-loader-cli-args branch March 26, 2026 09:20
thc1006 added a commit to thc1006/qvartools that referenced this pull request Apr 1, 2026
Critical fixes:
- #1/#2: Use persistent prev_coeffs/prev_batch_configs for PT2 scoring
  (was using best_coeffs before it was defined in current iteration)
- QuantumNoLab#5: Eviction checks cumulative_basis size (was checking best_batch_configs)
- QuantumNoLab#7: Convert numpy indices to torch.LongTensor before indexing CUDA tensor
- QuantumNoLab#8: Default energy_weight=0.0, entropy_weight=0.0 (was 0.1/0.05,
  silently changing existing behavior)

Lower priority fixes:
- QuantumNoLab#6: CIPSI docstring mentions build_sparse_hamiltonian requirement
- QuantumNoLab#9: Warn when energy_weight>0 but hamiltonian=None

Not applicable (won't fix):
- QuantumNoLab#3/QuantumNoLab#4: config_integer_hash returns tuple for n_sites>=64; our CAS max
  is 58Q (<64), so int cast is safe

Co-authored-by: leo07010 <leo07010@gmail.com>
thc1006 added a commit to thc1006/qvartools that referenced this pull request Apr 1, 2026
Copilot fixes:
- #1: top_idx on same device as unique_new (CUDA compat)
- #2: use prev_energy (consistent with prev_coeffs eigenvector)
- QuantumNoLab#3: eviction log only when eviction actually occurred
- QuantumNoLab#4: remove dead test code, clarify as smoke test

New:
- 64+Q hash support (remove int() casts, use hash values directly)
- Cr₂-CAS(12,32) @ 64Q and Cr₂-CAS(12,36) @ 72Q registry entries
- Both MOLECULE_REGISTRY and _MOLECULE_INFO_REGISTRY synced (26 total)

Co-authored-by: leo07010 <leo07010@gmail.com>
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.

Bug: config_loader._get_explicit_cli_args fails with positional and store_true arguments

3 participants