Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ Changed
implementation are accepted. Parameter and return types must still match
exactly, except when the protocol has no annotation or ``Any`` (`#941
<https://github.com/mauvilsa/jsonargparse/pull/941>`__).
- The default print config argument name will remain as ``--print_config`` in
v5.0.0, no longer changing as described in the deprecated section of v4.35.0.


v4.50.0 (2026-07-22)
Expand Down
4 changes: 3 additions & 1 deletion DOCUMENTATION.rst
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,9 @@ Some notes about this support are:
names must be builtins, ``typing`` names or dot import paths.

- ``TypeAliasType`` is supported with values parsed as the aliased type and the
alias shown as the argument type in help.
alias shown as the argument type in help. This includes aliases defined with
the `PEP 695 <https://peps.python.org/pep-0695/>`__ ``type X = ...`` statement
(python 3.12+) and aliases created with ``typing_extensions.TypeAliasType``.


.. _restricted-numbers:
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
:target: https://github.com/mauvilsa/jsonargparse/actions/workflows/tests.yaml
.. image:: https://codecov.io/gh/mauvilsa/jsonargparse/branch/main/graph/badge.svg
:target: https://codecov.io/gh/mauvilsa/jsonargparse
.. image:: https://sonarcloud.io/api/project_badges/measure?project=mauvilsa_jsonargparse&metric=alert_status
.. image:: https://sonarcloud.io/api/project_badges/measure?project=mauvilsa_jsonargparse&metric=alert_status&token=74f3ff0af709f6caa0544dfbcf823c49fb68cb46
:target: https://sonarcloud.io/dashboard?id=mauvilsa_jsonargparse
.. image:: https://badge.fury.io/py/jsonargparse.svg
:target: https://badge.fury.io/py/jsonargparse
Expand Down
22 changes: 11 additions & 11 deletions jsonargparse/_typehints.py
Original file line number Diff line number Diff line change
Expand Up @@ -1195,29 +1195,29 @@ def adapt_typehints(

# Module
elif typehint is ModuleType:
if serialize:
if isinstance(val, ModuleType):
if isinstance(val, ModuleType):
if serialize:
val = val.__name__
elif not isinstance(val, ModuleType):
if not is_importable_module_path(val):
raise_unexpected_value("Expected an import path corresponding to a module", val)
if instantiate_classes:
val = import_module(val)
elif not is_importable_module_path(val):
raise_unexpected_value("Expected an import path corresponding to a module", val)
elif instantiate_classes:
val = import_module(val)

# UnionType and GenericAlias
elif typehint in type_expression_types:
if serialize:
if isinstance(val, typehint):
if isinstance(val, typehint):
if serialize:
val = str(val)
elif not isinstance(val, typehint):
else:
expected = f"Expected a string with a {type_expression_types[typehint]} type expression"
try:
type_expression = str_to_type_expression(val)
except Exception as ex:
raise_unexpected_value(expected, val, ex)
if not isinstance(type_expression, typehint):
raise_unexpected_value(expected, val)
val = type_expression
if not serialize:
val = type_expression

# Union
elif typehint_origin == Union:
Expand Down
2 changes: 1 addition & 1 deletion jsonargparse_tests/test_parameter_resolvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,7 @@ class _ConcreteImpl:
"""Implements _NonRTCheckableProtocol structurally."""

def execute(self) -> None:
pass
pass # pragma: no cover


class _DoesNotImpl:
Expand Down
2 changes: 1 addition & 1 deletion jsonargparse_tests/test_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ def __init__(self, objects: List[ItemBase] = []):

class ItemsDictMain:
def __init__(self, objects: Optional[Dict[str, ItemBase]] = None):
self.objects = objects
self.objects = objects # pragma: no cover


item1_spec = {"class_path": f"{__name__}.ItemSub", "init_args": {"x": 2, "y": "a"}}
Expand Down
37 changes: 37 additions & 0 deletions jsonargparse_tests/test_typehints.py
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,24 @@ def test_module_type_dump_module_object(parser):
assert json_or_yaml_load(parser.dump(cfg)) == {"mod": "json"}


def test_module_type_union_with_callable_dump(parser):
parser.add_argument("--val", type=Union[ModuleType, Callable])
cfg = parser.parse_args(["--val=uuid.uuid4"])
assert json_or_yaml_load(parser.dump(cfg)) == {"val": "uuid.uuid4"}


class WithCallableDefault:
def __init__(self, cb: Callable = uuid.uuid4):
self.cb = cb


def test_module_type_union_with_class_dump(parser):
parser.add_argument("--val", type=Union[ModuleType, WithCallableDefault])
cfg = parser.parse_args([f"--val={__name__}.WithCallableDefault"])
expected = {"class_path": f"{__name__}.WithCallableDefault", "init_args": {"cb": "uuid.uuid4"}}
assert json_or_yaml_load(parser.dump(cfg)) == {"val": expected}


def test_module_type_help(parser):
parser.add_argument("--mod", type=ModuleType, help="Module to use.")
help_str = get_parser_help(parser)
Expand Down Expand Up @@ -995,6 +1013,19 @@ def test_union_type_optional(parser):
assert parser.parse_args(["--type=int | str"]).type == int | str


def test_union_type_dump_type_expression_string(parser):
parser.add_argument("--type", type=UnionType)
cfg = parser.parse_args(["--type=int | str"])
cfg.type = "int | str"
assert json_or_yaml_load(parser.dump(cfg)) == {"type": "int | str"}


def test_union_type_union_with_callable_dump(parser):
parser.add_argument("--val", type=Union[UnionType, Callable])
cfg = parser.parse_args(["--val=uuid.uuid4"])
assert json_or_yaml_load(parser.dump(cfg)) == {"val": "uuid.uuid4"}


def test_union_type_help(parser):
parser.add_argument("--type", type=UnionType, help="Type to use.")
help_str = get_parser_help(parser)
Expand Down Expand Up @@ -1025,6 +1056,12 @@ def test_generic_alias_dump(parser):
assert json_or_yaml_load(parser.dump(cfg)) == {"type": "dict[str, int]"}


def test_generic_alias_union_with_callable_dump(parser):
parser.add_argument("--val", type=Union[GenericAlias, Callable])
cfg = parser.parse_args(["--val=uuid.uuid4"])
assert json_or_yaml_load(parser.dump(cfg)) == {"val": "uuid.uuid4"}


def test_generic_alias_help(parser):
parser.add_argument("--type", type=GenericAlias, help="Type to use.")
help_str = get_parser_help(parser)
Expand Down