pyodide-py 0.26.0.dev0__tar.gz → 0.26.2__tar.gz

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 (31) hide show
  1. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/PKG-INFO +1 -1
  2. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/_pyodide/_base.py +25 -1
  3. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/_pyodide/_core_docs.py +61 -53
  4. pyodide_py-0.26.2/_pyodide/_future_helper.py +14 -0
  5. pyodide_py-0.26.2/_pyodide/jsbind.py +228 -0
  6. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/pyodide/__init__.py +1 -1
  7. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/pyodide/console.py +42 -13
  8. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/pyodide/ffi/__init__.py +2 -0
  9. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/pyodide/webloop.py +13 -15
  10. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/pyodide_py.egg-info/PKG-INFO +1 -1
  11. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/pyodide_py.egg-info/SOURCES.txt +2 -0
  12. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/pyproject.toml +1 -1
  13. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/README.md +0 -0
  14. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/_pyodide/__init__.py +0 -0
  15. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/_pyodide/_importhook.py +0 -0
  16. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/_pyodide/docs_argspec.py +0 -0
  17. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/_pyodide/docstring.py +0 -0
  18. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/_pyodide/py.typed +0 -0
  19. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/pyodide/_core.py +0 -0
  20. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/pyodide/_package_loader.py +0 -0
  21. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/pyodide/_run_js.py +0 -0
  22. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/pyodide/_state.py +0 -0
  23. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/pyodide/code.py +0 -0
  24. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/pyodide/ffi/wrappers.py +0 -0
  25. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/pyodide/http.py +0 -0
  26. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/pyodide/py.typed +0 -0
  27. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/pyodide_py.egg-info/dependency_links.txt +0 -0
  28. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/pyodide_py.egg-info/top_level.txt +0 -0
  29. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/setup.cfg +0 -0
  30. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/setup.py +0 -0
  31. {pyodide-py-0.26.0.dev0 → pyodide_py-0.26.2}/webbrowser.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyodide-py
3
- Version: 0.26.0.dev0
3
+ Version: 0.26.2
4
4
  Summary: A Python package providing core interpreter functionality for Pyodide
5
5
  Author: Pyodide developers
6
6
  Project-URL: Homepage, https://github.com/pyodide/pyodide
@@ -132,6 +132,8 @@ def _parse_and_compile_gen(
132
132
  mode: str = "exec",
133
133
  filename: str = "<exec>",
134
134
  flags: int = 0x0,
135
+ dont_inherit: bool = False,
136
+ optimize: int = -1,
135
137
  ) -> Generator[ast.Module, ast.Module, CodeType]:
136
138
  """Parse ``source``, then yield the AST, then compile the AST and return the
137
139
  code object.
@@ -160,7 +162,7 @@ def _parse_and_compile_gen(
160
162
  _last_expr_to_raise(mod)
161
163
 
162
164
  ast.fix_missing_locations(mod)
163
- return compile(mod, filename, mode, flags=flags)
165
+ return compile(mod, filename, mode, flags, dont_inherit, optimize)
164
166
 
165
167
 
166
168
  ReturnMode = Literal["last_expr", "last_expr_or_assign", "none"]
@@ -214,6 +216,16 @@ class CodeRunner:
214
216
  The flags to compile with. See the documentation for the built-in
215
217
  :external:py:func:`compile` function.
216
218
 
219
+ dont_inherit :
220
+
221
+ Whether to inherit ``__future__`` imports from the outer code.
222
+ See the documentation for the built-in :external:py:func:`compile` function.
223
+
224
+ optimize :
225
+
226
+ Specifies the optimization level of the compiler. See the documentation
227
+ for the built-in :external:py:func:`compile` function.
228
+
217
229
  Examples
218
230
  --------
219
231
  >>> source = "1 + 1"
@@ -254,6 +266,8 @@ class CodeRunner:
254
266
  quiet_trailing_semicolon: bool = True,
255
267
  filename: str = "<exec>",
256
268
  flags: int = 0x0,
269
+ dont_inherit: bool = False,
270
+ optimize: int = -1,
257
271
  ):
258
272
  self._compiled = False
259
273
  self._source = source
@@ -264,6 +278,8 @@ class CodeRunner:
264
278
  quiet_trailing_semicolon=quiet_trailing_semicolon,
265
279
  filename=filename,
266
280
  flags=flags,
281
+ dont_inherit=dont_inherit,
282
+ optimize=optimize,
267
283
  )
268
284
  self.ast = next(self._gen)
269
285
 
@@ -407,6 +423,8 @@ def eval_code(
407
423
  quiet_trailing_semicolon: bool = True,
408
424
  filename: str = "<exec>",
409
425
  flags: int = 0x0,
426
+ dont_inherit: bool = False,
427
+ optimize: int = -1,
410
428
  ) -> Any:
411
429
  """Runs a string as Python source code.
412
430
 
@@ -497,6 +515,8 @@ def eval_code(
497
515
  quiet_trailing_semicolon=quiet_trailing_semicolon,
498
516
  filename=filename,
499
517
  flags=flags,
518
+ dont_inherit=dont_inherit,
519
+ optimize=optimize,
500
520
  )
501
521
  .compile()
502
522
  .run(globals, locals)
@@ -512,6 +532,8 @@ async def eval_code_async(
512
532
  quiet_trailing_semicolon: bool = True,
513
533
  filename: str = "<exec>",
514
534
  flags: int = 0x0,
535
+ dont_inherit: bool = False,
536
+ optimize: int = -1,
515
537
  ) -> Any:
516
538
  """Runs a code string asynchronously.
517
539
 
@@ -577,6 +599,8 @@ async def eval_code_async(
577
599
  quiet_trailing_semicolon=quiet_trailing_semicolon,
578
600
  filename=filename,
579
601
  flags=flags,
602
+ dont_inherit=dont_inherit,
603
+ optimize=optimize,
580
604
  )
581
605
  .compile()
582
606
  .run_async(globals, locals)
@@ -116,6 +116,16 @@ class JsProxy(metaclass=_JsProxyMetaClass):
116
116
  return super().__new__(cls)
117
117
  raise TypeError(f"{cls.__name__} cannot be instantiated.")
118
118
 
119
+ def bind_sig(self, signature: Any) -> "JsProxy":
120
+ """Creates a copy of the JsProxy with a signature bound to it.
121
+
122
+ .. admonition:: Experimental
123
+ :class: warning
124
+
125
+ This feature is not yet stable, nor really documented.
126
+ """
127
+ return self
128
+
119
129
  @property
120
130
  def js_id(self) -> int:
121
131
  """An id number which can be used as a dictionary/set key if you want to
@@ -365,14 +375,12 @@ class JsPromise(JsProxy, Generic[T]):
365
375
  @overload
366
376
  def then(
367
377
  self, onfulfilled: None, onrejected: Callable[[BaseException], Awaitable[S]], /
368
- ) -> "JsPromise[S]":
369
- ...
378
+ ) -> "JsPromise[S]": ...
370
379
 
371
380
  @overload
372
381
  def then(
373
382
  self, onfulfilled: None, onrejected: Callable[[BaseException], S], /
374
- ) -> "JsPromise[S]":
375
- ...
383
+ ) -> "JsPromise[S]": ...
376
384
 
377
385
  @overload
378
386
  def then(
@@ -380,8 +388,7 @@ class JsPromise(JsProxy, Generic[T]):
380
388
  onfulfilled: Callable[[T], Awaitable[S]],
381
389
  onrejected: Callable[[BaseException], Awaitable[S]] | None = None,
382
390
  /,
383
- ) -> "JsPromise[S]":
384
- ...
391
+ ) -> "JsPromise[S]": ...
385
392
 
386
393
  @overload
387
394
  def then(
@@ -389,8 +396,7 @@ class JsPromise(JsProxy, Generic[T]):
389
396
  onfulfilled: Callable[[T], S],
390
397
  onrejected: Callable[[BaseException], S] | None = None,
391
398
  /,
392
- ) -> "JsPromise[S]":
393
- ...
399
+ ) -> "JsPromise[S]": ...
394
400
 
395
401
  @docs_argspec(
396
402
  "(self, onfulfilled: Callable[[T], Awaitable[S] | S] | None, onrejected: Callable[[BaseException], Awaitable[S] | S] | None = None, /) -> 'JsPromise[S]'"
@@ -410,12 +416,10 @@ class JsPromise(JsProxy, Generic[T]):
410
416
  @overload
411
417
  def catch(
412
418
  self, onrejected: Callable[[BaseException], Awaitable[S]], /
413
- ) -> "JsPromise[S]":
414
- ...
419
+ ) -> "JsPromise[S]": ...
415
420
 
416
421
  @overload
417
- def catch(self, onrejected: Callable[[BaseException], S], /) -> "JsPromise[S]":
418
- ...
422
+ def catch(self, onrejected: Callable[[BaseException], S], /) -> "JsPromise[S]": ...
419
423
 
420
424
  @docs_argspec(
421
425
  "(self, onrejected: Callable[[BaseException], Awaitable[S] | S], /) -> 'JsPromise[S]'"
@@ -670,8 +674,7 @@ class JsGenerator(JsIterable[T_co], Generic[T_co, T_contra, V_co]):
670
674
  val: BaseException | object = ...,
671
675
  tb: TracebackType | None = ...,
672
676
  /,
673
- ) -> T_co:
674
- ...
677
+ ) -> T_co: ...
675
678
 
676
679
  @overload
677
680
  def throw(
@@ -680,8 +683,7 @@ class JsGenerator(JsIterable[T_co], Generic[T_co, T_contra, V_co]):
680
683
  val: None = ...,
681
684
  tb: TracebackType | None = ...,
682
685
  /,
683
- ) -> T_co:
684
- ...
686
+ ) -> T_co: ...
685
687
 
686
688
  @docs_argspec("(self, error: BaseException, /) -> T_co")
687
689
  def throw(
@@ -801,8 +803,7 @@ class JsAsyncGenerator(JsAsyncIterable[T_co], Generic[T_co, T_contra, V_co]):
801
803
  val: BaseException | object = ...,
802
804
  tb: TracebackType | None = ...,
803
805
  /,
804
- ) -> Awaitable[T_co]:
805
- ...
806
+ ) -> Awaitable[T_co]: ...
806
807
 
807
808
  @overload
808
809
  def athrow(
@@ -811,8 +812,7 @@ class JsAsyncGenerator(JsAsyncIterable[T_co], Generic[T_co, T_contra, V_co]):
811
812
  val: None = ...,
812
813
  tb: TracebackType | None = ...,
813
814
  /,
814
- ) -> Awaitable[T_co]:
815
- ...
815
+ ) -> Awaitable[T_co]: ...
816
816
 
817
817
  @docs_argspec("(self, error: BaseException, /) -> T_co")
818
818
  def athrow(self, value: Any, *args: Any) -> Awaitable[T_co]:
@@ -856,23 +856,19 @@ class JsArray(JsIterable[T], Generic[T], MutableSequence[T], metaclass=_ABCMeta)
856
856
  _js_type_flags = ["IS_ARRAY", "IS_NODE_LIST", "IS_TYPEDARRAY"]
857
857
 
858
858
  @overload
859
- def __getitem__(self, idx: int) -> T:
860
- ...
859
+ def __getitem__(self, idx: int) -> T: ...
861
860
 
862
861
  @overload
863
- def __getitem__(self, idx: slice) -> "JsArray[T]":
864
- ...
862
+ def __getitem__(self, idx: slice) -> "JsArray[T]": ...
865
863
 
866
864
  def __getitem__(self, idx):
867
865
  raise NotImplementedError
868
866
 
869
867
  @overload
870
- def __setitem__(self, idx: int, value: T) -> None:
871
- ...
868
+ def __setitem__(self, idx: int, value: T) -> None: ...
872
869
 
873
870
  @overload
874
- def __setitem__(self, idx: slice, value: Iterable[T]) -> None:
875
- ...
871
+ def __setitem__(self, idx: slice, value: Iterable[T]) -> None: ...
876
872
 
877
873
  def __setitem__(self, idx, value):
878
874
  pass
@@ -999,12 +995,10 @@ class JsMap(JsIterable[KT], Generic[KT, VT_co], Mapping[KT, VT_co], metaclass=_A
999
995
  raise NotImplementedError
1000
996
 
1001
997
  @overload
1002
- def get(self, key: KT, /) -> VT_co | None:
1003
- ...
998
+ def get(self, key: KT, /) -> VT_co | None: ...
1004
999
 
1005
1000
  @overload
1006
- def get(self, key: KT, default: VT_co | T, /) -> VT_co | T:
1007
- ...
1001
+ def get(self, key: KT, default: VT_co | T, /) -> VT_co | T: ...
1008
1002
 
1009
1003
  @docs_argspec("(self, key: KT, default: VT_co | None, /) -> VT_co")
1010
1004
  def get(self, key: KT, default: Any = None, /) -> VT_co:
@@ -1013,11 +1007,9 @@ class JsMap(JsIterable[KT], Generic[KT, VT_co], Mapping[KT, VT_co], metaclass=_A
1013
1007
 
1014
1008
 
1015
1009
  class _SupportsKeysAndGetItem(Protocol[KT, VT_co]):
1016
- def keys(self) -> Iterable[KT]:
1017
- ...
1010
+ def keys(self) -> Iterable[KT]: ...
1018
1011
 
1019
- def __getitem__(self, __key: KT) -> VT_co:
1020
- ...
1012
+ def __getitem__(self, __key: KT) -> VT_co: ...
1021
1013
 
1022
1014
 
1023
1015
  class JsMutableMap(
@@ -1037,12 +1029,10 @@ class JsMutableMap(
1037
1029
  _js_type_flags = ["HAS_GET | HAS_SET | HAS_LENGTH | IS_ITERABLE", "IS_OBJECT_MAP"]
1038
1030
 
1039
1031
  @overload
1040
- def pop(self, key: KT, /) -> VT:
1041
- ...
1032
+ def pop(self, key: KT, /) -> VT: ...
1042
1033
 
1043
1034
  @overload
1044
- def pop(self, key: KT, default: VT | T = ..., /) -> VT | T:
1045
- ...
1035
+ def pop(self, key: KT, default: VT | T = ..., /) -> VT | T: ...
1046
1036
 
1047
1037
  @docs_argspec("(self, key: KT, default: VT | None = None, /) -> VT")
1048
1038
  def pop(self, key: KT, default: Any = None, /) -> Any:
@@ -1067,16 +1057,13 @@ class JsMutableMap(
1067
1057
  """Empty out the map entirely."""
1068
1058
 
1069
1059
  @overload
1070
- def update(self, __m: _SupportsKeysAndGetItem[KT, VT], **kwargs: VT) -> None:
1071
- ...
1060
+ def update(self, __m: _SupportsKeysAndGetItem[KT, VT], **kwargs: VT) -> None: ...
1072
1061
 
1073
1062
  @overload
1074
- def update(self, __m: Iterable[tuple[KT, VT]], **kwargs: VT) -> None:
1075
- ...
1063
+ def update(self, __m: Iterable[tuple[KT, VT]], **kwargs: VT) -> None: ...
1076
1064
 
1077
1065
  @overload
1078
- def update(self, **kwargs: VT) -> None:
1079
- ...
1066
+ def update(self, **kwargs: VT) -> None: ...
1080
1067
 
1081
1068
  @docs_argspec(
1082
1069
  "(self, other : Mapping[KT, VT] | Iterable[tuple[KT, VT]] = None , /, **kwargs) -> None"
@@ -1209,6 +1196,7 @@ class JsDomElement(JsProxy):
1209
1196
  # from pyproxy.c
1210
1197
 
1211
1198
 
1199
+ @docs_argspec("(obj: Callable[..., Any], /) -> JsOnceCallable")
1212
1200
  def create_once_callable(
1213
1201
  obj: Callable[..., Any], /, *, _may_syncify: bool = False
1214
1202
  ) -> JsOnceCallable:
@@ -1277,8 +1265,7 @@ def to_js(
1277
1265
  ]
1278
1266
  | None
1279
1267
  ) = None,
1280
- ) -> JsArray[Any]:
1281
- ...
1268
+ ) -> JsArray[Any]: ...
1282
1269
 
1283
1270
 
1284
1271
  @overload
@@ -1296,8 +1283,7 @@ def to_js(
1296
1283
  ]
1297
1284
  | None
1298
1285
  ) = None,
1299
- ) -> JsMap[Any, Any]:
1300
- ...
1286
+ ) -> JsMap[Any, Any]: ...
1301
1287
 
