pyglove 0.4.5.dev202502020807__py3-none-any.whl → 0.4.5.dev202502040809__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/typing/inspect.py +16 -0
- pyglove/core/typing/inspect_test.py +48 -1
- pyglove/core/typing/type_conversion.py +9 -4
- pyglove/core/typing/type_conversion_test.py +5 -0
- {pyglove-0.4.5.dev202502020807.dist-info → pyglove-0.4.5.dev202502040809.dist-info}/METADATA +1 -1
- {pyglove-0.4.5.dev202502020807.dist-info → pyglove-0.4.5.dev202502040809.dist-info}/RECORD +9 -9
- {pyglove-0.4.5.dev202502020807.dist-info → pyglove-0.4.5.dev202502040809.dist-info}/LICENSE +0 -0
- {pyglove-0.4.5.dev202502020807.dist-info → pyglove-0.4.5.dev202502040809.dist-info}/WHEEL +0 -0
- {pyglove-0.4.5.dev202502020807.dist-info → pyglove-0.4.5.dev202502040809.dist-info}/top_level.txt +0 -0
pyglove/core/typing/inspect.py
CHANGED
@@ -30,6 +30,11 @@ def is_subclass(
|
|
30
30
|
"""An issubclass extension that supports Any and generic types."""
|
31
31
|
|
32
32
|
def _is_subclass(src: Type[Any], target: Type[Any]) -> bool:
|
33
|
+
if is_protocol(target):
|
34
|
+
# NOTE(daiyip): loose runtime check for Protocol.
|
35
|
+
# As runtime protocol check (through decorating the protocol class with
|
36
|
+
# @typing.runtime_checkable) might be unreliable and very slow.
|
37
|
+
return inspect.isclass(src) and src.__module__ != 'builtins'
|
33
38
|
if target is Any:
|
34
39
|
return True
|
35
40
|
elif src is Any:
|
@@ -78,6 +83,17 @@ def is_subclass(
|
|
78
83
|
return _is_subclass(src, target)
|
79
84
|
|
80
85
|
|
86
|
+
def is_protocol(maybe_protocol: Type[Any]) -> bool:
|
87
|
+
"""Returns True if a type is a protocol."""
|
88
|
+
if not inspect.isclass(maybe_protocol):
|
89
|
+
return False
|
90
|
+
maybe_protocol = typing.get_origin(maybe_protocol) or maybe_protocol
|
91
|
+
for base in maybe_protocol.__bases__:
|
92
|
+
if base is typing.Protocol or typing.get_origin(base) is typing.Protocol:
|
93
|
+
return True
|
94
|
+
return False
|
95
|
+
|
96
|
+
|
81
97
|
def is_generic(maybe_generic: Type[Any]) -> bool:
|
82
98
|
"""Returns True if a type is a generic class."""
|
83
99
|
return typing.get_origin(maybe_generic) is not None
|
@@ -13,7 +13,7 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
"""Tests for generic type utility."""
|
15
15
|
|
16
|
-
from typing import Any, Generic, TypeVar
|
16
|
+
from typing import Any, Generic, Protocol, TypeVar
|
17
17
|
import unittest
|
18
18
|
|
19
19
|
from pyglove.core.typing import callable_signature
|
@@ -62,6 +62,26 @@ class AA1(AA):
|
|
62
62
|
pass
|
63
63
|
|
64
64
|
|
65
|
+
class E(Protocol):
|
66
|
+
|
67
|
+
def __call__(self, x: int) -> int:
|
68
|
+
pass
|
69
|
+
|
70
|
+
|
71
|
+
class E1(E):
|
72
|
+
pass
|
73
|
+
|
74
|
+
|
75
|
+
class E2(E, Protocol):
|
76
|
+
|
77
|
+
def bar(self, x: int) -> int:
|
78
|
+
pass
|
79
|
+
|
80
|
+
|
81
|
+
class F(Protocol[XType]):
|
82
|
+
x: XType
|
83
|
+
|
84
|
+
|
65
85
|
class InspectTest(unittest.TestCase):
|
66
86
|
|
67
87
|
def test_issubclass(self):
|
@@ -111,6 +131,16 @@ class InspectTest(unittest.TestCase):
|
|
111
131
|
self.assertFalse(inspect.is_subclass(B1[Str], B[str]))
|
112
132
|
self.assertFalse(inspect.is_subclass(B1[Str], A[str, int]))
|
113
133
|
|
134
|
+
# Protocol check.
|
135
|
+
self.assertTrue(inspect.is_subclass(E, E))
|
136
|
+
self.assertTrue(inspect.is_subclass(E, Protocol))
|
137
|
+
# We do not really check protocol comformance at runtime for performance
|
138
|
+
# reasons. So all user classes are considered as subclasses of a Protocol
|
139
|
+
# class.
|
140
|
+
self.assertTrue(inspect.is_subclass(A, E))
|
141
|
+
self.assertFalse(inspect.is_subclass(str, E))
|
142
|
+
self.assertFalse(inspect.is_subclass(1, E))
|
143
|
+
|
114
144
|
# Test tuple cases.
|
115
145
|
self.assertTrue(inspect.is_subclass(int, (str, int)))
|
116
146
|
self.assertTrue(inspect.is_subclass(C, (int, A[str, int])))
|
@@ -124,7 +154,24 @@ class InspectTest(unittest.TestCase):
|
|
124
154
|
self.assertTrue(inspect.is_instance(D(), A[str, int]))
|
125
155
|
self.assertTrue(inspect.is_instance(D(), B[str]))
|
126
156
|
|
157
|
+
def test_is_protocol(self):
|
158
|
+
self.assertFalse(inspect.is_protocol(1))
|
159
|
+
self.assertFalse(inspect.is_protocol(str))
|
160
|
+
self.assertFalse(inspect.is_protocol(Any))
|
161
|
+
self.assertFalse(inspect.is_protocol(A))
|
162
|
+
self.assertFalse(inspect.is_protocol(A[str, int]))
|
163
|
+
self.assertFalse(inspect.is_protocol(Protocol))
|
164
|
+
self.assertFalse(inspect.is_protocol(Protocol[XType]))
|
165
|
+
self.assertTrue(inspect.is_protocol(E))
|
166
|
+
# subclasses of a Protocol class will downgrade if it's not explicitly
|
167
|
+
# inherited from Protocol.
|
168
|
+
# See https://typing.readthedocs.io/en/latest/spec/protocol.html
|
169
|
+
self.assertFalse(inspect.is_protocol(E1))
|
170
|
+
self.assertTrue(inspect.is_protocol(E2))
|
171
|
+
self.assertTrue(inspect.is_protocol(F))
|
172
|
+
|
127
173
|
def test_is_generic(self):
|
174
|
+
self.assertFalse(inspect.is_generic(1))
|
128
175
|
self.assertFalse(inspect.is_generic(str))
|
129
176
|
self.assertFalse(inspect.is_generic(Any))
|
130
177
|
self.assertFalse(inspect.is_generic(A))
|
@@ -24,11 +24,13 @@ from pyglove.core.typing import inspect as pg_inspect
|
|
24
24
|
class _TypeConverterRegistry:
|
25
25
|
"""Type converter registry."""
|
26
26
|
|
27
|
+
_JSON_VALUE_TYPES = frozenset(
|
28
|
+
[int, float, bool, type(None), list, tuple, dict, str]
|
29
|
+
)
|
30
|
+
|
27
31
|
def __init__(self):
|
28
32
|
"""Constructor."""
|
29
33
|
self._converter_list = []
|
30
|
-
self._json_value_types = set(
|
31
|
-
[int, float, bool, type(None), list, tuple, dict, str])
|
32
34
|
|
33
35
|
def register(
|
34
36
|
self,
|
@@ -45,14 +47,17 @@ class _TypeConverterRegistry:
|
|
45
47
|
raise TypeError('Argument \'src\' and \'dest\' must be a type or '
|
46
48
|
'tuple of types.')
|
47
49
|
if isinstance(dest, tuple):
|
48
|
-
json_value_convertible = any(d in self.
|
50
|
+
json_value_convertible = any(d in self._JSON_VALUE_TYPES for d in dest)
|
49
51
|
else:
|
50
|
-
json_value_convertible = dest in self.
|
52
|
+
json_value_convertible = dest in self._JSON_VALUE_TYPES
|
51
53
|
self._converter_list.append((src, dest, convert_fn, json_value_convertible))
|
52
54
|
|
53
55
|
def get_converter(
|
54
56
|
self, src: Type[Any], dest: Type[Any]) -> Optional[Callable[[Any], Any]]:
|
55
57
|
"""Get converter from source type to destination type."""
|
58
|
+
if pg_inspect.is_protocol(dest):
|
59
|
+
return None
|
60
|
+
|
56
61
|
# TODO(daiyip): Right now we don't see the need of a large number of
|
57
62
|
# converters, thus its affordable to iterate the list.
|
58
63
|
# We may consider more efficient way to do lookup in future.
|
@@ -38,6 +38,10 @@ class TypeConversionTest(unittest.TestCase):
|
|
38
38
|
super().__init__(x)
|
39
39
|
self.y = y
|
40
40
|
|
41
|
+
class C(typing.Protocol):
|
42
|
+
def foo(self, x: int) -> int:
|
43
|
+
pass
|
44
|
+
|
41
45
|
a_converter = lambda a: a.x
|
42
46
|
type_conversion.register_converter(A, (str, int), a_converter)
|
43
47
|
|
@@ -46,6 +50,7 @@ class TypeConversionTest(unittest.TestCase):
|
|
46
50
|
self.assertIs(type_conversion.get_json_value_converter(A), a_converter)
|
47
51
|
|
48
52
|
self.assertIsNone(type_conversion.get_converter(A, (float, bool)))
|
53
|
+
self.assertIsNone(type_conversion.get_converter(A, C))
|
49
54
|
self.assertIs(type_conversion.get_converter(A, (float, int)), a_converter)
|
50
55
|
|
51
56
|
# B is a subclass of A, so A's converter applies.
|
@@ -118,13 +118,13 @@ pyglove/core/typing/callable_signature_test.py,sha256=iQmHsKPhJPQlMikDhEyxKyq7yW
|
|
118
118
|
pyglove/core/typing/class_schema.py,sha256=xfPrLYpqntejMLvSL-7rNiXHTQOgAr2kmxWeR890qLs,53940
|
119
119
|
pyglove/core/typing/class_schema_test.py,sha256=UWANPqhu9v_FHNo3cVe05P-bO-HliBmrSBywKrlWep0,29204
|
120
120
|
pyglove/core/typing/custom_typing.py,sha256=qdnIKHWNt5kZAAFdpQXra8bBu6RljMbbJ_YDG2mhAUA,2205
|
121
|
-
pyglove/core/typing/inspect.py,sha256=
|
122
|
-
pyglove/core/typing/inspect_test.py,sha256=
|
121
|
+
pyglove/core/typing/inspect.py,sha256=VLSz1KAunNm2hx0eEMjiwxKLl9FHlKr9nHelLT25iEA,7726
|
122
|
+
pyglove/core/typing/inspect_test.py,sha256=xclevobF0X8c_B5b1q1dkBJZN1TsVA1RUhk5l25DUCM,10248
|
123
123
|
pyglove/core/typing/key_specs.py,sha256=-7xjCuUGoQgD0sMafsRFNlw3S4f1r-7t5OO4ev5bbeI,9225
|
124
124
|
pyglove/core/typing/key_specs_test.py,sha256=5zornpyHMAYoRaG8KDXHiQ3obu9UfRp3399lBeUNTPk,6499
|
125
125
|
pyglove/core/typing/pytype_support.py,sha256=lyX11WVbCwoOi5tTQ90pEOS-yvo_6iEi7Lxbp-nXu2A,2069
|
126
|
-
pyglove/core/typing/type_conversion.py,sha256=
|
127
|
-
pyglove/core/typing/type_conversion_test.py,sha256=
|
126
|
+
pyglove/core/typing/type_conversion.py,sha256=0L4Cbsw_QiM-gpsn-4y-XLEIvwiUB16Clj9gCtoC_Xc,5224
|
127
|
+
pyglove/core/typing/type_conversion_test.py,sha256=BhASOGvtKXmYLWKCELU1RVB_Nmt1V-saSkGogvsNL7E,5342
|
128
128
|
pyglove/core/typing/typed_missing.py,sha256=-l1omAu0jBZv5BnsFYXBqfvQwVBnmPh_X1wcIKD9bOk,2734
|
129
129
|
pyglove/core/typing/typed_missing_test.py,sha256=TCNsb1SRpFaVdxYn2mB_yaLuja8w5Qn5NP7uGiZVBWs,2301
|
130
130
|
pyglove/core/typing/value_specs.py,sha256=Yxdrz4aURMBrtqUW4to-2Vptc6Z6oqQrLyMBiAZt2bc,102302
|
@@ -211,8 +211,8 @@ pyglove/ext/scalars/randoms.py,sha256=LkMIIx7lOq_lvJvVS3BrgWGuWl7Pi91-lA-O8x_gZs
|
|
211
211
|
pyglove/ext/scalars/randoms_test.py,sha256=nEhiqarg8l_5EOucp59CYrpO2uKxS1pe0hmBdZUzRNM,2000
|
212
212
|
pyglove/ext/scalars/step_wise.py,sha256=IDw3tuTpv0KVh7AN44W43zqm1-E0HWPUlytWOQC9w3Y,3789
|
213
213
|
pyglove/ext/scalars/step_wise_test.py,sha256=TL1vJ19xVx2t5HKuyIzGoogF7N3Rm8YhLE6JF7i0iy8,2540
|
214
|
-
pyglove-0.4.5.
|
215
|
-
pyglove-0.4.5.
|
216
|
-
pyglove-0.4.5.
|
217
|
-
pyglove-0.4.5.
|
218
|
-
pyglove-0.4.5.
|
214
|
+
pyglove-0.4.5.dev202502040809.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
215
|
+
pyglove-0.4.5.dev202502040809.dist-info/METADATA,sha256=g47xUh8StCt5ojg7Arw96L0nds0Kl8hrGazZx7IsU10,7067
|
216
|
+
pyglove-0.4.5.dev202502040809.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
217
|
+
pyglove-0.4.5.dev202502040809.dist-info/top_level.txt,sha256=wITzJSKcj8GZUkbq-MvUQnFadkiuAv_qv5qQMw0fIow,8
|
218
|
+
pyglove-0.4.5.dev202502040809.dist-info/RECORD,,
|
File without changes
|
File without changes
|
{pyglove-0.4.5.dev202502020807.dist-info → pyglove-0.4.5.dev202502040809.dist-info}/top_level.txt
RENAMED
File without changes
|