pyglove 0.4.5.dev20240319__py3-none-any.whl → 0.4.5.dev202501140808__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyglove/core/__init__.py +54 -20
- pyglove/core/coding/__init__.py +42 -0
- pyglove/core/coding/errors.py +111 -0
- pyglove/core/coding/errors_test.py +98 -0
- pyglove/core/coding/execution.py +309 -0
- pyglove/core/coding/execution_test.py +333 -0
- pyglove/core/{object_utils/codegen.py → coding/function_generation.py} +10 -4
- pyglove/core/{object_utils/codegen_test.py → coding/function_generation_test.py} +5 -7
- pyglove/core/coding/parsing.py +153 -0
- pyglove/core/coding/parsing_test.py +150 -0
- pyglove/core/coding/permissions.py +100 -0
- pyglove/core/coding/permissions_test.py +93 -0
- pyglove/core/geno/base.py +54 -41
- pyglove/core/geno/base_test.py +2 -4
- pyglove/core/geno/categorical.py +37 -28
- pyglove/core/geno/custom.py +19 -16
- pyglove/core/geno/numerical.py +20 -17
- pyglove/core/geno/space.py +4 -5
- pyglove/core/hyper/base.py +6 -6
- pyglove/core/hyper/categorical.py +94 -55
- pyglove/core/hyper/custom.py +7 -7
- pyglove/core/hyper/custom_test.py +9 -10
- pyglove/core/hyper/derived.py +30 -22
- pyglove/core/hyper/derived_test.py +2 -4
- pyglove/core/hyper/dynamic_evaluation.py +5 -6
- pyglove/core/hyper/evolvable.py +57 -46
- pyglove/core/hyper/numerical.py +48 -24
- pyglove/core/hyper/numerical_test.py +9 -9
- pyglove/core/hyper/object_template.py +58 -46
- pyglove/core/io/__init__.py +1 -0
- pyglove/core/io/file_system.py +17 -7
- pyglove/core/io/file_system_test.py +2 -0
- pyglove/core/io/sequence.py +299 -0
- pyglove/core/io/sequence_test.py +124 -0
- pyglove/core/logging_test.py +0 -2
- pyglove/core/patching/object_factory.py +4 -4
- pyglove/core/patching/pattern_based.py +4 -4
- pyglove/core/patching/rule_based.py +17 -5
- pyglove/core/patching/rule_based_test.py +27 -4
- pyglove/core/symbolic/__init__.py +2 -7
- pyglove/core/symbolic/base.py +320 -183
- pyglove/core/symbolic/base_test.py +123 -19
- pyglove/core/symbolic/boilerplate.py +7 -13
- pyglove/core/symbolic/boilerplate_test.py +25 -23
- pyglove/core/symbolic/class_wrapper.py +48 -45
- pyglove/core/symbolic/class_wrapper_test.py +2 -2
- pyglove/core/symbolic/compounding.py +9 -15
- pyglove/core/symbolic/compounding_test.py +2 -4
- pyglove/core/symbolic/dict.py +154 -110
- pyglove/core/symbolic/dict_test.py +238 -130
- pyglove/core/symbolic/diff.py +199 -10
- pyglove/core/symbolic/diff_test.py +226 -0
- pyglove/core/symbolic/flags.py +1 -1
- pyglove/core/symbolic/functor.py +29 -26
- pyglove/core/symbolic/functor_test.py +102 -50
- pyglove/core/symbolic/inferred.py +2 -2
- pyglove/core/symbolic/list.py +81 -50
- pyglove/core/symbolic/list_test.py +119 -97
- pyglove/core/symbolic/object.py +225 -113
- pyglove/core/symbolic/object_test.py +320 -108
- pyglove/core/symbolic/origin.py +17 -14
- pyglove/core/symbolic/origin_test.py +4 -2
- pyglove/core/symbolic/pure_symbolic.py +4 -3
- pyglove/core/symbolic/ref.py +108 -21
- pyglove/core/symbolic/ref_test.py +93 -0
- pyglove/core/symbolic/symbolize_test.py +10 -2
- pyglove/core/tuning/local_backend.py +2 -2
- pyglove/core/tuning/protocols.py +3 -3
- pyglove/core/tuning/sample_test.py +3 -3
- pyglove/core/typing/__init__.py +14 -5
- pyglove/core/typing/annotation_conversion.py +43 -27
- pyglove/core/typing/annotation_conversion_test.py +23 -0
- pyglove/core/typing/callable_ext.py +241 -3
- pyglove/core/typing/callable_ext_test.py +255 -0
- pyglove/core/typing/callable_signature.py +510 -66
- pyglove/core/typing/callable_signature_test.py +619 -99
- pyglove/core/typing/class_schema.py +229 -154
- pyglove/core/typing/class_schema_test.py +149 -95
- pyglove/core/typing/custom_typing.py +5 -4
- pyglove/core/typing/inspect.py +63 -0
- pyglove/core/typing/inspect_test.py +39 -0
- pyglove/core/typing/key_specs.py +10 -11
- pyglove/core/typing/key_specs_test.py +7 -4
- pyglove/core/typing/type_conversion.py +4 -5
- pyglove/core/typing/type_conversion_test.py +12 -12
- pyglove/core/typing/typed_missing.py +6 -7
- pyglove/core/typing/typed_missing_test.py +7 -8
- pyglove/core/typing/value_specs.py +604 -362
- pyglove/core/typing/value_specs_test.py +328 -90
- pyglove/core/utils/__init__.py +164 -0
- pyglove/core/{object_utils → utils}/common_traits.py +3 -67
- pyglove/core/utils/common_traits_test.py +36 -0
- pyglove/core/{object_utils → utils}/docstr_utils.py +23 -0
- pyglove/core/{object_utils → utils}/docstr_utils_test.py +36 -4
- pyglove/core/{object_utils → utils}/error_utils.py +78 -9
- pyglove/core/{object_utils → utils}/error_utils_test.py +61 -5
- pyglove/core/utils/formatting.py +464 -0
- pyglove/core/utils/formatting_test.py +453 -0
- pyglove/core/{object_utils → utils}/hierarchical.py +23 -25
- pyglove/core/{object_utils → utils}/hierarchical_test.py +3 -5
- pyglove/core/{object_utils → utils}/json_conversion.py +177 -52
- pyglove/core/{object_utils → utils}/json_conversion_test.py +97 -16
- pyglove/core/{object_utils → utils}/missing.py +3 -3
- pyglove/core/{object_utils → utils}/missing_test.py +2 -4
- pyglove/core/utils/text_color.py +128 -0
- pyglove/core/utils/text_color_test.py +94 -0
- pyglove/core/{object_utils → utils}/thread_local_test.py +1 -3
- pyglove/core/utils/timing.py +236 -0
- pyglove/core/utils/timing_test.py +154 -0
- pyglove/core/{object_utils → utils}/value_location.py +275 -6
- pyglove/core/utils/value_location_test.py +707 -0
- pyglove/core/views/__init__.py +32 -0
- pyglove/core/views/base.py +804 -0
- pyglove/core/views/base_test.py +580 -0
- pyglove/core/views/html/__init__.py +27 -0
- pyglove/core/views/html/base.py +547 -0
- pyglove/core/views/html/base_test.py +830 -0
- pyglove/core/views/html/controls/__init__.py +35 -0
- pyglove/core/views/html/controls/base.py +275 -0
- pyglove/core/views/html/controls/label.py +207 -0
- pyglove/core/views/html/controls/label_test.py +157 -0
- pyglove/core/views/html/controls/progress_bar.py +183 -0
- pyglove/core/views/html/controls/progress_bar_test.py +97 -0
- pyglove/core/views/html/controls/tab.py +320 -0
- pyglove/core/views/html/controls/tab_test.py +87 -0
- pyglove/core/views/html/controls/tooltip.py +99 -0
- pyglove/core/views/html/controls/tooltip_test.py +99 -0
- pyglove/core/views/html/tree_view.py +1517 -0
- pyglove/core/views/html/tree_view_test.py +1461 -0
- {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501140808.dist-info}/METADATA +18 -4
- pyglove-0.4.5.dev202501140808.dist-info/RECORD +214 -0
- {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501140808.dist-info}/WHEEL +1 -1
- pyglove/core/object_utils/__init__.py +0 -154
- pyglove/core/object_utils/common_traits_test.py +0 -82
- pyglove/core/object_utils/formatting.py +0 -234
- pyglove/core/object_utils/formatting_test.py +0 -223
- pyglove/core/object_utils/value_location_test.py +0 -385
- pyglove/core/symbolic/schema_utils.py +0 -327
- pyglove/core/symbolic/schema_utils_test.py +0 -57
- pyglove/core/typing/class_schema_utils.py +0 -202
- pyglove/core/typing/class_schema_utils_test.py +0 -194
- pyglove-0.4.5.dev20240319.dist-info/RECORD +0 -185
- /pyglove/core/{object_utils → utils}/thread_local.py +0 -0
- {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501140808.dist-info}/LICENSE +0 -0
- {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501140808.dist-info}/top_level.txt +0 -0
@@ -16,29 +16,120 @@
|
|
16
16
|
import dataclasses
|
17
17
|
import enum
|
18
18
|
import inspect
|
19
|
+
import sys
|
20
|
+
import types
|
19
21
|
import typing
|
20
|
-
from typing import Any, Callable, Dict, List, Optional
|
22
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
21
23
|
|
22
|
-
from pyglove.core import
|
24
|
+
from pyglove.core import coding
|
25
|
+
from pyglove.core import utils
|
23
26
|
from pyglove.core.typing import class_schema
|
27
|
+
from pyglove.core.typing import key_specs as ks
|
24
28
|
|
25
29
|
|
26
30
|
@dataclasses.dataclass
|
27
31
|
class Argument:
|
28
32
|
"""Definition for a callable argument."""
|
33
|
+
|
34
|
+
class Kind(enum.Enum):
|
35
|
+
"""Arugment kind."""
|
36
|
+
POSITIONAL_OR_KEYWORD = 1
|
37
|
+
VAR_POSITIONAL = 2
|
38
|
+
KEYWORD_ONLY = 3
|
39
|
+
VAR_KEYWORD = 4
|
40
|
+
|
41
|
+
@classmethod
|
42
|
+
def from_parameter(cls, parameter: inspect.Parameter) -> 'Argument.Kind':
|
43
|
+
"""Returns Argument.Kind from inspect.Parameter."""
|
44
|
+
if parameter.kind == inspect.Parameter.POSITIONAL_ONLY:
|
45
|
+
return Argument.Kind.POSITIONAL_OR_KEYWORD
|
46
|
+
elif parameter.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
|
47
|
+
return Argument.Kind.POSITIONAL_OR_KEYWORD
|
48
|
+
elif parameter.kind == inspect.Parameter.VAR_POSITIONAL:
|
49
|
+
return Argument.Kind.VAR_POSITIONAL
|
50
|
+
elif parameter.kind == inspect.Parameter.KEYWORD_ONLY:
|
51
|
+
return Argument.Kind.KEYWORD_ONLY
|
52
|
+
else:
|
53
|
+
assert parameter.kind == inspect.Parameter.VAR_KEYWORD, parameter.kind
|
54
|
+
return Argument.Kind.VAR_KEYWORD
|
55
|
+
|
29
56
|
name: str
|
57
|
+
kind: Kind
|
30
58
|
value_spec: class_schema.ValueSpec
|
59
|
+
description: Optional[str] = None
|
60
|
+
|
61
|
+
def __post_init__(self):
|
62
|
+
if self.kind == Argument.Kind.VAR_POSITIONAL:
|
63
|
+
if not isinstance(self.value_spec, class_schema.ValueSpec.ListType):
|
64
|
+
raise TypeError(
|
65
|
+
f'Variable positional argument {self.name!r} should have a value '
|
66
|
+
f'of `pg.typing.List` type. Encountered: {self.value_spec!r}.'
|
67
|
+
)
|
68
|
+
self.value_spec.set_default([])
|
69
|
+
|
70
|
+
if (self.kind == Argument.Kind.VAR_KEYWORD
|
71
|
+
and not isinstance(self.value_spec, class_schema.ValueSpec.DictType)):
|
72
|
+
raise TypeError(
|
73
|
+
f'Variable keyword argument {self.name!r} should have a value of '
|
74
|
+
f'`pg.typing.Dict` type. Encountered: {self.value_spec!r}.'
|
75
|
+
)
|
31
76
|
|
32
77
|
@classmethod
|
33
78
|
def from_annotation(
|
34
79
|
cls,
|
35
80
|
name: str,
|
81
|
+
kind: Kind,
|
36
82
|
annotation: Any = inspect.Parameter.empty,
|
37
|
-
auto_typing: bool = False
|
83
|
+
auto_typing: bool = False,
|
84
|
+
parent_module: Optional[types.ModuleType] = None) -> 'Argument':
|
38
85
|
"""Creates an argument from annotation."""
|
39
|
-
return
|
40
|
-
name,
|
41
|
-
|
86
|
+
return cls(
|
87
|
+
name,
|
88
|
+
kind,
|
89
|
+
class_schema.ValueSpec.from_annotation(
|
90
|
+
annotation, auto_typing=auto_typing, parent_module=parent_module
|
91
|
+
)
|
92
|
+
)
|
93
|
+
|
94
|
+
@classmethod
|
95
|
+
def from_parameter(
|
96
|
+
cls,
|
97
|
+
param: inspect.Parameter,
|
98
|
+
description: Optional[str] = None,
|
99
|
+
auto_typing: bool = True,
|
100
|
+
parent_module: Optional[types.ModuleType] = None,
|
101
|
+
) -> 'Argument':
|
102
|
+
"""Creates an argument from inspect.Parameter."""
|
103
|
+
value_spec = class_schema.ValueSpec.from_annotation(
|
104
|
+
param.annotation, auto_typing=auto_typing, parent_module=parent_module
|
105
|
+
)
|
106
|
+
if param.default != inspect.Parameter.empty:
|
107
|
+
value_spec.set_default(param.default)
|
108
|
+
|
109
|
+
# pytype: disable=wrong-arg-count
|
110
|
+
# pytype: disable=not-instantiable
|
111
|
+
if param.kind == inspect.Parameter.VAR_POSITIONAL:
|
112
|
+
value_spec = class_schema.ValueSpec.ListType(value_spec, default=[])
|
113
|
+
elif param.kind == inspect.Parameter.VAR_KEYWORD:
|
114
|
+
value_spec = class_schema.ValueSpec.DictType(value_spec)
|
115
|
+
# pytype: enable=wrong-arg-count
|
116
|
+
# pytype: enable=not-instantiable
|
117
|
+
return cls(
|
118
|
+
param.name,
|
119
|
+
Argument.Kind.from_parameter(param),
|
120
|
+
value_spec,
|
121
|
+
description=description
|
122
|
+
)
|
123
|
+
|
124
|
+
def to_field(self) -> class_schema.Field:
|
125
|
+
"""Converts current argument to a pg.typing.Field object."""
|
126
|
+
if self.kind == Argument.Kind.VAR_KEYWORD:
|
127
|
+
key = ks.StrKey()
|
128
|
+
value = self.value_spec.schema.dynamic_field.value # pytype: disable=attribute-error
|
129
|
+
else:
|
130
|
+
key = ks.ConstStrKey(self.name)
|
131
|
+
value = self.value_spec
|
132
|
+
return class_schema.Field(key, value, description=self.description)
|
42
133
|
|
43
134
|
|
44
135
|
class CallableType(enum.Enum):
|
@@ -50,7 +141,7 @@ class CallableType(enum.Enum):
|
|
50
141
|
METHOD = 2
|
51
142
|
|
52
143
|
|
53
|
-
class Signature(
|
144
|
+
class Signature(utils.Formattable):
|
54
145
|
"""PY3 function signature."""
|
55
146
|
|
56
147
|
def __init__(self,
|
@@ -62,7 +153,8 @@ class Signature(object_utils.Formattable):
|
|
62
153
|
varargs: Optional[Argument] = None,
|
63
154
|
varkw: Optional[Argument] = None,
|
64
155
|
return_value: Optional[class_schema.ValueSpec] = None,
|
65
|
-
qualname: Optional[str] = None
|
156
|
+
qualname: Optional[str] = None,
|
157
|
+
description: Optional[str] = None):
|
66
158
|
"""Constructor.
|
67
159
|
|
68
160
|
Args:
|
@@ -77,6 +169,7 @@ class Signature(object_utils.Formattable):
|
|
77
169
|
name for `**kwargs`.
|
78
170
|
return_value: Optional value spec for return value.
|
79
171
|
qualname: Optional qualified name.
|
172
|
+
description: Optional description of the signature.
|
80
173
|
"""
|
81
174
|
args = args or []
|
82
175
|
self.callable_type = callable_type
|
@@ -88,9 +181,10 @@ class Signature(object_utils.Formattable):
|
|
88
181
|
self.varkw = varkw
|
89
182
|
self.return_value = return_value
|
90
183
|
self.qualname = qualname or name
|
184
|
+
self.description = description
|
91
185
|
|
92
186
|
@property
|
93
|
-
def named_args(self):
|
187
|
+
def named_args(self) -> List[Argument]:
|
94
188
|
"""Returns all named arguments according to their declaration order."""
|
95
189
|
return self.args + self.kwonlyargs
|
96
190
|
|
@@ -114,7 +208,7 @@ class Signature(object_utils.Formattable):
|
|
114
208
|
if arg.name == name:
|
115
209
|
return arg.value_spec
|
116
210
|
if self.varkw is not None:
|
117
|
-
return self.varkw.value_spec
|
211
|
+
return self.varkw.value_spec.schema.dynamic_field.value # pytype: disable=attribute-error
|
118
212
|
return None
|
119
213
|
|
120
214
|
@property
|
@@ -155,24 +249,206 @@ class Signature(object_utils.Formattable):
|
|
155
249
|
self.varargs == other.varargs and self.varkw == other.varkw and
|
156
250
|
self.return_value == other.return_value)
|
157
251
|
|
158
|
-
def format(
|
252
|
+
def format(
|
253
|
+
self,
|
254
|
+
compact: bool = False,
|
255
|
+
verbose: bool = True,
|
256
|
+
root_indent: int = 0,
|
257
|
+
**kwargs,
|
258
|
+
) -> str:
|
159
259
|
"""Format current object."""
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
260
|
+
return utils.kvlist_str(
|
261
|
+
[
|
262
|
+
('', self.id, ''),
|
263
|
+
('args', self.args, []),
|
264
|
+
('kwonlyargs', self.kwonlyargs, []),
|
265
|
+
('returns', self.return_value, None),
|
266
|
+
('varargs', self.varargs, None),
|
267
|
+
('varkw', self.varkw, None),
|
268
|
+
('description', self.description, None),
|
269
|
+
],
|
270
|
+
label=self.__class__.__name__,
|
271
|
+
compact=compact,
|
272
|
+
verbose=verbose,
|
273
|
+
root_indent=root_indent,
|
274
|
+
**kwargs,
|
275
|
+
)
|
276
|
+
|
277
|
+
def annotate(
|
278
|
+
self,
|
279
|
+
args: Union[
|
280
|
+
Dict[class_schema.FieldKeyDef, class_schema.FieldValueDef],
|
281
|
+
List[class_schema.FieldDef],
|
282
|
+
None,
|
283
|
+
] = None,
|
284
|
+
return_value: Union[class_schema.ValueSpec, Any, None] = None,
|
285
|
+
) -> 'Signature':
|
286
|
+
"""Annotate arguments with extra typing."""
|
287
|
+
if return_value is not None:
|
288
|
+
return_value = class_schema.ValueSpec.from_annotation(
|
289
|
+
return_value, auto_typing=True
|
290
|
+
)
|
291
|
+
if utils.MISSING_VALUE != return_value.default:
|
292
|
+
raise ValueError('return value spec should not have default value.')
|
293
|
+
self.return_value = return_value
|
294
|
+
|
295
|
+
if not args:
|
296
|
+
return self
|
297
|
+
|
298
|
+
schema = class_schema.create_schema(args, allow_nonconst_keys=True) # pylint: disable=redefined-outer-name
|
299
|
+
|
300
|
+
arg_fields: Dict[str, class_schema.Field] = dict()
|
301
|
+
varargs_field = None
|
302
|
+
kwarg_field = None
|
303
|
+
existing_names = set(self.arg_names)
|
304
|
+
extra_arg_names = []
|
305
|
+
|
306
|
+
for arg_name, field in schema.fields.items():
|
307
|
+
if isinstance(arg_name, ks.StrKey):
|
308
|
+
if kwarg_field is not None:
|
309
|
+
raise KeyError(
|
310
|
+
f'{self.id}: multiple StrKey found in override args.'
|
311
|
+
)
|
312
|
+
kwarg_field = field
|
313
|
+
else:
|
314
|
+
assert isinstance(arg_name, (str, ks.ConstStrKey))
|
315
|
+
if self.varargs and self.varargs.name == arg_name:
|
316
|
+
varargs_field = field
|
317
|
+
|
318
|
+
elif self.varkw and self.varkw.name == arg_name:
|
319
|
+
if kwarg_field is not None:
|
320
|
+
raise KeyError(
|
321
|
+
f'{self.id}: multiple StrKey found in '
|
322
|
+
f'symbolic arguments declaration.')
|
323
|
+
kwarg_field = field
|
324
|
+
elif arg_name not in existing_names:
|
325
|
+
if self.has_varkw:
|
326
|
+
extra_arg_names.append(arg_name)
|
327
|
+
else:
|
328
|
+
raise KeyError(
|
329
|
+
f'{self.id}: found extra symbolic argument {arg_name.text!r}.')
|
330
|
+
arg_fields[arg_name.text] = field
|
331
|
+
|
332
|
+
def update_arg(arg: Argument, field: class_schema.Field):
|
333
|
+
"""Updates an argument with override field."""
|
334
|
+
if arg.value_spec.has_default and (
|
335
|
+
not field.value.has_default
|
336
|
+
# Loose the default as user may mark it as noneable.
|
337
|
+
or field.value.default is None
|
338
|
+
):
|
339
|
+
field.value.set_default(
|
340
|
+
arg.value_spec.default, root_path=utils.KeyPath(arg.name)
|
341
|
+
)
|
342
|
+
if arg.value_spec.default != field.value.default:
|
343
|
+
if field.value.is_noneable and not arg.value_spec.has_default:
|
344
|
+
# Special handling noneable which always comes with a default.
|
345
|
+
field.value.set_default(utils.MISSING_VALUE)
|
346
|
+
elif not (
|
347
|
+
# Special handling Dict type which always has default.
|
348
|
+
isinstance(field.value, class_schema.ValueSpec.DictType)
|
349
|
+
and not arg.value_spec.has_default
|
350
|
+
):
|
351
|
+
raise ValueError(
|
352
|
+
f'The annotated default value ({field.default_value}) of '
|
353
|
+
f'symbolic argument {arg.name!r} is not equal to the default '
|
354
|
+
f'value ({arg.value_spec.default}) specified from the function '
|
355
|
+
'signature.'
|
356
|
+
)
|
357
|
+
arg.value_spec = field.value
|
358
|
+
if field.description:
|
359
|
+
arg.description = field.description
|
360
|
+
|
361
|
+
# Named arguments.
|
362
|
+
for arg in self.named_args:
|
363
|
+
field = arg_fields.get(arg.name)
|
364
|
+
if field is not None:
|
365
|
+
update_arg(arg, field)
|
366
|
+
|
367
|
+
# Add extra arguments.
|
368
|
+
for arg_name in extra_arg_names:
|
369
|
+
field = arg_fields.get(arg_name)
|
370
|
+
assert field is not None
|
371
|
+
self.kwonlyargs.append(
|
372
|
+
Argument(
|
373
|
+
arg_name.text,
|
374
|
+
Argument.Kind.KEYWORD_ONLY,
|
375
|
+
field.value,
|
376
|
+
field.description
|
377
|
+
)
|
378
|
+
)
|
379
|
+
|
380
|
+
# Update varargs.
|
381
|
+
if varargs_field is not None:
|
382
|
+
assert self.varargs is not None
|
383
|
+
if not isinstance(varargs_field.value, class_schema.ValueSpec.ListType):
|
384
|
+
raise ValueError(
|
385
|
+
f'Variable positional argument {self.varargs.name!r} should have a '
|
386
|
+
'value of `pg.typing.List` type. '
|
387
|
+
f'Encountered: {varargs_field.value!r}.'
|
388
|
+
)
|
389
|
+
update_arg(self.varargs, varargs_field)
|
390
|
+
|
391
|
+
# Update kwarg.
|
392
|
+
if kwarg_field is not None:
|
393
|
+
assert self.varkw is not None
|
394
|
+
value_spec = class_schema.ValueSpec.DictType(kwarg_field.value)
|
395
|
+
self.varkw.value_spec = value_spec
|
396
|
+
if kwarg_field.description:
|
397
|
+
self.varkw.description = kwarg_field.description
|
398
|
+
|
399
|
+
return self
|
400
|
+
|
401
|
+
def fields(
|
402
|
+
self,
|
403
|
+
remove_self: bool = True,
|
404
|
+
include_return: bool = False,
|
405
|
+
) -> List[class_schema.Field]:
|
406
|
+
"""Returns the fields of this signature."""
|
407
|
+
fields = [
|
408
|
+
arg.to_field() for i, arg in enumerate(self.args)
|
409
|
+
if not remove_self or i > 0 or arg.name != 'self'
|
410
|
+
]
|
411
|
+
if self.varargs:
|
412
|
+
fields.append(self.varargs.to_field())
|
413
|
+
fields.extend([arg.to_field() for arg in self.kwonlyargs])
|
414
|
+
if self.varkw:
|
415
|
+
fields.append(self.varkw.to_field())
|
416
|
+
if include_return and self.return_value:
|
417
|
+
fields.append(
|
418
|
+
class_schema.Field('return', self.return_value, 'Return value.')
|
419
|
+
)
|
420
|
+
return fields
|
421
|
+
|
422
|
+
def to_schema(
|
423
|
+
self,
|
424
|
+
remove_self: bool = True,
|
425
|
+
include_return: bool = False,
|
426
|
+
) -> class_schema.Schema:
|
427
|
+
"""Returns the schema of this signature."""
|
428
|
+
init_arg_list = [arg.name for arg in self.args]
|
429
|
+
if init_arg_list and init_arg_list[0] == 'self':
|
430
|
+
init_arg_list.pop(0)
|
431
|
+
|
432
|
+
if self.varargs:
|
433
|
+
init_arg_list.append(f'*{self.varargs.name}')
|
434
|
+
|
435
|
+
return class_schema.Schema(
|
436
|
+
self.fields(remove_self=remove_self, include_return=include_return),
|
437
|
+
name=f'{self.module_name}.{self.qualname}',
|
438
|
+
description=self.description,
|
439
|
+
allow_nonconst_keys=True,
|
440
|
+
metadata=dict(
|
441
|
+
init_arg_list=init_arg_list,
|
442
|
+
varargs_name=self.varargs.name if self.varargs else None,
|
443
|
+
varkw_name=self.varkw.name if self.varkw else None,
|
444
|
+
returns=self.return_value,
|
445
|
+
),
|
170
446
|
)
|
171
447
|
|
172
448
|
@classmethod
|
173
449
|
def from_schema(
|
174
450
|
cls,
|
175
|
-
schema: class_schema.Schema,
|
451
|
+
schema: class_schema.Schema, # pylint: disable=redefined-outer-name
|
176
452
|
module_name: str,
|
177
453
|
name: str,
|
178
454
|
qualname: Optional[str] = None,
|
@@ -205,20 +481,24 @@ class Signature(object_utils.Formattable):
|
|
205
481
|
|
206
482
|
args = []
|
207
483
|
if is_method:
|
208
|
-
args.append(
|
484
|
+
args.append(
|
485
|
+
Argument.from_annotation('self', Argument.Kind.POSITIONAL_OR_KEYWORD)
|
486
|
+
)
|
209
487
|
|
210
488
|
# Prepare positional arguments.
|
211
|
-
args.extend([
|
489
|
+
args.extend([
|
490
|
+
Argument(n, Argument.Kind.POSITIONAL_OR_KEYWORD, get_arg_spec(n))
|
491
|
+
for n in arg_names
|
492
|
+
])
|
212
493
|
|
213
494
|
# Prepare varargs.
|
214
495
|
varargs = None
|
215
496
|
if vararg_name:
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
varargs = Argument(vararg_name, vararg_spec.element.value) # pytype: disable=attribute-error
|
497
|
+
varargs = Argument(
|
498
|
+
vararg_name,
|
499
|
+
Argument.Kind.VAR_POSITIONAL,
|
500
|
+
get_arg_spec(vararg_name)
|
501
|
+
) # pytype: disable=attribute-error
|
222
502
|
|
223
503
|
# Prepare keyword-only arguments.
|
224
504
|
existing_names = set(arg_names)
|
@@ -228,58 +508,154 @@ class Signature(object_utils.Formattable):
|
|
228
508
|
kwonlyargs = []
|
229
509
|
varkw = None
|
230
510
|
for key, field in schema.fields.items():
|
231
|
-
if key not in existing_names:
|
511
|
+
if key not in existing_names and not field.frozen:
|
232
512
|
if key.is_const:
|
233
|
-
kwonlyargs.append(
|
513
|
+
kwonlyargs.append(
|
514
|
+
Argument(str(key), Argument.Kind.KEYWORD_ONLY, field.value)
|
515
|
+
)
|
234
516
|
else:
|
517
|
+
# pytype: disable=not-instantiable
|
518
|
+
# pytype: disable=wrong-arg-count
|
235
519
|
varkw = Argument(
|
236
520
|
schema.metadata.get('varkw_name', None) or 'kwargs',
|
237
|
-
|
521
|
+
Argument.Kind.VAR_KEYWORD,
|
522
|
+
class_schema.ValueSpec.DictType(field.value)
|
523
|
+
)
|
524
|
+
# pytype: enable=wrong-arg-count
|
525
|
+
# pytype: enable=not-instantiable
|
238
526
|
|
239
527
|
return Signature(
|
240
528
|
callable_type=CallableType.FUNCTION,
|
241
529
|
name=name,
|
242
530
|
module_name=module_name,
|
243
531
|
qualname=qualname,
|
532
|
+
description=schema.description,
|
244
533
|
args=args,
|
245
534
|
kwonlyargs=kwonlyargs,
|
246
535
|
varargs=varargs,
|
247
536
|
varkw=varkw,
|
248
|
-
return_value=schema.metadata.get('returns', None)
|
537
|
+
return_value=schema.metadata.get('returns', None)
|
538
|
+
)
|
249
539
|
|
250
540
|
@classmethod
|
251
541
|
def from_callable(
|
252
542
|
cls,
|
253
543
|
callable_object: Callable[..., Any],
|
254
|
-
auto_typing: bool = False
|
544
|
+
auto_typing: bool = False,
|
545
|
+
auto_doc: bool = False,
|
546
|
+
) -> 'Signature':
|
255
547
|
"""Creates Signature from a callable object."""
|
256
548
|
callable_object = typing.cast(object, callable_object)
|
257
549
|
if not callable(callable_object):
|
258
550
|
raise TypeError(f'{callable_object!r} is not callable.')
|
259
551
|
|
260
|
-
if isinstance(callable_object,
|
552
|
+
if isinstance(callable_object, utils.Functor):
|
261
553
|
assert callable_object.__signature__ is not None
|
262
554
|
return callable_object.__signature__
|
263
555
|
|
264
556
|
func = callable_object
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
557
|
+
docstr = None
|
558
|
+
if inspect.isclass(func):
|
559
|
+
callable_type = CallableType.METHOD
|
560
|
+
try:
|
561
|
+
sig = inspect.signature(func)
|
562
|
+
except ValueError:
|
563
|
+
sig = inspect.signature(func.__init__)
|
564
|
+
|
565
|
+
if auto_doc:
|
566
|
+
description = None
|
567
|
+
args_doc = {}
|
568
|
+
if func.__doc__:
|
569
|
+
cls_doc = utils.DocStr.parse(func.__doc__)
|
570
|
+
description = cls_doc.short_description
|
571
|
+
args_doc.update(cls_doc.args)
|
572
|
+
|
573
|
+
if func.__init__.__doc__:
|
574
|
+
init_doc = utils.DocStr.parse(func.__init__.__doc__)
|
575
|
+
args_doc.update(init_doc.args)
|
576
|
+
docstr = utils.DocStr(
|
577
|
+
utils.DocStrStyle.GOOGLE,
|
578
|
+
short_description=description,
|
579
|
+
long_description=None,
|
580
|
+
examples=[],
|
581
|
+
args=args_doc,
|
582
|
+
returns=None,
|
583
|
+
raises=[],
|
584
|
+
blank_after_short_description=True,
|
585
|
+
)
|
586
|
+
else:
|
587
|
+
if not inspect.isroutine(func):
|
588
|
+
if not inspect.isroutine(callable_object.__call__):
|
589
|
+
raise TypeError(f'{callable_object!r}.__call__ is not a method.')
|
590
|
+
func = callable_object.__call__
|
591
|
+
callable_type = (
|
592
|
+
CallableType.METHOD if inspect.ismethod(func)
|
593
|
+
else CallableType.FUNCTION
|
594
|
+
)
|
595
|
+
if auto_doc:
|
596
|
+
docstr = utils.docstr(func)
|
597
|
+
sig = inspect.signature(func)
|
598
|
+
|
599
|
+
module_name = getattr(func, '__module__', None)
|
600
|
+
return cls.from_signature(
|
601
|
+
sig=sig,
|
602
|
+
name=func.__name__,
|
603
|
+
qualname=func.__qualname__,
|
604
|
+
callable_type=callable_type,
|
605
|
+
module_name=module_name or 'wrapper',
|
606
|
+
auto_typing=auto_typing,
|
607
|
+
docstr=docstr,
|
608
|
+
parent_module=sys.modules[module_name] if module_name else None
|
609
|
+
)
|
269
610
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
611
|
+
@classmethod
|
612
|
+
def from_signature(
|
613
|
+
cls,
|
614
|
+
sig: inspect.Signature,
|
615
|
+
name: str,
|
616
|
+
callable_type: CallableType,
|
617
|
+
module_name: Optional[str] = None,
|
618
|
+
qualname: Optional[str] = None,
|
619
|
+
auto_typing: bool = False,
|
620
|
+
docstr: Union[str, utils.DocStr, None] = None,
|
621
|
+
parent_module: Optional[types.ModuleType] = None,
|
622
|
+
) -> 'Signature':
|
623
|
+
"""Returns PyGlove signature from Python signature.
|
624
|
+
|
625
|
+
Args:
|
626
|
+
sig: Python signature.
|
627
|
+
name: Name of the entity (class name or function/method name).
|
628
|
+
callable_type: the type of this callable.
|
629
|
+
module_name: Module name of the entity.
|
630
|
+
qualname: (Optional) qualified name of the entity.
|
631
|
+
auto_typing: If True, automatically convert argument annotations
|
632
|
+
to PyGlove ValueSpec objects. Otherwise use pg.typing.Any()
|
633
|
+
with annotations.
|
634
|
+
docstr: (Optional) DocStr for this entity.
|
635
|
+
parent_module: (Optional) Parent module from where the signature is
|
636
|
+
derived. This is useful to infer classes with forward declarations.
|
276
637
|
|
277
|
-
|
638
|
+
Returns:
|
639
|
+
A PyGlove Signature object.
|
640
|
+
"""
|
278
641
|
args = []
|
279
642
|
kwonly_args = []
|
280
643
|
varargs = None
|
281
644
|
varkw = None
|
282
645
|
|
646
|
+
if isinstance(docstr, str):
|
647
|
+
docstr = utils.DocStr.parse(docstr)
|
648
|
+
|
649
|
+
def make_arg_spec(param: inspect.Parameter) -> Argument:
|
650
|
+
"""Makes argument spec from inspect.Parameter."""
|
651
|
+
docstr_arg = docstr.parameter(param) if docstr else None
|
652
|
+
return Argument.from_parameter(
|
653
|
+
param,
|
654
|
+
description=docstr_arg.description if docstr_arg else None,
|
655
|
+
auto_typing=auto_typing,
|
656
|
+
parent_module=parent_module,
|
657
|
+
)
|
658
|
+
|
283
659
|
for param in sig.parameters.values():
|
284
660
|
arg_spec = make_arg_spec(param)
|
285
661
|
if (param.kind == inspect.Parameter.POSITIONAL_ONLY
|
@@ -296,20 +672,23 @@ class Signature(object_utils.Formattable):
|
|
296
672
|
return_value = None
|
297
673
|
if sig.return_annotation is not inspect.Parameter.empty:
|
298
674
|
return_value = class_schema.ValueSpec.from_annotation(
|
299
|
-
sig.return_annotation,
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
else:
|
304
|
-
callable_type = CallableType.FUNCTION
|
675
|
+
sig.return_annotation,
|
676
|
+
auto_typing=auto_typing,
|
677
|
+
parent_module=parent_module
|
678
|
+
)
|
305
679
|
|
306
|
-
return
|
680
|
+
return cls(
|
307
681
|
callable_type=callable_type,
|
308
|
-
name=
|
309
|
-
module_name=
|
310
|
-
qualname=
|
311
|
-
|
312
|
-
|
682
|
+
name=name,
|
683
|
+
module_name=module_name,
|
684
|
+
qualname=qualname,
|
685
|
+
description=docstr.short_description if docstr else None,
|
686
|
+
args=args,
|
687
|
+
kwonlyargs=kwonly_args,
|
688
|
+
varargs=varargs,
|
689
|
+
varkw=varkw,
|
690
|
+
return_value=return_value,
|
691
|
+
)
|
313
692
|
|
314
693
|
def make_function(
|
315
694
|
self,
|
@@ -329,7 +708,7 @@ class Signature(object_utils.Formattable):
|
|
329
708
|
force_missing_as_default: bool = False,
|
330
709
|
arg_prefix: str = ''):
|
331
710
|
s = [f'{arg_prefix}{arg_name}']
|
332
|
-
if arg_spec.annotation !=
|
711
|
+
if arg_spec.annotation != utils.MISSING_VALUE:
|
333
712
|
s.append(f': _annotation_{arg_name}')
|
334
713
|
exec_locals[f'_annotation_{arg_name}'] = arg_spec.annotation
|
335
714
|
if not arg_prefix and (force_missing_as_default or arg_spec.has_default):
|
@@ -346,7 +725,15 @@ class Signature(object_utils.Formattable):
|
|
346
725
|
|
347
726
|
# Build variable positional arguments.
|
348
727
|
if self.varargs:
|
349
|
-
|
728
|
+
assert isinstance(
|
729
|
+
self.varargs.value_spec,
|
730
|
+
class_schema.ValueSpec.ListType
|
731
|
+
), self.varargs
|
732
|
+
_append_arg(
|
733
|
+
self.varargs.name,
|
734
|
+
getattr(self.varargs.value_spec, 'element'),
|
735
|
+
arg_prefix='*',
|
736
|
+
)
|
350
737
|
elif self.kwonlyargs:
|
351
738
|
args.append('*')
|
352
739
|
|
@@ -356,23 +743,80 @@ class Signature(object_utils.Formattable):
|
|
356
743
|
|
357
744
|
# Build variable keyword arguments.
|
358
745
|
if self.varkw:
|
359
|
-
|
746
|
+
assert isinstance(
|
747
|
+
self.varkw.value_spec,
|
748
|
+
class_schema.ValueSpec.DictType
|
749
|
+
), self.varkw
|
750
|
+
_append_arg(
|
751
|
+
self.varkw.name,
|
752
|
+
self.varkw.value_spec.schema.dynamic_field.value, # pytype: disable=attribute-error
|
753
|
+
arg_prefix='**'
|
754
|
+
)
|
360
755
|
|
361
756
|
# Generate function.
|
362
|
-
fn =
|
757
|
+
fn = coding.make_function(
|
363
758
|
self.name,
|
364
759
|
args=args,
|
365
760
|
body=body,
|
366
761
|
exec_globals=exec_globals,
|
367
762
|
exec_locals=exec_locals,
|
368
763
|
return_type=getattr(
|
369
|
-
self.return_value, 'annotation',
|
764
|
+
self.return_value, 'annotation', coding.NO_TYPE_ANNOTATION
|
765
|
+
),
|
766
|
+
)
|
370
767
|
fn.__module__ = self.module_name
|
371
768
|
fn.__name__ = self.name
|
372
769
|
fn.__qualname__ = self.qualname
|
373
770
|
return fn
|
374
771
|
|
375
772
|
|
376
|
-
def
|
773
|
+
def signature(
|
774
|
+
func: Callable[..., Any],
|
775
|
+
auto_typing: bool = True,
|
776
|
+
auto_doc: bool = True,
|
777
|
+
) -> Signature: # pylint:disable=g-bare-generic
|
377
778
|
"""Gets signature from a python callable."""
|
378
|
-
return Signature.from_callable(func, auto_typing)
|
779
|
+
return Signature.from_callable(func, auto_typing, auto_doc)
|
780
|
+
|
781
|
+
|
782
|
+
def schema(
|
783
|
+
cls_or_fn: Callable[..., Any],
|
784
|
+
args: Union[
|
785
|
+
List[Union[class_schema.Field, class_schema.FieldDef]],
|
786
|
+
Dict[class_schema.FieldKeyDef, class_schema.FieldValueDef],
|
787
|
+
None
|
788
|
+
] = None,
|
789
|
+
returns: Optional[class_schema.ValueSpec] = None,
|
790
|
+
*,
|
791
|
+
auto_typing: bool = True,
|
792
|
+
auto_doc: bool = True,
|
793
|
+
remove_self: bool = True,
|
794
|
+
include_return: bool = False,
|
795
|
+
) -> class_schema.Schema:
|
796
|
+
"""Returns the schema from the signature of a class or a function.
|
797
|
+
|
798
|
+
Args:
|
799
|
+
cls_or_fn: A class or a function.
|
800
|
+
args: (Optional) additional annotations for arguments.
|
801
|
+
returns: (Optional) additional annotation for return value.
|
802
|
+
auto_typing: If True, enable type inference from annotations.
|
803
|
+
auto_doc: If True, extract schema/field description form docstrs.
|
804
|
+
remove_self: If True, remove the first `self` argument if it appears in the
|
805
|
+
signature.
|
806
|
+
include_return: If True, include the return value spec in the schema with
|
807
|
+
key 'return_value'.
|
808
|
+
|
809
|
+
Returns:
|
810
|
+
A pg.typing.Schema object.
|
811
|
+
"""
|
812
|
+
s = getattr(cls_or_fn, '__schema__', None)
|
813
|
+
if isinstance(s, class_schema.Schema):
|
814
|
+
return s
|
815
|
+
return signature(
|
816
|
+
cls_or_fn, auto_typing=auto_typing, auto_doc=auto_doc
|
817
|
+
).annotate(
|
818
|
+
args, return_value=returns
|
819
|
+
).to_schema(
|
820
|
+
remove_self=remove_self,
|
821
|
+
include_return=include_return,
|
822
|
+
)
|