Skip to content

_typehints.py: 5 unhandled-edge-case crashes (forward refs in __main__, bare tuple/set, Dict[int, str] with bad keys) #904

Description

@pramodavansaber

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpending author responseA question was asked or some work is pending

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions