Hi! Found five edge-case crashes in jsonargparse/_typehints.py while running adversarial test generation against the repo via tailtest. All five raise unhandled exceptions on legitimate inputs that should produce a controlled error or a typed result.
1. resolve_forward_ref crashes when __builtins__ is a module (line 777)
__builtins__.copy() # AttributeError when __builtins__ is the module, not the dict
__builtins__ is the builtins module in __main__ and many embedding contexts; in regular modules it's a dict. Both should work.
Fix: import builtins; vars(builtins).copy()
2 & 3. is_ellipsis_tuple() AttributeError on bare tuple / set types (line 1633)
Bare tuple and set (without subscripting) have no __origin__. The function accesses it unconditionally → AttributeError.
Fix: getattr(typehint, "__origin__", None) or use the existing get_typehint_origin() helper.
4 & 5. Dict[int, str] with non-numeric / empty-string keys leaks raw ValueError (line 972)
Casting user-supplied JSON keys to int via int(k) raises bare ValueError("invalid literal for int() with base 10: '...'"). Users see the raw Python error instead of a typed message tied to the offending field.
Fix: wrap in try/except and raise a TypeError/ValueError with the schema context: which key, which type was expected.
Reproduction
import pytest
from typing import Dict
# Bug 1
def test_resolve_forward_ref_when_builtins_is_module():
"""resolve_forward_ref crashes if __builtins__ is the module (e.g. in __main__)."""
import builtins, jsonargparse._typehints as th
# Simulate the __main__ context where __builtins__ is the module
fake_globals = {"__builtins__": builtins}
try:
th.resolve_forward_ref("int", aliases=fake_globals)
except AttributeError as e:
if "module 'builtins' has no attribute 'copy'" in str(e):
pytest.fail(f"BUG: __builtins__ as module crashes: {e}")
# Bugs 2 & 3
def test_is_ellipsis_tuple_bare_tuple():
from jsonargparse._typehints import is_ellipsis_tuple
try:
is_ellipsis_tuple(tuple) # bare tuple type
except AttributeError as e:
pytest.fail(f"BUG: bare tuple crashes: {e}")
def test_is_ellipsis_tuple_bare_set():
from jsonargparse._typehints import is_ellipsis_tuple
try:
is_ellipsis_tuple(set)
except AttributeError as e:
pytest.fail(f"BUG: bare set crashes: {e}")
# Bugs 4 & 5
def test_dict_int_key_non_numeric_string_key():
from jsonargparse._typehints import adapt_typehints
try:
adapt_typehints({"not_a_number": "v"}, Dict[int, str])
except (ValueError, TypeError) as e:
if "invalid literal for int()" in str(e):
pytest.fail(f"BUG: raw int() error leaks: {e}")
def test_dict_int_key_empty_string_key():
from jsonargparse._typehints import adapt_typehints
try:
adapt_typehints({"": "v"}, Dict[int, str])
except (ValueError, TypeError) as e:
if "invalid literal for int()" in str(e):
pytest.fail(f"BUG: raw int() error on empty key: {e}")
Happy to open a PR for any/all of these — most fixes are 1-3 lines. Found via tailtest.
Hi! Found five edge-case crashes in
jsonargparse/_typehints.pywhile running adversarial test generation against the repo via tailtest. All five raise unhandled exceptions on legitimate inputs that should produce a controlled error or a typed result.1.
resolve_forward_refcrashes when__builtins__is a module (line 777)__builtins__is thebuiltinsmodule in__main__and many embedding contexts; in regular modules it's a dict. Both should work.Fix:
import builtins; vars(builtins).copy()2 & 3.
is_ellipsis_tuple()AttributeError on baretuple/settypes (line 1633)Bare
tupleandset(without subscripting) have no__origin__. The function accesses it unconditionally →AttributeError.Fix:
getattr(typehint, "__origin__", None)or use the existingget_typehint_origin()helper.4 & 5.
Dict[int, str]with non-numeric / empty-string keys leaks rawValueError(line 972)Casting user-supplied JSON keys to int via
int(k)raises bareValueError("invalid literal for int() with base 10: '...'"). Users see the raw Python error instead of a typed message tied to the offending field.Fix: wrap in try/except and raise a
TypeError/ValueErrorwith the schema context: which key, which type was expected.Reproduction
Happy to open a PR for any/all of these — most fixes are 1-3 lines. Found via tailtest.