langfun 0.1.2.dev202510160805__py3-none-any.whl → 0.1.2.dev202510170805__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.

Potentially problematic release.


This version of langfun might be problematic. Click here for more details.

langfun/core/__init__.py CHANGED
@@ -40,9 +40,13 @@ from langfun.core.component import context
40
40
  as_context = context
41
41
  use_context = context
42
42
 
43
- # Invoke a callable object asynchronously.
43
+ # Support for async IO.
44
44
  from langfun.core.async_support import invoke_async
45
45
 
46
+ # Adaptors for async function/context manager to sync versions.
47
+ from langfun.core.async_support import invoke_sync
48
+ from langfun.core.async_support import sync_context_manager
49
+
46
50
  # Shortcut function for overriding components attributes, usually for
47
51
  # override settings.
48
52
  from langfun.core.component import use_settings
@@ -14,15 +14,85 @@
14
14
  """Utility for async IO in Langfun."""
15
15
 
16
16
  import asyncio
17
- from typing import Any, Callable
17
+ import contextlib
18
+ from typing import Any, Awaitable, Callable, Iterator
19
+ import anyio
18
20
  import pyglove as pg
19
21
 
20
22
 
21
23
  async def invoke_async(
22
- callable_object: Callable[..., Any], *args, **kwargs
24
+ sync_callable: Callable[..., Any], *args, **kwargs
23
25
  ) -> Any:
24
26
  """Invokes a callable asynchronously with `lf.context` manager enabled."""
25
27
  return await asyncio.to_thread(
26
28
  # Enable `lf.context` manager for async calls.
27
- pg.with_contextual_override(callable_object), *args, **kwargs
29
+ pg.with_contextual_override(sync_callable), *args, **kwargs
28
30
  )
31
+
32
+
33
+ def invoke_sync(
34
+ async_callable: Callable[..., Awaitable[Any]],
35
+ *args,
36
+ **kwargs
37
+ ) -> Any:
38
+ """Invokes a async callable synchronously."""
39
+ async def _invoke():
40
+ return await async_callable(*args, **kwargs)
41
+ invoke_fn = pg.with_contextual_override(_invoke)
42
+ blocking_portal = pg.utils.thread_local_get('__blocking_portal__', None)
43
+ if blocking_portal is None:
44
+ return anyio.run(invoke_fn)
45
+ return blocking_portal.call(invoke_fn)
46
+
47
+
48
+ @contextlib.contextmanager
49
+ def sync_context_manager(
50
+ async_context_manager: contextlib.AbstractAsyncContextManager[Any]
51
+ ) -> Iterator[Any]:
52
+ """Adapts an async context manager to a sync context manager.
53
+
54
+ sync_context_manager installs a blocking portal in current thread to run the
55
+ async context manager in a blocking way. It's useful for running async code in
56
+ sync context managers, e.g. `sync_context_manager` can be nested and share the
57
+ same event loop.
58
+
59
+ Example:
60
+
61
+ ```python
62
+ @contextlib.asynccontextmanager
63
+ async def foo(x):
64
+ try:
65
+ yield x
66
+ finally:
67
+ pass
68
+
69
+ with lf.sync_context_manager(foo(x)) as x
70
+ with lf.sync_context_manager(foo(y)) as y:
71
+ ...
72
+ ```
73
+
74
+ Args:
75
+ async_context_manager: The async context manager to adapt.
76
+
77
+ Yields:
78
+ The value yielded by the async context manager.
79
+ """
80
+ blocking_portal = pg.utils.thread_local_get('__blocking_portal__', None)
81
+ portal_exit_stack = None
82
+
83
+ try:
84
+ if blocking_portal is None:
85
+ portal_exit_stack = contextlib.ExitStack()
86
+ blocking_portal = portal_exit_stack.enter_context(
87
+ anyio.from_thread.start_blocking_portal()
88
+ )
89
+ pg.utils.thread_local_set('__blocking_portal__', blocking_portal)
90
+ context_manager = blocking_portal.wrap_async_context_manager(
91
+ async_context_manager
92
+ )
93
+ with context_manager as value:
94
+ yield value
95
+ finally:
96
+ if portal_exit_stack is not None:
97
+ portal_exit_stack.close()
98
+ pg.utils.thread_local_del('__blocking_portal__')
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import asyncio
16
+ import contextlib
16
17
  import time
17
18
  import unittest
18
19
 
@@ -34,6 +35,28 @@ class AsyncSupportTest(unittest.TestCase):
34
35
  with pg.contextual_override(z=3):
35
36
  self.assertEqual(asyncio.run(r), 6)
36
37
 
38
+ def test_invoke_sync(self):
39
+ @contextlib.asynccontextmanager
40
+ async def bar(x):
41
+ try:
42
+ yield x
43
+ finally:
44
+ pass
45
+
46
+ async def foo(x, *, y):
47
+ time.sleep(2)
48
+ return x + y + pg.contextual_value('z', 0)
49
+
50
+ with pg.contextual_override(z=3):
51
+ with async_support.sync_context_manager(bar(1)) as x:
52
+ self.assertEqual(x, 1)
53
+ with async_support.sync_context_manager(bar(2)) as y:
54
+ self.assertEqual(y, 2)
55
+ self.assertEqual(async_support.invoke_sync(foo, 1, y=2), 6)
56
+
57
+ with pg.contextual_override(z=2):
58
+ self.assertEqual(async_support.invoke_sync(foo, 1, y=2), 5)
59
+
37
60
 
38
61
  if __name__ == '__main__':
39
62
  unittest.main()
@@ -262,6 +262,7 @@ class ProgressControlTest(unittest.TestCase):
262
262
  with contextlib.redirect_stderr(string_io):
263
263
  ctrl.update(1)
264
264
  ctrl.refresh()
265
+ sys.stderr.flush()
265
266
  self.assertEqual(string_io.getvalue(), '')
266
267
  concurrent.progress_bar = 'tqdm'
267
268
 
@@ -274,6 +275,7 @@ class ProgressControlTest(unittest.TestCase):
274
275
  ctrl.set_status('bar')
275
276
  ctrl.update(10)
276
277
  ctrl.refresh()
278
+ sys.stderr.flush()
277
279
  self.assertEqual(
278
280
  string_io.getvalue(),
279
281
  '\x1b[1m\x1b[31mfoo\x1b[0m: \x1b[34m10% (10/100)\x1b[0m : bar\n'
@@ -288,6 +290,7 @@ class ProgressControlTest(unittest.TestCase):
288
290
  self.assertIsInstance(ctrl, concurrent._TqdmProgressControl)
289
291
  ctrl.update(10)
290
292
  ctrl.refresh()
293
+ sys.stderr.flush()
291
294
  self.assertIn('10/100', string_io.getvalue())
292
295
 
293
296
  tqdm = concurrent.tqdm
@@ -316,6 +319,7 @@ class ProgressBarTest(unittest.TestCase):
316
319
  for _ in concurrent.concurrent_execute(fun, range(5)):
317
320
  concurrent.ProgressBar.refresh()
318
321
  concurrent.ProgressBar.uninstall(bar_id)
322
+ sys.stderr.flush()
319
323
  output_str = string_io.getvalue()
320
324
  self.assertIn('100%', output_str)
321
325
  self.assertIn('5/5', output_str)
@@ -332,7 +336,7 @@ class ProgressBarTest(unittest.TestCase):
332
336
  concurrent.ProgressBar.update(bar_id, 0, status=1)
333
337
  concurrent.ProgressBar.uninstall(bar_id)
334
338
  sys.stderr.flush()
335
- time.sleep(1)
339
+ time.sleep(1)
336
340
  self.assertIn('1/4', string_io.getvalue())
337
341
  # TODO(daiyip): Re-enable once flakiness is fixed.
338
342
  # self.assertIn('2/4', string_io.getvalue())
@@ -564,7 +568,8 @@ class ConcurrentMapTest(unittest.TestCase):
564
568
  fun, [1, 2, 3], timeout=1.5, max_workers=1, show_progress=True
565
569
  )
566
570
  ], key=lambda x: x[0])
567
- string_io.flush()
571
+ sys.stderr.flush()
572
+
568
573
  self.assertEqual( # pylint: disable=g-generic-assert
569
574
  output,
570
575
  [
@@ -592,6 +597,7 @@ class ConcurrentMapTest(unittest.TestCase):
592
597
  show_progress=bar_id, status_fn=lambda p: dict(x=1, y=1)
593
598
  )
594
599
  ], key=lambda x: x[0])
600
+ sys.stderr.flush()
595
601
 
596
602
  self.assertEqual( # pylint: disable=g-generic-assert
597
603
  output,
@@ -50,7 +50,7 @@ class TqdmProgressTrackerTest(unittest.TestCase):
50
50
  string_io = io.StringIO()
51
51
  with contextlib.redirect_stderr(string_io):
52
52
  _ = experiment.run(root_dir, 'new', plugins=[])
53
- sys.stderr.flush()
53
+ sys.stderr.flush()
54
54
  self.assertIn('All: 100%', string_io.getvalue())
55
55
 
56
56
  def test_with_example_ids(self):
@@ -61,7 +61,7 @@ class TqdmProgressTrackerTest(unittest.TestCase):
61
61
  string_io = io.StringIO()
62
62
  with contextlib.redirect_stderr(string_io):
63
63
  _ = experiment.run(root_dir, 'new', example_ids=[1], plugins=[])
64
- sys.stderr.flush()
64
+ sys.stderr.flush()
65
65
  self.assertIn('All: 100%', string_io.getvalue())
66
66
 
67
67
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfun
3
- Version: 0.1.2.dev202510160805
3
+ Version: 0.1.2.dev202510170805
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors
@@ -21,11 +21,13 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
21
  Classifier: Topic :: Software Development :: Libraries
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE
24
+ Requires-Dist: anyio>=4.7.0
24
25
  Requires-Dist: jinja2>=3.1.2
25
26
  Requires-Dist: puremagic>=1.20
26
27
  Requires-Dist: pyglove>=0.4.5.dev202507140812
27
28
  Requires-Dist: requests>=2.31.0
28
29
  Provides-Extra: all
30
+ Requires-Dist: anyio>=4.7.0; extra == "all"
29
31
  Requires-Dist: jinja2>=3.1.2; extra == "all"
30
32
  Requires-Dist: puremagic>=1.20; extra == "all"
31
33
  Requires-Dist: pyglove>=0.4.5.dev202507140812; extra == "all"
@@ -6,13 +6,13 @@ langfun/assistant/capabilities/gui/drawing.py,sha256=8wgol61P7HovLg5EaevRmDPXTFu
6
6
  langfun/assistant/capabilities/gui/drawing_test.py,sha256=d6LQ1ctG78YRi58UVBdndwyEqTC_ITdk191oA3tASxM,3421
7
7
  langfun/assistant/capabilities/gui/location.py,sha256=QYJlY5kUNEwiZFiPYRyFAO7bD2ez4jL5hYn1_AK1ulc,8643
8
8
  langfun/assistant/capabilities/gui/location_test.py,sha256=pQUOH1sKuAwjTTYKu615RUnecc_lNrddfvkyxf9297w,9051
9
- langfun/core/__init__.py,sha256=lZQNVBzQa5d6kQFHRwRg9zZqPaEC_-PwAV-73k4fzR4,4854
10
- langfun/core/async_support.py,sha256=Mzqze88WqWBRRcBVJEQ4H1jRgLwUP43acI0NZ7unZ7M,1022
11
- langfun/core/async_support_test.py,sha256=lZ4rZ-kWlc94lezTajOVo2OQbxWiHfF6KhKOfMGzELQ,1094
9
+ langfun/core/__init__.py,sha256=GC4amBXybjSfYVrEQSLFb_KBhH75uD95GKkHNiJfwkY,5011
10
+ langfun/core/async_support.py,sha256=WF4sflm0Q-UHJ8lPtlEo9hSwqXqm1kfaAYVPTyVP3n0,3062
11
+ langfun/core/async_support_test.py,sha256=fMz1ulGrfUCuHp7RtYktuBwpbRN3kCBKnB4LFvaXSAA,1754
12
12
  langfun/core/component.py,sha256=g1kQM0bryYYYWVDrSMnHfc74wIBbpfe5_B3s-UIP5GE,3028
13
13
  langfun/core/component_test.py,sha256=0CxTgjAud3aj8wBauFhG2FHDqrxCTl4OI4gzQTad-40,9254
14
14
  langfun/core/concurrent.py,sha256=zY-pXqlGqss_GI20tM1gXvyW8QepVPUuFNmutcIdhbI,32760
15
- langfun/core/concurrent_test.py,sha256=zrkDid2oHSXJYGPhrQzA_6Af6oHAn9UrnYGZmN00ies,17693
15
+ langfun/core/concurrent_test.py,sha256=KzXOlfR3i_-s_GKBLYrO5-ETCvHoFbFY2o9FEeOeXq4,17818
16
16
  langfun/core/console.py,sha256=cLQEf84aDxItA9fStJV22xJch0TqFLNf9hLqwJ0RHmU,2652
17
17
  langfun/core/console_test.py,sha256=pBOcuNMJdVELywvroptfcRtJMsegMm3wSlHAL2TdxVk,1679
18
18
  langfun/core/langfunc.py,sha256=G50YgoVZ0y1GFw2ev41MlOqr6qa8YakbvNC0h_E0PiA,11140
@@ -85,7 +85,7 @@ langfun/core/eval/v2/metrics_test.py,sha256=LibZXvWEJDVRY-Mza_bQT-SbmbXCHUnFhL7Z
85
85
  langfun/core/eval/v2/progress.py,sha256=azZgssQgNdv3IgjKEaQBuGI5ucFDNbdi02P4z_nQ8GE,10292
86
86
  langfun/core/eval/v2/progress_test.py,sha256=YU7VHzmy5knPZwj9vpBN3rQQH2tukj9eKHkuBCI62h8,2540
87
87
  langfun/core/eval/v2/progress_tracking.py,sha256=zNhNPGlnJnHELEfFpbTMCSXFn8d1IJ57OOYkfFaBFfM,6097
88
- langfun/core/eval/v2/progress_tracking_test.py,sha256=0d13LQyUKy1_bkscN0-vcBcQ36HNp89kgJ_N0jl2URM,2339
88
+ langfun/core/eval/v2/progress_tracking_test.py,sha256=MC7hD-KfxqSIwKK_BN7oGx7HA8O3_fY-Y3cYYAhZzxE,2343
89
89
  langfun/core/eval/v2/reporting.py,sha256=yUIPCAMnp7InIzpv1DDWrcLO-75iiOUTpscj7smkfrA,8335
90
90
  langfun/core/eval/v2/reporting_test.py,sha256=CMK-vwho8cNRJwlbkCqm_v5fykE7Y3V6SaIOCY0CDyA,5671
91
91
  langfun/core/eval/v2/runners.py,sha256=bEniZDNu44AQgvqpwLsvBU4V_7WltAe-NPhYgIsLj1E,16848
@@ -181,8 +181,8 @@ langfun/env/event_handlers/event_logger.py,sha256=3dbPjBe53dBgntYHlyLlj_77hVecPS
181
181
  langfun/env/event_handlers/event_logger_test.py,sha256=PGof3rPllNnyzs3Yp8kaOHLeTkVrzUgCJwlODTrVRKI,9111
182
182
  langfun/env/event_handlers/metric_writer.py,sha256=NgJKsd6xWOtEd0IjYi7coGEaqGYkkPcDjXN9CQ3vxPU,18043
183
183
  langfun/env/event_handlers/metric_writer_test.py,sha256=flRqK10wonhJk4idGD_8jjEjrfjgH0R-qcu-7Bj1G5s,5335
184
- langfun-0.1.2.dev202510160805.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
185
- langfun-0.1.2.dev202510160805.dist-info/METADATA,sha256=au899IUDN58JgGfkb7QaynFP9i-qZruve58dFnL8b4g,7380
186
- langfun-0.1.2.dev202510160805.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
187
- langfun-0.1.2.dev202510160805.dist-info/top_level.txt,sha256=RhlEkHxs1qtzmmtWSwYoLVJAc1YrbPtxQ52uh8Z9VvY,8
188
- langfun-0.1.2.dev202510160805.dist-info/RECORD,,
184
+ langfun-0.1.2.dev202510170805.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
185
+ langfun-0.1.2.dev202510170805.dist-info/METADATA,sha256=3yLUnJv86dB7mQqbEn1zevGRwvvRZpKQjaNdF739BuY,7452
186
+ langfun-0.1.2.dev202510170805.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
187
+ langfun-0.1.2.dev202510170805.dist-info/top_level.txt,sha256=RhlEkHxs1qtzmmtWSwYoLVJAc1YrbPtxQ52uh8Z9VvY,8
188
+ langfun-0.1.2.dev202510170805.dist-info/RECORD,,