1302
1288
 
1303
1289
  @overload
@@ -1315,8 +1301,7 @@ def to_js(
1315
1301
  ]
1316
1302
  | None
1317
1303
  ) = None,
1318
- ) -> Any:
1319
- ...
1304
+ ) -> Any: ...
1320
1305
 
1321
1306
 
1322
1307
  def to_js(
@@ -1496,7 +1481,29 @@ def destroy_proxies(pyproxies: JsArray[Any], /) -> None:
1496
1481
 
1497
1482
 
1498
1483
  def run_sync(x: Awaitable[T]) -> T:
1499
- """Hi!"""
1484
+ """Block until an awaitable is resolved.
1485
+
1486
+ Only works if JS Promise integration is enabled in the runtime and the
1487
+ current Python call stack was entered via :js:func:`pyodide.runPythonAsync`,
1488
+ by calling an async Python function, or via
1489
+ :js:func:`~pyodide.ffi.PyCallable.callPromising`.
1490
+
1491
+ .. admonition:: Experimental
1492
+ :class: warning
1493
+
1494
+ This feature is not yet stable.
1495
+ """
1496
+ raise NotImplementedError
1497
+
1498
+
1499
+ def can_run_sync() -> bool:
1500
+ """Returns ``True`` if :py:func:`run_sync` is available and ``False`` if not.
1501
+
1502
+ .. admonition:: Experimental
1503
+ :class: warning
1504
+
1505
+ This feature is not yet stable.
1506
+ """
1500
1507
  raise NotImplementedError
1501
1508
 
1502
1509
 
@@ -1525,6 +1532,7 @@ __all__ = [
1525
1532
  "JsCallable",
1526
1533
  "JsTypedArray",
1527
1534
  "run_sync",
1535
+ "can_run_sync",
1528
1536
  "create_once_callable",
1529
1537
  "create_proxy",
1530
1538
  "destroy_proxies",
@@ -0,0 +1,14 @@
1
+ def set_result(fut, val):
2
+ if fut.done():
3
+ return
4
+ fut.set_result(val)
5
+
6
+
7
+ def set_exception(fut, val):
8
+ if fut.done():
9
+ return
10
+ fut.set_exception(val)
11
+
12
+
13
+ def get_future_resolvers(fut):
14
+ return (set_result.__get__(fut), set_exception.__get__(fut))
@@ -0,0 +1,228 @@
1
+ from inspect import Parameter, isclass, iscoroutinefunction, signature
2
+ from types import GenericAlias
3
+ from typing import ( # type:ignore[attr-defined]
4
+ Any,
5
+ _AnnotatedAlias,
6
+ _GenericAlias,
7
+ _UnionGenericAlias,
8
+ get_type_hints,
9
+ )
10
+
11
+ from _pyodide_core import (
12
+ Js2PyConverter,
13
+ JsFuncSignature,
14
+ Py2JsConverter,
15
+ create_promise_converter,
16
+ js2py_deep,
17
+ js2py_default,
18
+ js2py_default_call_result,
19
+ py2js_as_json_adaptor,
20
+ py2js_deep,
21
+ py2js_default,
22
+ )
23
+
24
+
25
+ class Py2JsConverterMeta(type):
26
+ def __new__(metaclass, name, bases, namespace):
27
+ result = namespace.get("converter", py2js_default).copy()
28
+ pc1 = namespace["pre_convert"]
29
+ pc2 = getattr(result, "pre_convert", None)
30
+ if pc1 and pc2:
31
+
32
+ def pcfinal(o):
33
+ return pc2(pc1(o))
34
+ else:
35
+ pcfinal = pc1 or pc2 # type:ignore[assignment]
36
+ result.pre_convert = pcfinal
37
+ return result
38
+
39
+
40
+ class Js2PyConverterMeta(type):
41
+ def __new__(metaclass, name, bases, namespace):
42
+ result = namespace.get("converter", js2py_default).copy()
43
+ pc1 = namespace["post_convert"]
44
+ pc2 = getattr(result, "post_convert", None)
45
+ if pc1 and pc2:
46
+
47
+ def pcfinal(o):
48
+ return pc1(pc2(o))
49
+ else:
50
+ pcfinal = pc1 or pc2 # type:ignore[assignment]
51
+ result.post_convert = pcfinal
52
+ return result
53
+
54
+
55
+ def js2py_bind(x):
56
+ class Converter(metaclass=Js2PyConverterMeta):
57
+ @staticmethod
58
+ def post_convert(obj):
59
+ return obj.bind_sig(x)
60
+
61
+ return Converter
62
+
63
+
64
+ class Json:
65
+ py2js = py2js_as_json_adaptor
66
+
67
+
68
+ class Deep:
69
+ js2py = js2py_deep
70
+ py2js = py2js_deep
71
+
72
+
73
+ class Default:
74
+ pass
75
+
76
+
77
+ class BindClass:
78
+ pass
79
+
80
+
81
+ class TypeConverter:
82
+ def unpack_generic_alias(self, x: _GenericAlias) -> Any:
83
+ if isinstance(x, _UnionGenericAlias):
84
+ if len(x.__args__) != 2:
85
+ return None
86
+ e0 = x.__args__[0]
87
+ e1 = x.__args__[1]
88
+ e0isNone = e0 == type(None)
89
+ e1isNone = e1 == type(None)
90
+ if (not e0isNone) and (not e1isNone):
91
+ return None
92
+ if e0isNone:
93
+ x = e1
94
+ if e1isNone:
95
+ x = e0
96
+ if isinstance(x, GenericAlias) and x.__name__ in ["Future", "Awaitable"]:
97
+ arg = x.__args__[0]
98
+ return create_promise_converter(self.js2py_annotation(arg))
99
+ if isinstance(x, _AnnotatedAlias):
100
+ return x.__metadata__[0]
101
+ return None
102
+
103
+ def js2py_annotation(self, annotation: Any) -> "Js2PyConverter":
104
+ if isinstance(annotation, (_GenericAlias, GenericAlias)): # noqa: UP038
105
+ annotation = self.unpack_generic_alias(annotation)
106
+ if annotation is None:
107
+ return None
108
+ if isinstance(annotation, Js2PyConverter):
109
+ return annotation
110
+ res = getattr(annotation, "js2py", None)
111
+ if res:
112
+ return res
113
+
114
+ if issubclass(annotation, BindClass):
115
+ return js2py_bind(annotation)
116
+ return None
117
+
118
+ def py2js_annotation(self, annotation: Any) -> "Py2JsConverter":
119
+ if isinstance(annotation, _GenericAlias):
120
+ annotation = self.unpack_generic_alias(annotation)
121
+ if annotation is None:
122
+ return None
123
+ if isinstance(annotation, Py2JsConverter):
124
+ return annotation
125
+ res = getattr(annotation, "py2js", None)
126
+ if res:
127
+ return res
128
+
129
+ return None
130
+
131
+
132
+ type_converter = TypeConverter()
133
+
134
+
135
+ def func_to_sig(f):
136
+ res = getattr(f, "_js_sig", None)
137
+ if res:
138
+ return res
139
+ res = func_to_sig_inner(f)
140
+ f._js_sig = res
141
+ return res
142
+
143
+
144
+ def get_attr_sig(sig, attr):
145
+ if not hasattr(sig, "_type_hints"):
146
+ sig._type_hints = get_type_hints(sig, include_extras=True)
147
+ attr_sig = sig._type_hints.get(attr, None)
148
+ if not attr_sig:
149
+ return (False, getattr(sig, attr, None))
150
+ if isinstance(attr_sig, BindClass):
151
+ return (False, attr_sig)
152
+ converter = type_converter.js2py_annotation(attr_sig)
153
+ if converter:
154
+ return (True, converter)
155
+ return (False, None)
156
+
157
+
158
+ no_default = Parameter.empty
159
+
160
+
161
+ def func_to_sig_inner(f):
162
+ sig = signature(f)
163
+ posparams = []
164
+ posparams_defaults = []
165
+ posparams_nmandatory = 0
166
+ varpos = None
167
+ kwparam_names = []
168
+ kwparam_converters = []
169
+ kwparam_defaults = []
170
+ varkwd = None
171
+ types = get_type_hints(f, include_extras=True)
172
+
173
+ for p in sig.parameters.values():
174
+ converter = (
175
+ type_converter.py2js_annotation(types.get(p.name, None)) or py2js_default
176
+ )
177
+ match p.kind:
178
+ case Parameter.POSITIONAL_ONLY:
179
+ posparams.append(converter)
180
+ if p.default == Parameter.empty:
181
+ posparams_nmandatory += 1
182
+ else:
183
+ posparams_defaults.append(p.default)
184
+ case Parameter.POSITIONAL_OR_KEYWORD:
185
+ raise RuntimeError("Don't currently handle POS_OR_KWD args")
186
+ case Parameter.KEYWORD_ONLY:
187
+ kwparam_names.append(p.name)
188
+ kwparam_converters.append(converter)
189
+ kwparam_defaults.append(p.default)
190
+ case Parameter.VAR_POSITIONAL:
191
+ varpos = converter
192
+ case Parameter.VAR_KEYWORD:
193
+ varkwd = converter
194
+ case _:
195
+ raise RuntimeError("Unreachable")
196
+ if len(kwparam_names) > 64:
197
+ # We use a bitflag to check which kwparams have been passed to fill in
198
+ # defaults / raise type error.
199
+ raise RuntimeError("Cannot handle function with more than 64 kwonly args")
200
+ result = type_converter.js2py_annotation(types.get("return", None))
201
+ if iscoroutinefunction(f):
202
+ if result is None:
203
+ result = js2py_default
204
+ result = create_promise_converter(result)
205
+ elif result is None:
206
+ result = js2py_default_call_result
207
+
208
+ should_construct = isclass(f)
209
+ return JsFuncSignature(
210
+ f,
211
+ should_construct,
212
+ posparams_nmandatory,
213
+ tuple(posparams),
214
+ tuple(posparams_defaults),
215
+ varpos,
216
+ tuple(kwparam_names),
217
+ tuple(kwparam_converters),
218
+ tuple(kwparam_defaults),
219
+ varkwd,
220
+ result,
221
+ )
222
+
223
+
224
+ def _default_sig_stencil(*args, **kwargs):
225
+ pass
226
+
227
+
228
+ default_signature = func_to_sig_inner(_default_sig_stencil)
@@ -10,7 +10,7 @@
10
10
  # This package is imported by the test suite as well, and currently we don't use
11
11
  # pytest mocks for js or pyodide_js, so make sure to test "if IN_BROWSER" before
12
12
  # importing from these.
13
- __version__ = "0.26.0.dev0"
13
+ __version__ = "0.26.2"
14
14
 
15
15
  __all__ = ["__version__", "console", "code", "ffi", "http", "webloop"]
16
16
 
@@ -92,11 +92,15 @@ class _Compile(Compile):
92
92
  return_mode: ReturnMode = "last_expr",
93
93
  quiet_trailing_semicolon: bool = True,
94
94
  flags: int = 0x0,
95
+ dont_inherit: bool = False,
96
+ optimize: int = -1,
95
97
  ) -> None:
96
98
  super().__init__()
97
99
  self.flags |= flags
98
100
  self.return_mode = return_mode
99
101
  self.quiet_trailing_semicolon = quiet_trailing_semicolon
102
+ self.dont_inherit = dont_inherit
103
+ self.optimize = optimize
100
104
 
101
105
  def __call__( # type: ignore[override]
102
106
  self, source: str, filename: str, symbol: str, *, incomplete_input: bool = True
@@ -119,6 +123,8 @@ class _Compile(Compile):
119
123
  filename=filename,
120
124
  return_mode=return_mode,
121
125
  flags=self.flags,
126
+ dont_inherit=self.dont_inherit,
127
+ optimize=self.optimize,
122
128
  ).compile()
123
129
  assert code_runner.code
124
130
  for feature in _features:
@@ -146,11 +152,15 @@ class _CommandCompiler(CommandCompiler):
146
152
  return_mode: ReturnMode = "last_expr",
147
153
  quiet_trailing_semicolon: bool = True,
148
154
  flags: int = 0x0,
155
+ dont_inherit: bool = False,
156
+ optimize: int = -1,
149
157
  ) -> None:
150
158
  self.compiler = _Compile(
151
159
  return_mode=return_mode,
152
160
  quiet_trailing_semicolon=quiet_trailing_semicolon,
153
161
  flags=flags,
162
+ dont_inherit=dont_inherit,
163
+ optimize=optimize,
154
164
  )
155
165
 
156
166
  def __call__( # type: ignore[override]
@@ -237,6 +247,16 @@ class Console:
237
247
  filename :
238
248
 
239
249
  The file name to report in error messages. Defaults to ``"<console>"``.
250
+
251
+ dont_inherit :
252
+
253
+ Whether to inherit ``__future__`` imports from the outer code.
254
+ See the documentation for the built-in :external:py:func:`compile` function.
255
+
256
+ optimize :
257
+
258
+ Specifies the optimization level of the compiler. See the documentation
259
+ for the built-in :external:py:func:`compile` function.
240
260
  """
241
261
 
242
262
  globals: dict[str, Any]
@@ -270,6 +290,8 @@ class Console:
270
290
  stderr_callback: Callable[[str], None] | None = None,
271
291
  persistent_stream_redirection: bool = False,
272
292
  filename: str = "<console>",
293
+ dont_inherit: bool = False,
294
+ optimize: int = -1,
273
295
  ) -> None:
274
296
  if globals is None:
275
297
  globals = {"__name__": "__console__", "__doc__": None}
@@ -283,9 +305,9 @@ class Console:
283
305
  self.buffer = []
284
306
  self._lock = asyncio.Lock()
285
307
  self._streams_redirected = False
286
- self._stream_generator: Generator[
287
- None, None, None
288
- ] | None = None # track persistent stream redirection
308
+ self._stream_generator: Generator[None, None, None] | None = (
309
+ None # track persistent stream redirection
310
+ )
289
311
  if persistent_stream_redirection:
290
312
  self.persistent_redirect_streams()
291
313
  self._completer = rlcompleter.Completer(self.globals)
@@ -294,7 +316,11 @@ class Console:
294
316
  self.completer_word_break_characters = (
295
317
  """ \t\n`~!@#$%^&*()-=+[{]}\\|;:'\",<>/?"""
296
318
  )
297
- self._compile = _CommandCompiler(flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT)
319
+ self._compile = _CommandCompiler(
320
+ flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT,
321
+ dont_inherit=dont_inherit,
322
+ optimize=optimize,
323
+ )
298
324
 
299
325
  def persistent_redirect_streams(self) -> None:
300
326
  """Redirect :py:data:`~sys.stdin`/:py:data:`~sys.stdout`/:py:data:`~sys.stdout` persistently"""
@@ -379,18 +405,21 @@ class Console:
379
405
  res.set_result(fut.result())
380
406
  res = None
381
407
 
382
- ensure_future(self.runcode(source, code)).add_done_callback(done_cb)
408
+ ensure_future(self._runcode_with_lock(source, code)).add_done_callback(done_cb)
383
409
  return res
384
410
 
411
+ async def _runcode_with_lock(self, source: str, code: CodeRunner) -> Any:
412
+ async with self._lock:
413
+ return await self.runcode(source, code)
414
+
385
415
  async def runcode(self, source: str, code: CodeRunner) -> Any:
386
416
  """Execute a code object and return the result."""
387
- async with self._lock:
388
- with self.redirect_streams():
389
- try:
390
- return await code.run_async(self.globals)
391
- finally:
392
- sys.stdout.flush()
393
- sys.stderr.flush()
417
+ with self.redirect_streams():
418
+ try:
419
+ return await code.run_async(self.globals)
420
+ finally:
421
+ sys.stdout.flush()
422
+ sys.stderr.flush()
394
423
 
395
424
  def formatsyntaxerror(self, e: Exception) -> str:
396
425
  """Format the syntax error that just occurred.
@@ -409,7 +438,7 @@ class Console:
409
438
  kept_frames = 0
410
439
  # Try to trim out stack frames inside our code
411
440
  for frame, _ in traceback.walk_tb(tb):
412
- keep_frames = keep_frames or frame.f_code.co_filename == "<console>"
441
+ keep_frames = keep_frames or frame.f_code.co_filename == self.filename
413
442
  keep_frames = keep_frames or frame.f_code.co_filename == "<exec>"
414
443
  if keep_frames:
415
444
  kept_frames += 1
@@ -21,6 +21,7 @@ if IN_BROWSER:
21
21
  for t in [
22
22
  "JsException",
23
23
  "run_sync",
24
+ "can_run_sync",
24
25
  "create_once_callable",
25
26
  "create_proxy",
26
27
  "destroy_proxies",
@@ -54,6 +55,7 @@ __all__ = [
54
55
  "create_proxy",
55
56
  "destroy_proxies",
56
57
  "to_js",
58
+ "run_sync",
57
59
  "IN_BROWSER",
58
60
  "register_js_module",
59
61
  "unregister_js_module",
@@ -8,7 +8,7 @@ from asyncio import Future, Task
8
8
  from collections.abc import Awaitable, Callable
9
9
  from typing import Any, TypeVar, overload
10
10
 
11
- from .ffi import IN_BROWSER, create_once_callable
11
+ from .ffi import IN_BROWSER, create_once_callable, run_sync
12
12
 
13
13
  if IN_BROWSER:
14
14
  from pyodide_js._api import scheduleCallback
@@ -30,32 +30,28 @@ class PyodideFuture(Future[T]):
30
30
  self,
31
31
  onfulfilled: None,
32
32
  onrejected: Callable[[BaseException], Awaitable[S]],
33
- ) -> "PyodideFuture[S]":
34
- ...
33
+ ) -> "PyodideFuture[S]": ...
35
34
 
36
35
  @overload
37
36
  def then(
38
37
  self,
39
38
  onfulfilled: None,
40
39
  onrejected: Callable[[BaseException], S],
41
- ) -> "PyodideFuture[S]":
42
- ...
40
+ ) -> "PyodideFuture[S]": ...
43
41
 
44
42
  @overload
45
43
  def then(
46
44
  self,
47
45
  onfulfilled: Callable[[T], Awaitable[S]],
48
46
  onrejected: Callable[[BaseException], Awaitable[S]] | None = None,
49
- ) -> "PyodideFuture[S]":
50
- ...
47
+ ) -> "PyodideFuture[S]": ...
51
48
 
52
49
  @overload
53
50
  def then(
54
51
  self,
55
52
  onfulfilled: Callable[[T], S],
56
53
  onrejected: Callable[[BaseException], S] | None = None,
57
- ) -> "PyodideFuture[S]":
58
- ...
54
+ ) -> "PyodideFuture[S]": ...
59
55
 
60
56
  def then(
61
57
  self,
@@ -128,12 +124,10 @@ class PyodideFuture(Future[T]):
128
124
  @overload
129
125
  def catch(
130
126
  self, onrejected: Callable[[BaseException], Awaitable[S]]
131
- ) -> "PyodideFuture[S]":
132
- ...
127
+ ) -> "PyodideFuture[S]": ...
133
128
 
134
129
  @overload
135
- def catch(self, onrejected: Callable[[BaseException], S]) -> "PyodideFuture[S]":
136
- ...
130
+ def catch(self, onrejected: Callable[[BaseException], S]) -> "PyodideFuture[S]": ...
137
131
 
138
132
  def catch(
139
133
  self, onrejected: Callable[[BaseException], object]
@@ -265,6 +259,10 @@ class WebLoop(asyncio.AbstractEventLoop):
265
259
  do_something_with_result(result)
266
260
  ```
267
261
  """
262
+ from pyodide_js._api import config
263
+
264
+ if config.enableRunUntilComplete:
265
+ return run_sync(future)
268
266
  return asyncio.ensure_future(future)
269
267
 
270
268
  #
@@ -274,7 +272,7 @@ class WebLoop(asyncio.AbstractEventLoop):
274
272
  def call_soon(
275
273
  self,
276
274
  callback: Callable[..., Any],
277
- *args: Any,
275
+ *args: Any, # type: ignore[override]
278
276
  context: contextvars.Context | None = None,
279
277
  ) -> asyncio.Handle:
280
278
  """Arrange for a callback to be called as soon as possible.
@@ -290,7 +288,7 @@ class WebLoop(asyncio.AbstractEventLoop):
290
288
  def call_soon_threadsafe(
291
289
  self,
292
290
  callback: Callable[..., Any],
293
- *args: Any,
291
+ *args: Any, # type: ignore[override]
294
292
  context: contextvars.Context | None = None,
295
293
  ) -> asyncio.Handle:
296
294
  """Like ``call_soon()``, but thread-safe.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyodide-py
3
- Version: 0.26.0.dev0
3
+ Version: 0.26.2
4
4
  Summary: A Python package providing core interpreter functionality for Pyodide
5
5
  Author: Pyodide developers
6
6
  Project-URL: Homepage, https://github.com/pyodide/pyodide
@@ -5,9 +5,11 @@ setup.py
5
5
  ./_pyodide/__init__.py
6
6
  ./_pyodide/_base.py
7
7
  ./_pyodide/_core_docs.py
8
+ ./_pyodide/_future_helper.py
8
9
  ./_pyodide/_importhook.py
9
10
  ./_pyodide/docs_argspec.py
10
11
  ./_pyodide/docstring.py
12
+ ./_pyodide/jsbind.py
11
13
  ./_pyodide/py.typed
12
14
  ./pyodide/__init__.py
13
15
  ./pyodide/_core.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pyodide-py"
7
- version = "0.26.0.dev0"
7
+ version = "0.26.2"
8
8
  authors = [{name = "Pyodide developers"}]
9
9
  description = "A Python package providing core interpreter functionality for Pyodide"
10
10
  classifiers = [
File without changes
File without changes
File without changes