Skip to content

Commit cabcdee

Browse files
authored
Fixes CLI help text for function types (#370)
1 parent 235dd02 commit cabcdee

File tree

2 files changed

+15
-8
lines changed

2 files changed

+15
-8
lines changed

pydantic_settings/sources.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations as _annotations
22

3+
import inspect
34
import json
45
import os
56
import re
@@ -17,7 +18,6 @@
1718
from enum import Enum
1819
from pathlib import Path
1920
from textwrap import dedent
20-
from types import FunctionType
2121
from typing import (
2222
TYPE_CHECKING,
2323
Any,
@@ -1718,8 +1718,9 @@ def _metavar_format_choices(self, args: list[str], obj_qualname: str | None = No
17181718
def _metavar_format_recurse(self, obj: Any) -> str:
17191719
"""Pretty metavar representation of a type. Adapts logic from `pydantic._repr.display_as_type`."""
17201720
obj = _strip_annotated(obj)
1721-
if isinstance(obj, FunctionType):
1722-
return obj.__name__
1721+
if _is_function(obj):
1722+
# If function is locally defined use __name__ instead of __qualname__
1723+
return obj.__name__ if '<locals>' in obj.__qualname__ else obj.__qualname__
17231724
elif obj is ...:
17241725
return '...'
17251726
elif isinstance(obj, Representation):
@@ -1762,13 +1763,13 @@ def _help_format(self, field_name: str, field_info: FieldInfo, model_default: An
17621763
default = f'(default: {self.cli_parse_none_str})'
17631764
if is_model_class(type(model_default)) or is_pydantic_dataclass(type(model_default)):
17641765
default = f'(default: {getattr(model_default, field_name)})'
1765-
elif model_default not in (PydanticUndefined, None) and callable(model_default):
1766+
elif model_default not in (PydanticUndefined, None) and _is_function(model_default):
17661767
default = f'(default factory: {self._metavar_format(model_default)})'
17671768
elif field_info.default not in (PydanticUndefined, None):
17681769
enum_name = _annotation_enum_val_to_name(field_info.annotation, field_info.default)
17691770
default = f'(default: {field_info.default if enum_name is None else enum_name})'
17701771
elif field_info.default_factory is not None:
1771-
default = f'(default: {field_info.default_factory})'
1772+
default = f'(default factory: {self._metavar_format(field_info.default_factory)})'
17721773
_help += f' {default}' if _help else default
17731774
return _help.replace('%', '%%') if issubclass(type(self._root_parser), ArgumentParser) else _help
17741775

@@ -2092,3 +2093,7 @@ def _annotation_enum_name_to_val(annotation: type[Any] | None, name: Any) -> Any
20922093
if name in tuple(val.name for val in type_):
20932094
return type_[name]
20942095
return None
2096+
2097+
2098+
def _is_function(obj: Any) -> bool:
2099+
return inspect.isfunction(obj) or inspect.isbuiltin(obj) or inspect.isroutine(obj) or inspect.ismethod(obj)

tests/test_settings.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import pathlib
66
import re
77
import sys
8+
import time
89
import typing
910
import uuid
1011
from datetime import datetime, timezone
@@ -2553,9 +2554,7 @@ class Cfg(BaseSettings):
25532554
-h, --help show this help message and exit
25542555
--foo str (required)
25552556
--bar int (default: 123)
2556-
--boo int (default: <function
2557-
test_cli_help_differentiation.<locals>.Cfg.<lambda> at
2558-
0xffffffff>)
2557+
--boo int (default factory: <lambda>)
25592558
"""
25602559
)
25612560

@@ -3757,6 +3756,9 @@ class CfgWithSubCommand(BaseSettings):
37573756
(Annotated[SimpleSettings, 'annotation'], 'JSON'),
37583757
(DirectoryPath, 'Path'),
37593758
(FruitsEnum, '{pear,kiwi,lime}'),
3759+
(time.time_ns, 'time_ns'),
3760+
(foobar, 'foobar'),
3761+
(CliDummyParser.add_argument, 'CliDummyParser.add_argument'),
37603762
],
37613763
)
37623764
@pytest.mark.parametrize('hide_none_type', [True, False])

0 commit comments

Comments
 (0)