Blame 无法作为单个页面加载。
Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
import codecs
import dataclasses
import inspect
import json
import os
import warnings
from argparse import ArgumentParser, Action
from dataclasses import dataclass, is_dataclass
from enum import Enum
from typing import *
import yaml
from .utils import *
from .type_check import *
from .typing_ import *
__all__ = [
'ConfigValidationError',
'field_checker', 'root_checker', 'config_field', 'ConfigField',
'config_params', 'get_config_params', 'ConfigMeta',
'Config', 'validate_config', 'config_to_dict', 'config_defaults',
'ConfigLoader', 'object_to_config',
'format_config', 'print_config', 'save_config',
]
# general special attributes that is recognized by ``type_info``
# special attributes of a Config class
_FIELDS = '__mltk_config_fields__' # fields
_UNBOUND_CHECKERS = '__mltk_config_unbound_checkers__' # unbound field and root checker params
_PARAMS = '__mltk_config_params__' # config parameters
_PARAMS_CLASS_NAME = '__ConfigParams__' # nested class as config parameters
# special attributes of a Config classmethod
_CHECKER_PARAMS = '__mltk_config_checker_params__'
ConfigValidationError = TypeCheckError
"""Legacy name for the :class:`TypeCheckError`."""
@dataclass
class ObjectFieldCheckerParams(object):
fields: Tuple[str, ...]
method: classmethod
pre: bool
@dataclass
class ObjectRootCheckerParams(object):
method: classmethod
pre: bool
ObjectCheckerParams = Union[ObjectFieldCheckerParams, ObjectRootCheckerParams]
def field_checker(*fields, pre: bool = False):
"""
Decorator to register a class method as a field checker in :class:`Config`.
The checker should be implemented as a class method, with `cls` as its
first argument, and the field value as its second argument. Besides,
it may also accept `values` and `field` as keyword argument, with
`values` receiving all field values (being a dict if ``pre = True``,
or a `Config` instance if ``pre = False``), and `field` receiving the
name of the field being checked.
>>> class MyConfig(Config):
... a: int
... b: int
... c: int
...
... @field_checker('c')
... def _checker(cls, v, values, field):
... if v != values['a'] + values['b']:
... raise ValueError('a + b != c')
... return v
>>> validate_config(MyConfig(a=1, b='2', c=3.0))
MyConfig(a=1, b=2, c=3)
>>> validate_config(MyConfig(a=1, b='2', c=4.0))
Traceback (most recent call last):
...
mltk.type_check.TypeCheckError: caused by:
* ValueError: a + b != c
Args:
*fields: The fields to be checked. "*" represents all fields.
pre: Whether or not this checker should be run before the fields
having been checked against the field definitions? If :obj:`True`,
`values` will be a dict, rather than an instance of `Config`.
Defaults to :obj:`False`.
"""
def wrapper(method):
if not isinstance(method, classmethod):
method = classmethod(method)
if not hasattr(method, _CHECKER_PARAMS):
setattr(method, _CHECKER_PARAMS, [])
getattr(method, _CHECKER_PARAMS).append(
ObjectFieldCheckerParams(fields=fields, method=method, pre=pre))
return method
return wrapper
def root_checker(pre: bool = False):
"""
Decorator to register a class method as a root checker in :class:`Config`.
The checker should be implemented as a class method, with `cls` as its
first argument, and the object values as its second argument.
When ``pre = True``, the values will be a dict; otherwise it will be
an instance of `Config`.
>>> class MyConfig(Config):
... a: int
... b: int
... c: int
...
... @root_checker()
... def _checker(cls, values):
... if values.c != values.a + values.b:
... raise ValueError('a + b != c')
>>> validate_config(MyConfig(a=1, b='2', c=3.0))
MyConfig(a=1, b=2, c=3)
>>> validate_config(MyConfig(a=1, b='2', c=4.0))
Traceback (most recent call last):
...
mltk.type_check.TypeCheckError: caused by:
* ValueError: a + b != c
Args:
pre: Whether or not this checker should be run before the fields
having been checked against the field definitions? If :obj:`True`,
`values` will be a dict, rather than an instance of `Config`.
Defaults to :obj:`False`.
"""
def wrapper(method):
if not isinstance(method, classmethod):
method = classmethod(method)
if not hasattr(method, _CHECKER_PARAMS):
setattr(method, _CHECKER_PARAMS, [])
getattr(method, _CHECKER_PARAMS).append(
ObjectRootCheckerParams(method=method, pre=pre))
return method
return wrapper
def config_field(type: Optional[Type] = None,
default: Any = NOT_SET,
default_factory: Callable[[], Any] = NOT_SET,
description: Optional[str] = None,
choices: Optional[Sequence[Any]] = None,
required: bool = True,
envvar: Optional[str] = None,
ignore_empty_env: bool = True,
# deprecated arguments
nullable: bool = NOT_SET):
"""
Define a :class:`Config` field.
Args:
type: Type of the field. Any type literal that can be recognized
by :func:`mltk.utils.type_info`, e.g., ``Optional[int]``.
If the field type is already specified via type annotation,
then the type specified by this argument will be ignored.
default: The default value of this field.
default_factory: A function ``() -> Any``, which returns the
default value of this field. `default` and `default_factory`
cannot be both specified.
description: Description of this field.
choices: Valid values for this field to take.
required: Whether or not this field is required?
If :obj:`False`, the object will pass type checking even
when this field is not specified a value.
envvar: The name of the environmental variable to read from.
ignore_empty_env: Whether or not empty string from the environmental
variable will be ignored, as if no value has been given?
nullable: DEPRECATED. Whether or not this field is nullable?
Use ``Optional[T]`` as type instead of using this argument.
Returns:
The config field object.
"""
if nullable is not NOT_SET:
warnings.warn('`nullable` argument is deprecated. Use `Optional[T]` '
'as type instead.', DeprecationWarning)
# check the type argument.
if type is None:
# If the type annotation is adopted, it will be later overwritten.
if default is not NOT_SET:
ti = type_info_from_value(default)
elif default_factory is not NOT_SET:
ti = type_info_from_value(default_factory())
else:
ti = AnyTypeInfo()
else: