• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

oir / startle / 22260139436

21 Feb 2026 04:23PM UTC coverage: 98.513% (-0.2%) from 98.687%
22260139436

push

github

web-flow
Refactor to separate recursive inspection from Args construction (#135)

244 of 244 branches covered (100.0%)

Branch coverage included in aggregate %.

1280 of 1303 relevant lines covered (98.23%)

0.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.57
startle/_value_parser.py
1
"""
2
String-to-type conversion functions.
3
"""
4

5
from collections.abc import Callable
1✔
6
from enum import Enum
1✔
7
from inspect import isclass
1✔
8
from pathlib import Path
1✔
9
from typing import Any, Literal, cast, get_args, get_origin
1✔
10

11
from ._typing import strip_optional
1✔
12
from .error import UnsupportedValueTypeError, ValueParsingError
1✔
13

14

15
def _to_str(value: str) -> str:
1✔
16
    return value
1✔
17

18

19
def _to_int(value: str) -> int:
1✔
20
    try:
1✔
21
        return int(value)
1✔
22
    except ValueError as err:
1✔
23
        raise ValueParsingError(value, "integer") from err
1✔
24

25

26
def _to_float(value: str) -> float:
1✔
27
    try:
1✔
28
        return float(value)
1✔
29
    except ValueError as err:
1✔
30
        raise ValueParsingError(value, "float") from err
1✔
31

32

33
def _to_bool(value: str) -> bool:
1✔
34
    if value.lower() in {"true", "t", "yes", "y", "1"}:
1✔
35
        return True
1✔
36
    if value.lower() in {"false", "f", "no", "n", "0"}:
1✔
37
        return False
1✔
38
    raise ValueParsingError(value, "boolean")
1✔
39

40

41
def _to_path(value: str) -> Path:
1✔
42
    return Path(value)  # can this raise?
1✔
43

44

45
def _to_enum(value: str, enum_type: type) -> Enum:
1✔
46
    try:
1✔
47
        # for StringEnum and (str, Enum) types, use enum value
48
        # otherwise use the name of the member
49
        member_type: type = getattr(enum_type, "_member_type_", object)
1✔
50
        if member_type is str or (member_type is object and issubclass(enum_type, str)):
1✔
51
            return cast(Enum, enum_type(value))
1✔
52
        try:
1✔
53
            enum_type_ = cast(type[Enum], enum_type)
1✔
54
            return enum_type_[value.upper().replace("-", "_")]
1✔
55
        except KeyError as err:
1✔
56
            raise ValueParsingError(value, f"enum {enum_type.__name__}") from err
1✔
57
    except ValueError as err:
1✔
58
        raise ValueParsingError(value, f"enum {enum_type.__name__}") from err
1✔
59

60

61
PARSERS: dict[Any, Callable[[str], Any]] = {
1✔
62
    str: _to_str,
63
    Any: _to_str,
64
    int: _to_int,
65
    float: _to_float,
66
    bool: _to_bool,
67
    Path: _to_path,
68
}
69

70

71
def _get_parser(type_: Any) -> Callable[[str], Any] | None:
1✔
72
    """
73
    Get the parser function for a given type.
74
    """
75

76
    # if type is Optional[T], convert to T
77
    type_ = strip_optional(type_)
1✔
78

79
    if get_origin(type_) is Literal:
1✔
80
        type_args = get_args(type_)
1✔
81
        if all(isinstance(arg, str) for arg in type_args):
1✔
82

83
            def parser(value: str) -> str:
1✔
84
                if value in type_args:
1✔
85
                    return value
1✔
86
                raise ValueParsingError(value, f"literal {type_args}")
1✔
87

88
            return parser
1✔
89

90
    # check if type_ is an Enum
91
    if isclass(type_) and issubclass(type_, Enum):
1✔
92
        return lambda value: _to_enum(value, type_)
1✔
93

94
    if fp := PARSERS.get(type_):
1✔
95
        return fp
1✔
96

97
    return None
1✔
98

99

100
def parse(value: str, type_: Any) -> Any:
1✔
101
    """
102
    Parse or convert a string value to a given type.
103
    """
104
    if parser := _get_parser(type_):
1✔
105
        return parser(value)
1✔
106

107
    # otherwise it is unsupported
108
    raise UnsupportedValueTypeError(f"{type_.__module__}.{type_.__qualname__}")
×
109

110

111
def is_parsable(type_: Any) -> bool:
1✔
112
    """
113
    Check if a type is parsable (supported).
114
    """
115
    return _get_parser(type_) is not None
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc