pyglove 0.5.0.dev202508250811__py3-none-any.whl → 0.5.0.dev202511300809__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.
Files changed (44) hide show
  1. pyglove/core/__init__.py +8 -1
  2. pyglove/core/geno/base.py +7 -3
  3. pyglove/core/io/file_system.py +295 -2
  4. pyglove/core/io/file_system_test.py +291 -0
  5. pyglove/core/logging.py +45 -1
  6. pyglove/core/logging_test.py +12 -21
  7. pyglove/core/monitoring.py +657 -0
  8. pyglove/core/monitoring_test.py +289 -0
  9. pyglove/core/symbolic/__init__.py +7 -0
  10. pyglove/core/symbolic/base.py +89 -35
  11. pyglove/core/symbolic/base_test.py +3 -3
  12. pyglove/core/symbolic/dict.py +31 -12
  13. pyglove/core/symbolic/dict_test.py +49 -0
  14. pyglove/core/symbolic/list.py +17 -3
  15. pyglove/core/symbolic/list_test.py +24 -2
  16. pyglove/core/symbolic/object.py +3 -1
  17. pyglove/core/symbolic/object_test.py +13 -10
  18. pyglove/core/symbolic/ref.py +19 -7
  19. pyglove/core/symbolic/ref_test.py +94 -7
  20. pyglove/core/symbolic/unknown_symbols.py +147 -0
  21. pyglove/core/symbolic/unknown_symbols_test.py +100 -0
  22. pyglove/core/typing/annotation_conversion.py +8 -1
  23. pyglove/core/typing/annotation_conversion_test.py +14 -19
  24. pyglove/core/typing/class_schema.py +24 -1
  25. pyglove/core/typing/json_schema.py +221 -8
  26. pyglove/core/typing/json_schema_test.py +508 -12
  27. pyglove/core/typing/type_conversion.py +17 -3
  28. pyglove/core/typing/type_conversion_test.py +7 -2
  29. pyglove/core/typing/value_specs.py +5 -1
  30. pyglove/core/typing/value_specs_test.py +5 -0
  31. pyglove/core/utils/__init__.py +2 -0
  32. pyglove/core/utils/contextual.py +9 -4
  33. pyglove/core/utils/contextual_test.py +10 -0
  34. pyglove/core/utils/error_utils.py +59 -25
  35. pyglove/core/utils/json_conversion.py +360 -63
  36. pyglove/core/utils/json_conversion_test.py +146 -13
  37. pyglove/core/views/html/controls/tab.py +33 -0
  38. pyglove/core/views/html/controls/tab_test.py +37 -0
  39. pyglove/ext/evolution/base_test.py +1 -1
  40. {pyglove-0.5.0.dev202508250811.dist-info → pyglove-0.5.0.dev202511300809.dist-info}/METADATA +8 -1
  41. {pyglove-0.5.0.dev202508250811.dist-info → pyglove-0.5.0.dev202511300809.dist-info}/RECORD +44 -40
  42. {pyglove-0.5.0.dev202508250811.dist-info → pyglove-0.5.0.dev202511300809.dist-info}/WHEEL +0 -0
  43. {pyglove-0.5.0.dev202508250811.dist-info → pyglove-0.5.0.dev202511300809.dist-info}/licenses/LICENSE +0 -0
  44. {pyglove-0.5.0.dev202508250811.dist-info → pyglove-0.5.0.dev202511300809.dist-info}/top_level.txt +0 -0
@@ -1930,8 +1930,12 @@ class Object(Generic, ValueSpecBase):
1930
1930
  elif isinstance(t, type):
1931
1931
  if t is object:
1932
1932
  raise TypeError('<class \'object\'> is too general for Object spec.')
1933
+ elif getattr(t, '__no_type_check__', False):
1934
+ t = object
1933
1935
  elif not pg_inspect.is_generic(t):
1934
- raise TypeError('"cls" for Object spec should be a type or str.')
1936
+ raise TypeError(
1937
+ f'"cls" for Object spec should be a type or str. Encountered: {t!r}.'
1938
+ )
1935
1939
 
1936
1940
  self._forward_ref = forward_ref
1937
1941
  self._type_args = type_args
@@ -2193,6 +2193,11 @@ class ObjectTest(ValueSpecTest):
2193
2193
  self.assertEqual(
2194
2194
  vs.Object(forward_ref('Foo')).forward_refs, set([forward_ref('Foo')]))
2195
2195
 
2196
+ def test_no_type_check_special_handling(self):
2197
+ x = self.A()
2198
+ setattr(x, '__no_type_check__', True)
2199
+ self.assertIs(vs.Object(x).cls, object)
2200
+
2196
2201
  def test_default(self):
2197
2202
  self.assertEqual(vs.Object(self.A).default, typed_missing.MISSING_VALUE)
2198
2203
  a = self.A()
@@ -74,6 +74,7 @@ modules with the following features:
74
74
  from pyglove.core.utils.json_conversion import Nestable
75
75
  from pyglove.core.utils.json_conversion import JSONValueType
76
76
 
77
+ from pyglove.core.utils.json_conversion import JSONConversionContext
77
78
  from pyglove.core.utils.json_conversion import JSONConvertible
78
79
  from pyglove.core.utils.json_conversion import from_json
79
80
  from pyglove.core.utils.json_conversion import to_json
@@ -148,6 +149,7 @@ from pyglove.core.utils.docstr_utils import docstr
148
149
 
149
150
  # Handling exceptions.
150
151
  from pyglove.core.utils.error_utils import catch_errors
152
+ from pyglove.core.utils.error_utils import match_error
151
153
  from pyglove.core.utils.error_utils import CatchErrorsContext
152
154
  from pyglove.core.utils.error_utils import ErrorInfo
153
155
 
@@ -15,6 +15,7 @@
15
15
 
16
16
  import contextlib
17
17
  import dataclasses
18
+ import inspect
18
19
  import threading
19
20
  from typing import Any, Callable, ContextManager, Iterator, Optional
20
21
 
@@ -89,10 +90,14 @@ def with_contextual_override(func: Callable[..., Any]) -> Callable[..., Any]:
89
90
  with contextual_override() as current_context:
90
91
  pass
91
92
 
92
- def _func(*args, **kwargs) -> Any:
93
- with contextual_override(**current_context):
94
- return func(*args, **kwargs)
95
-
93
+ if inspect.iscoroutinefunction(func):
94
+ async def _func(*args, **kwargs) -> Any:
95
+ with contextual_override(**current_context):
96
+ return await func(*args, **kwargs)
97
+ else:
98
+ def _func(*args, **kwargs) -> Any:
99
+ with contextual_override(**current_context):
100
+ return func(*args, **kwargs)
96
101
  return _func
97
102
 
98
103
 
@@ -11,6 +11,7 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+ import asyncio
14
15
  import concurrent.futures
15
16
  import unittest
16
17
  from pyglove.core.utils import contextual
@@ -83,6 +84,15 @@ class ContextualTest(unittest.TestCase):
83
84
  [3]
84
85
  )
85
86
 
87
+ def test_with_contextual_override_async_func(self):
88
+ async def func(i):
89
+ del i
90
+ return contextual.contextual_value('x')
91
+
92
+ with contextual.contextual_override(x=3):
93
+ self.assertEqual(
94
+ asyncio.run(contextual.with_contextual_override(func)(0)), 3
95
+ )
86
96
 
87
97
  if __name__ == '__main__':
88
98
  unittest.main()
@@ -108,6 +108,64 @@ def catch_errors(
108
108
  Yields:
109
109
  A CatchErrorsContext object.
110
110
  """
111
+ errors = _parse_error_spec(errors)
112
+ context = CatchErrorsContext()
113
+ try:
114
+ yield context
115
+ except BaseException as e: # pylint: disable=broad-exception-caught
116
+ if match_error(e, errors):
117
+ context.error = e
118
+ if error_handler is not None:
119
+ error_handler(e)
120
+ else:
121
+ raise
122
+
123
+
124
+ def match_error(
125
+ error: BaseException,
126
+ errors: Union[
127
+ Union[Type[BaseException], Tuple[Type[BaseException], str]],
128
+ Sequence[Union[Type[BaseException], Tuple[Type[BaseException], str]]],
129
+ Dict[Type[BaseException], List[str]],
130
+ ],
131
+ ) -> bool:
132
+ """Returns True if the error matches the specification, .
133
+
134
+ Args:
135
+ error: The error to match.
136
+ errors: A sequence of exception types or tuples of exception type and error
137
+ messages (described in regular expression) as the desired exception types
138
+ to match.
139
+
140
+ Returns:
141
+ True if the error matches the specification, False otherwise.
142
+ """
143
+ error_mapping = _parse_error_spec(errors)
144
+ error_message = error.__class__.__name__ + ': ' + str(error)
145
+ for error_type, error_regexes in error_mapping.items():
146
+ if isinstance(error, error_type):
147
+ if not error_regexes:
148
+ return True
149
+ else:
150
+ for regex in error_regexes:
151
+ assert regex is not None
152
+ if not regex.startswith(('^', '.*')):
153
+ regex = '.*' + regex
154
+ if re.match(regex, error_message):
155
+ return True
156
+ return False
157
+
158
+
159
+ def _parse_error_spec(
160
+ errors: Union[
161
+ Union[Type[BaseException], Tuple[Type[BaseException], str]],
162
+ Sequence[Union[Type[BaseException], Tuple[Type[BaseException], str]]],
163
+ Dict[Type[BaseException], List[str]],
164
+ ]
165
+ ) -> Dict[Type[BaseException], List[str]]:
166
+ """Parses a sequence of error specifications into a dictionary."""
167
+ if isinstance(errors, dict):
168
+ return errors
111
169
  if not isinstance(errors, (tuple, list)):
112
170
  errors = [errors]
113
171
  elif (
@@ -136,28 +194,4 @@ def catch_errors(
136
194
  error_mapping[error_type] = []
137
195
  if regex is not None:
138
196
  error_mapping[error_type].append(regex)
139
-
140
- context = CatchErrorsContext()
141
- try:
142
- yield context
143
- except tuple(error_mapping.keys()) as e:
144
- error_message = e.__class__.__name__ + ': ' + str(e)
145
- found_match = False
146
- for error_type, error_regexes in error_mapping.items():
147
- if isinstance(e, error_type):
148
- if not error_regexes:
149
- found_match = True
150
- else:
151
- for regex in error_regexes:
152
- assert regex is not None
153
- if not regex.startswith(('^', '.*')):
154
- regex = '.*' + regex
155
- if re.match(regex, error_message):
156
- found_match = True
157
- break
158
- if found_match:
159
- context.error = e
160
- if error_handler is not None:
161
- error_handler(e)
162
- else:
163
- raise e
197
+ return error_mapping