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

liran-funaro / classparse / 5169003229

pending completion
5169003229

push

github

liran-funaro
`dataclasses.field()` parameters added to auxiliary methods

46 of 47 new or added lines in 3 files covered. (97.87%)

561 of 562 relevant lines covered (99.82%)

3.98 hits per line

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

97.73
/classparse/__init__.py
1
"""
2
Declarative `ArgumentParser` definition with `dataclass` notation.
3

4
Author: Liran Funaro <liran.funaro@gmail.com>
5

6
Copyright (c) 2023-2023, Liran Funaro.
7
All rights reserved.
8

9
Redistribution and use in source and binary forms, with or without
10
modification, are permitted provided that the following conditions are met:
11
1. Redistributions of source code must retain the above copyright
12
   notice, this list of conditions and the following disclaimer.
13
2. Redistributions in binary form must reproduce the above copyright
14
   notice, this list of conditions and the following disclaimer in the
15
   documentation and/or other materials provided with the distribution.
16
3. Neither the name of the copyright holder nor the
17
   names of its contributors may be used to endorse or promote products
18
   derived from this software without specific prior written permission.
19

20
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
POSSIBILITY OF SUCH DAMAGE.
31
"""
32
import argparse
4✔
33
import dataclasses
4✔
34
import functools
4✔
35
import sys
4✔
36
from typing import Callable, Optional, Sequence, Type, TypeVar, Union, overload
4✔
37

38
from classparse.analyze import NO_ARG, POS_ARG, NAME_OR_FLAG
4✔
39
from classparse.proto import DataclassParser, DataclassParserType
4✔
40
from classparse.transform import DataclassParserMaker, _transform_dataclass_parser
4✔
41

42
__version__ = "0.1.4"
4✔
43

44

45
def _field(**kwargs):
4✔
46
    if "kw_only" in kwargs and sys.version_info.major <= 3 and sys.version_info.minor <= 10:
4✔
47
        del kwargs["kw_only"]
3✔
48
    return dataclasses.field(**kwargs)
4✔
49

50

51
# noinspection PyShadowingBuiltins
52
def arg(
4✔
53
    *name_or_flag,
54
    default=dataclasses.MISSING,
55
    default_factory=dataclasses.MISSING,
56
    init=True,
57
    repr=True,  # pylint: disable=redefined-builtin
58
    hash=None,  # pylint: disable=redefined-builtin
59
    compare=True,
60
    kw_only=dataclasses.MISSING,
61
    **metadata,
62
):
63
    """
64
    Allow adding parameters to a named argument.
65
    See `argparse.add_argument()`.
66
    """
67
    if len(name_or_flag) > 0:
4✔
68
        metadata.update({NAME_OR_FLAG: name_or_flag})
4✔
69

70
    # Non positional fields must have a default value
71
    if default is dataclasses.MISSING and default_factory is dataclasses.MISSING:
4✔
72
        default = None
4✔
73

74
    return _field(
4✔
75
        default=default,
76
        default_factory=default_factory,
77
        init=init,
78
        repr=repr,
79
        hash=hash,
80
        compare=compare,
81
        kw_only=kw_only,
82
        metadata=metadata,
83
    )
84

85

86
# noinspection PyShadowingBuiltins
87
def pos_arg(
4✔
88
    default=dataclasses.MISSING,
89
    *,
90
    default_factory=dataclasses.MISSING,
91
    init=True,
92
    repr=True,  # pylint: disable=redefined-builtin
93
    hash=None,  # pylint: disable=redefined-builtin
94
    compare=True,
95
    kw_only=dataclasses.MISSING,
96
    **metadata,
97
):
98
    """
99
    Allow adding parameters to a positional argument.
100
    See `argparse.add_argument()`.
101
    """
102
    metadata.update({POS_ARG: True})
4✔
103
    return _field(
4✔
104
        default=default,
105
        default_factory=default_factory,
106
        init=init,
107
        repr=repr,
108
        hash=hash,
109
        compare=compare,
110
        kw_only=kw_only,
111
        metadata=metadata,
112
    )
113

114

115
# noinspection PyShadowingBuiltins
116
def no_arg(
4✔
117
    default=dataclasses.MISSING,
118
    *,
119
    default_factory=dataclasses.MISSING,
120
    init=True,
121
    repr=True,  # pylint: disable=redefined-builtin
122
    hash=None,  # pylint: disable=redefined-builtin
123
    compare=True,
124
    kw_only=dataclasses.MISSING,
125
    metadata=None,
126
):
127
    """Set dataclass field as non argparse argument"""
128
    no_arg_meta = {NO_ARG: True}
4✔
129
    if metadata is None:
4✔
130
        metadata = no_arg_meta
4✔
131
    else:
NEW
132
        metadata.update(no_arg_meta)
×
133
    return _field(
4✔
134
        default=default,
135
        default_factory=default_factory,
136
        init=init,
137
        repr=repr,
138
        hash=hash,
139
        compare=compare,
140
        kw_only=kw_only,
141
        metadata=metadata,
142
    )
143

144

145
_T = TypeVar("_T", bound=object)
4✔
146
InstanceOrClass = Union[Type[_T], _T]
4✔
147
DataClass = TypeVar("DataClass", bound=object)
4✔
148

149

150
def make_parser(
4✔
151
    instance_or_cls: InstanceOrClass[DataClass], default_argument_args: Optional[dict] = None, **parser_args
152
) -> argparse.ArgumentParser:
153
    """Make an ArgumentParser from a dataclass"""
154
    return DataclassParserMaker(instance_or_cls, default_argument_args, **parser_args).main_parser
4✔
155

156

157
def parse_to(
4✔
158
    instance_or_cls: InstanceOrClass[DataClass],
159
    args: Optional[Sequence[str]] = None,
160
    default_argument_args: Optional[dict] = None,
161
    **parser_args,
162
) -> DataclassParser[DataClass]:
163
    """Parse arguments to a dataclass"""
164
    parser_maker = DataclassParserMaker(instance_or_cls, default_argument_args=default_argument_args, **parser_args)
4✔
165
    return parser_maker.parse_args(instance_or_cls, args=args)
4✔
166

167

168
@overload
4✔
169
def classparser(
4✔
170
    cls: None = None, *, default_argument_args=None, load_defaults_from_file=False, **parser_args
171
) -> Callable[[Type[DataClass]], DataclassParserType[DataClass]]:
172
    ...  # pragma: no cover
173

174

175
@overload
4✔
176
def classparser(cls: Type[DataClass]) -> DataclassParserType[DataClass]:
4✔
177
    ...  # pragma: no cover
178

179

180
def classparser(cls=None, /, **kwargs):
4✔
181
    """Decorator that adds `DataclassParser` methods to the dataclass"""
182
    if cls is None:
4✔
183
        # The method is called with parentheses: @classparser().
184
        return functools.partial(_transform_dataclass_parser, kwargs=kwargs)
4✔
185

186
    # The method is called without parentheses: @classparser.
187
    return _transform_dataclass_parser(cls, kwargs)
4✔
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