@pyscript/core 0.7.11 → 0.7.13

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 (80) hide show
  1. package/dist/{codemirror-BYspKCDy.js → codemirror-lkW_eC9r.js} +2 -2
  2. package/dist/codemirror-lkW_eC9r.js.map +1 -0
  3. package/dist/{codemirror_commands-BLDaEdQ6.js → codemirror_commands-DL2aL4qa.js} +2 -2
  4. package/dist/codemirror_commands-DL2aL4qa.js.map +1 -0
  5. package/dist/codemirror_lang-python-DD5EtV36.js +2 -0
  6. package/dist/codemirror_lang-python-DD5EtV36.js.map +1 -0
  7. package/dist/codemirror_language-DRHeqAwG.js +2 -0
  8. package/dist/codemirror_language-DRHeqAwG.js.map +1 -0
  9. package/dist/codemirror_state-BNbbkoNK.js +2 -0
  10. package/dist/codemirror_state-BNbbkoNK.js.map +1 -0
  11. package/dist/codemirror_view-FN7LalDk.js +2 -0
  12. package/dist/codemirror_view-FN7LalDk.js.map +1 -0
  13. package/dist/core-B4BRXuDy.js +4 -0
  14. package/dist/core-B4BRXuDy.js.map +1 -0
  15. package/dist/core.js +1 -1
  16. package/dist/{deprecations-manager-DIDxhyRq.js → deprecations-manager-BRHTwqUZ.js} +2 -2
  17. package/dist/{deprecations-manager-DIDxhyRq.js.map → deprecations-manager-BRHTwqUZ.js.map} +1 -1
  18. package/dist/{donkey-CLhmQOjG.js → donkey-CBEqGHeD.js} +2 -2
  19. package/dist/{donkey-CLhmQOjG.js.map → donkey-CBEqGHeD.js.map} +1 -1
  20. package/dist/{error-uzvvriog.js → error-DRVc1NKK.js} +2 -2
  21. package/dist/{error-uzvvriog.js.map → error-DRVc1NKK.js.map} +1 -1
  22. package/dist/index-C-U2wRvV.js +2 -0
  23. package/dist/index-C-U2wRvV.js.map +1 -0
  24. package/dist/{mpy-CnF17tqI.js → mpy-B-jI5Qug.js} +2 -2
  25. package/dist/{mpy-CnF17tqI.js.map → mpy-B-jI5Qug.js.map} +1 -1
  26. package/dist/{py-BZSSqcx3.js → py-DNLpCVR2.js} +2 -2
  27. package/dist/{py-BZSSqcx3.js.map → py-DNLpCVR2.js.map} +1 -1
  28. package/dist/{py-editor-DZ0Dxzzk.js → py-editor-DCtATRBs.js} +2 -2
  29. package/dist/{py-editor-DZ0Dxzzk.js.map → py-editor-DCtATRBs.js.map} +1 -1
  30. package/dist/{py-game-bqieV522.js → py-game-BGWt8dH1.js} +2 -2
  31. package/dist/{py-game-bqieV522.js.map → py-game-BGWt8dH1.js.map} +1 -1
  32. package/dist/{py-terminal-DYY4WN57.js → py-terminal-BKvzGq7q.js} +2 -2
  33. package/dist/{py-terminal-DYY4WN57.js.map → py-terminal-BKvzGq7q.js.map} +1 -1
  34. package/dist/xterm_addon-fit-DxKdSnof.js +14 -0
  35. package/dist/xterm_addon-fit-DxKdSnof.js.map +1 -0
  36. package/dist/xterm_addon-web-links-B6rWzrcs.js +14 -0
  37. package/dist/xterm_addon-web-links-B6rWzrcs.js.map +1 -0
  38. package/dist/zip-CgZGjqjF.js +2 -0
  39. package/dist/zip-CgZGjqjF.js.map +1 -0
  40. package/package.json +15 -14
  41. package/src/3rd-party/xterm_addon-fit.js +14 -2
  42. package/src/3rd-party/xterm_addon-web-links.js +14 -2
  43. package/src/core.js +13 -2
  44. package/src/stdlib/pyscript/__init__.py +100 -31
  45. package/src/stdlib/pyscript/context.py +198 -0
  46. package/src/stdlib/pyscript/display.py +211 -127
  47. package/src/stdlib/pyscript/events.py +191 -88
  48. package/src/stdlib/pyscript/fetch.py +156 -25
  49. package/src/stdlib/pyscript/ffi.py +132 -16
  50. package/src/stdlib/pyscript/flatted.py +78 -1
  51. package/src/stdlib/pyscript/fs.py +205 -49
  52. package/src/stdlib/pyscript/media.py +210 -50
  53. package/src/stdlib/pyscript/storage.py +214 -27
  54. package/src/stdlib/pyscript/util.py +28 -7
  55. package/src/stdlib/pyscript/web.py +1079 -881
  56. package/src/stdlib/pyscript/websocket.py +252 -45
  57. package/src/stdlib/pyscript/workers.py +176 -27
  58. package/src/stdlib/pyscript.js +13 -13
  59. package/types/stdlib/pyscript.d.ts +1 -1
  60. package/dist/codemirror-BYspKCDy.js.map +0 -1
  61. package/dist/codemirror_commands-BLDaEdQ6.js.map +0 -1
  62. package/dist/codemirror_lang-python-CkOVBHci.js +0 -2
  63. package/dist/codemirror_lang-python-CkOVBHci.js.map +0 -1
  64. package/dist/codemirror_language-DOkvasqm.js +0 -2
  65. package/dist/codemirror_language-DOkvasqm.js.map +0 -1
  66. package/dist/codemirror_state-BIAL8JKm.js +0 -2
  67. package/dist/codemirror_state-BIAL8JKm.js.map +0 -1
  68. package/dist/codemirror_view-Bt4sLgyA.js +0 -2
  69. package/dist/codemirror_view-Bt4sLgyA.js.map +0 -1
  70. package/dist/core-PTfg6inS.js +0 -4
  71. package/dist/core-PTfg6inS.js.map +0 -1
  72. package/dist/index-jZ1aOVVJ.js +0 -2
  73. package/dist/index-jZ1aOVVJ.js.map +0 -1
  74. package/dist/xterm_addon-fit--gyF3PcZ.js +0 -2
  75. package/dist/xterm_addon-fit--gyF3PcZ.js.map +0 -1
  76. package/dist/xterm_addon-web-links-D95xh2la.js +0 -2
  77. package/dist/xterm_addon-web-links-D95xh2la.js.map +0 -1
  78. package/dist/zip-pccs084i.js +0 -2
  79. package/dist/zip-pccs084i.js.map +0 -1
  80. package/src/stdlib/pyscript/magic_js.py +0 -84
@@ -1,88 +1,295 @@
1
+ """
2
+ This module provides a Pythonic wrapper around the browser's
3
+ [WebSocket API](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket),
4
+ enabling two-way communication with WebSocket servers.
5
+
6
+ Use this for real-time applications:
7
+
8
+ - Pythonic interface to browser WebSockets.
9
+ - Automatic handling of async event handlers.
10
+ - Support for receiving text (`str`) and binary (`memoryview`) data.
11
+ - Support for sending text (`str`) and binary (`bytes` and `bytearray`) data.
12
+ - Compatible with Pyodide and MicroPython.
13
+ - Works in webworker contexts.
14
+ - Naming deliberately follows the JavaScript WebSocket API closely for
15
+ familiarity.
16
+
17
+ See the Python docs for
18
+ [an explanation of memoryview](https://docs.python.org/3/library/stdtypes.html#memoryview).
19
+
20
+
21
+ ```python
22
+ from pyscript import WebSocket
23
+
24
+
25
+ def on_open(event):
26
+ print("Connected!")
27
+ ws.send("Hello server")
28
+
29
+ def on_message(event):
30
+ print(f"Received: {event.data}")
31
+
32
+ def on_close(event):
33
+ print("Connection closed")
34
+
35
+ ws = WebSocket(url="ws://localhost:8080/")
36
+ ws.onopen = on_open
37
+ ws.onmessage = on_message
38
+ ws.onclose = on_close
39
+ ```
40
+ """
41
+
1
42
  import js
2
43
  from pyscript.ffi import create_proxy
3
44
  from pyscript.util import as_bytearray, is_awaitable
4
45
 
5
- code = "code"
6
- protocols = "protocols"
7
- reason = "reason"
8
- methods = ["onclose", "onerror", "onmessage", "onopen"]
9
46
 
47
+ def _attach_event_handler(websocket, handler_name, handler_function):
48
+ """
49
+ Given a `websocket`, and `handler_name`, attach the `handler_function`
50
+ to the `WebSocket` instance, handling both synchronous and asynchronous
51
+ handler functions.
10
52
 
11
- def add_listener(socket, onevent, listener):
12
- p = create_proxy(listener)
53
+ Creates a JavaScript proxy for the handler and wraps async handlers
54
+ appropriately. Handles the `WebSocketEvent` wrapping for all handlers.
55
+ """
56
+ if is_awaitable(handler_function):
13
57
 
14
- if is_awaitable(listener):
58
+ async def async_wrapper(event):
59
+ await handler_function(WebSocketEvent(event))
15
60
 
16
- async def wrapper(e):
17
- await p(EventMessage(e))
61
+ wrapped_handler = create_proxy(async_wrapper)
62
+ else:
63
+ wrapped_handler = create_proxy(
64
+ lambda event: handler_function(WebSocketEvent(event))
65
+ )
66
+ # Note: Direct assignment (websocket[handler_name]) fails in Pyodide.
67
+ setattr(websocket, handler_name, wrapped_handler)
18
68
 
19
- m = wrapper
20
69
 
21
- else:
22
- m = lambda e: p(EventMessage(e))
70
+ class WebSocketEvent:
71
+ """
72
+ A read-only wrapper for
73
+ [WebSocket event objects](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent).
23
74
 
24
- # Pyodide fails at setting socket[onevent] directly
25
- setattr(socket, onevent, m)
75
+ This class wraps browser WebSocket events and provides convenient access
76
+ to event properties. It handles the conversion of binary data from
77
+ JavaScript typed arrays to Python bytes-like objects.
26
78
 
79
+ The most commonly used property is `event.data`, which contains the
80
+ message data for "message" events.
81
+
82
+ ```python
83
+ def on_message(event): # The event is a WebSocketEvent instance.
84
+ # For text messages.
85
+ if isinstance(event.data, str):
86
+ print(f"Text: {event.data}")
87
+ else:
88
+ # For binary messages.
89
+ print(f"Binary: {len(event.data)} bytes")
90
+ ```
91
+ """
27
92
 
28
- class EventMessage:
29
93
  def __init__(self, event):
94
+ """
95
+ Create a WebSocketEvent wrapper from an underlying JavaScript
96
+ `event`.
97
+ """
30
98
  self._event = event
31
99
 
32
100
  def __getattr__(self, attr):
33
- value = getattr(self._event, attr)
101
+ """
102
+ Get an attribute `attr` from the underlying event object.
34
103
 
104
+ Handles special conversion of binary data from JavaScript typed
105
+ arrays to Python `memoryview` objects.
106
+ """
107
+ value = getattr(self._event, attr)
35
108
  if attr == "data" and not isinstance(value, str):
36
109
  if hasattr(value, "to_py"):
110
+ # Pyodide - convert JavaScript typed array to Python.
37
111
  return value.to_py()
38
- # shims in MicroPython
39
- return memoryview(as_bytearray(value))
40
-
112
+ else:
113
+ # MicroPython - manually convert JS ArrayBuffer.
114
+ return memoryview(as_bytearray(value))
41
115
  return value
42
116
 
43
117
 
44
118
  class WebSocket:
119
+ """
120
+ This class provides a Python-friendly interface to WebSocket connections,
121
+ handling communication with WebSocket servers. It supports both text and
122
+ binary data transmission.
123
+
124
+ Access the underlying WebSocket methods and properties directly if needed.
125
+ However, the wrapper provides a more Pythonic API. If you need to work
126
+ with the raw JavaScript WebSocket instance, you can access it via the
127
+ `_js_websocket` attribute.
128
+
129
+ Using textual (`str`) data:
130
+
131
+ ```python
132
+ from pyscript import WebSocket
133
+
134
+
135
+ # Create WebSocket with handlers as arguments.
136
+ def handle_message(event):
137
+ print(f"Got: {event.data}")
138
+
139
+ ws = WebSocket(
140
+ url="ws://echo.websocket.org/",
141
+ onmessage=handle_message
142
+ )
143
+
144
+ # Or assign handlers after creation.
145
+ def handle_open(event):
146
+ ws.send("Hello!")
147
+
148
+ ws.onopen = handle_open
149
+ ```
150
+
151
+ Using binary (`memoryview`) data:
152
+
153
+ ```python
154
+ def handle_message(event):
155
+ if isinstance(event.data, str):
156
+ print(f"Text: {event.data}")
157
+ else:
158
+ # Binary data as memoryview.
159
+ print(f"Binary: {len(event.data)} bytes")
160
+
161
+ ws = WebSocket(url="ws://example.com/", onmessage=handle_message)
162
+
163
+ # Send binary data.
164
+ data = bytearray([0x01, 0x02, 0x03])
165
+ ws.send(data)
166
+ ```
167
+
168
+ Read more about Python's
169
+ [`memoryview` here](https://docs.python.org/3/library/stdtypes.html#memoryview).
170
+ """
171
+
172
+ # WebSocket ready state constants.
45
173
  CONNECTING = 0
46
174
  OPEN = 1
47
175
  CLOSING = 2
48
176
  CLOSED = 3
49
177
 
50
- def __init__(self, **kw):
51
- url = kw["url"]
52
- if protocols in kw:
53
- socket = js.WebSocket.new(url, kw[protocols])
54
- else:
55
- socket = js.WebSocket.new(url)
178
+ def __init__(self, url, protocols=None, **handlers):
179
+ """
180
+ Create a new WebSocket connection from the given `url` (`ws://` or
181
+ `wss://`). Optionally specify `protocols` (a string or a list of
182
+ protocol strings) and event handlers (`onopen`, `onmessage`, etc.) as
183
+ keyword arguments.
184
+
185
+ These arguments and naming conventions mirror those of the
186
+ [underlying JavaScript WebSocket API](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
187
+ for familiarity.
56
188
 
57
- socket.binaryType = "arraybuffer"
58
- object.__setattr__(self, "_ws", socket)
189
+ If you need access to the underlying JavaScript WebSocket instance,
190
+ you can get it via the `_js_websocket` attribute.
59
191
 
60
- for t in methods:
61
- if t in kw:
62
- add_listener(socket, t, kw[t])
192
+ ```python
193
+ # Basic connection.
194
+ ws = WebSocket(url="ws://localhost:8080/")
195
+
196
+ # With protocol.
197
+ ws = WebSocket(
198
+ url="wss://example.com/socket",
199
+ protocols="chat"
200
+ )
201
+
202
+ # With handlers.
203
+ ws = WebSocket(
204
+ url="ws://localhost:8080/",
205
+ onopen=lambda e: print("Connected"),
206
+ onmessage=lambda e: print(e.data)
207
+ )
208
+ ```
209
+ """
210
+ # Create underlying JavaScript WebSocket.
211
+ if protocols:
212
+ js_websocket = js.WebSocket.new(url, protocols)
213
+ else:
214
+ js_websocket = js.WebSocket.new(url)
215
+ # Set binary type to arraybuffer for easier Python handling.
216
+ js_websocket.binaryType = "arraybuffer"
217
+ # Store the underlying WebSocket.
218
+ # Use object.__setattr__ to bypass our custom __setattr__.
219
+ object.__setattr__(self, "_js_websocket", js_websocket)
220
+ # Attach any event handlers passed as keyword arguments.
221
+ for handler_name, handler in handlers.items():
222
+ setattr(self, handler_name, handler)
63
223
 
64
224
  def __getattr__(self, attr):
65
- return getattr(self._ws, attr)
225
+ """
226
+ Get an attribute `attr` from the underlying WebSocket.
227
+
228
+ This allows transparent access to WebSocket properties like
229
+ `readyState`, `url`, `bufferedAmount`, etc.
230
+ """
231
+ return getattr(self._js_websocket, attr)
66
232
 
67
233
  def __setattr__(self, attr, value):
68
- if attr in methods:
69
- add_listener(self._ws, attr, value)
70
- else:
71
- setattr(self._ws, attr, value)
234
+ """
235
+ Set an attribute `attr` on the WebSocket to the given `value`.
72
236
 
73
- def close(self, **kw):
74
- if code in kw and reason in kw:
75
- self._ws.close(kw[code], kw[reason])
76
- elif code in kw:
77
- self._ws.close(kw[code])
237
+ Event handler attributes (`onopen`, `onmessage`, etc.) are specially
238
+ handled to create proper proxies. Other attributes are set on the
239
+ underlying WebSocket directly.
240
+ """
241
+ if attr in ["onclose", "onerror", "onmessage", "onopen"]:
242
+ _attach_event_handler(self._js_websocket, attr, value)
78
243
  else:
79
- self._ws.close()
244
+ setattr(self._js_websocket, attr, value)
80
245
 
81
246
  def send(self, data):
247
+ """
248
+ Send `data` through the WebSocket.
249
+
250
+ Accepts both text (`str`) and binary data (`bytes`, `bytearray`, etc.).
251
+ Binary data is automatically converted to a JavaScript `Uint8Array`.
252
+
253
+ ```python
254
+ # Send text.
255
+ ws.send("Hello server!")
256
+
257
+ # Send binary.
258
+ ws.send(bytes([1, 2, 3, 4]))
259
+ ws.send(bytearray([5, 6, 7, 8]))
260
+ ```
261
+
262
+ !!! warning
263
+
264
+ The WebSocket **must be in the OPEN state to send data**.
265
+ """
82
266
  if isinstance(data, str):
83
- self._ws.send(data)
267
+ self._js_websocket.send(data)
84
268
  else:
85
269
  buffer = js.Uint8Array.new(len(data))
86
- for pos, b in enumerate(data):
87
- buffer[pos] = b
88
- self._ws.send(buffer)
270
+ for index, byte_value in enumerate(data):
271
+ buffer[index] = byte_value
272
+ self._js_websocket.send(buffer)
273
+
274
+ def close(self, code=None, reason=None):
275
+ """
276
+ Close the WebSocket connection. Optionally specify a `code` (`int`)
277
+ and a `reason` (`str`) for closing the connection.
278
+
279
+ ```python
280
+ # Normal close.
281
+ ws.close()
282
+
283
+ # Close with code and reason.
284
+ ws.close(code=1000, reason="Task completed")
285
+ ```
286
+
287
+ Usage and values for `code` and `reasons`
288
+ [are explained here](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close).
289
+ """
290
+ if code and reason:
291
+ self._js_websocket.close(code, reason)
292
+ elif code:
293
+ self._js_websocket.close(code)
294
+ else:
295
+ self._js_websocket.close()
@@ -1,45 +1,194 @@
1
- import js as _js
2
- from polyscript import workers as _workers
1
+ """
2
+ This module provides access to named
3
+ [web workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API)
4
+ defined in `<script>` tags, and utilities for dynamically creating workers
5
+ from Python code.
3
6
 
4
- _get = _js.Reflect.get
7
+ Named workers are Python web workers defined in HTML with a `name` attribute
8
+ that can be referenced from the main thread or other workers. This module
9
+ provides the `workers` object for accessing named workers and the
10
+ `create_named_worker()` function for dynamically creating them.
5
11
 
12
+ Accessing named workers:
6
13
 
7
- def _set(script, name, value=""):
8
- script.setAttribute(name, value)
14
+ ```html
15
+ <!-- Define a named worker -->
16
+ <script type="py" worker name="calculator">
17
+ def add(a, b):
18
+ return a + b
9
19
 
20
+ __export__ = ["add"]
21
+ </script>
22
+
23
+ <!-- Access from main thread -->
24
+ <script type="mpy">
25
+ from pyscript import workers
26
+
27
+
28
+ calc = await workers["calculator"]
29
+ result = await calc.add(5, 3)
30
+ print(result) # 8
31
+ </script>
32
+ ```
33
+
34
+ Dynamically creating named workers:
35
+
36
+ ```python
37
+ from pyscript import create_named_worker
38
+
39
+
40
+ # Create a worker from a Python file.
41
+ worker = await create_named_worker(
42
+ src="./background_tasks.py",
43
+ name="task-processor"
44
+ )
45
+
46
+ # Use the worker's exported functions.
47
+ result = await worker.process_data([1, 2, 3, 4, 5])
48
+ print(result)
49
+ ```
50
+
51
+ Key features:
52
+ - Access (`await`) named workers via dictionary-like syntax.
53
+ - Dynamically create workers from Python.
54
+ - Cross-interpreter support (Pyodide and MicroPython).
55
+
56
+ Worker access is asynchronous - you must `await workers[name]` to get
57
+ a reference to the worker. This is because workers may not be ready
58
+ immediately at startup.
59
+ """
60
+
61
+ import js
62
+ import json
63
+ from polyscript import workers as _polyscript_workers
64
+
65
+
66
+ class _ReadOnlyWorkersProxy:
67
+ """
68
+ A read-only proxy for accessing named web workers. Use
69
+ `create_named_worker()` to create new workers found in this proxy.
70
+
71
+ This provides dictionary-like access to named workers defined in
72
+ the page. It handles differences between Pyodide and MicroPython
73
+ implementations transparently.
74
+
75
+ (See: https://github.com/pyscript/pyscript/issues/2106 for context.)
76
+
77
+ The proxy is read-only to prevent accidental modification of the
78
+ underlying workers registry. Both item access and attribute access are
79
+ supported for convenience (especially since HTML attribute names may
80
+ not be valid Python identifiers).
81
+
82
+ ```python
83
+ from pyscript import workers
84
+
85
+ # Access a named worker.
86
+ my_worker = await workers["worker-name"]
87
+ result = await my_worker.some_function()
88
+
89
+ # Alternatively, if the name works, access via attribute notation.
90
+ my_worker = await workers.worker_name
91
+ result = await my_worker.some_function()
92
+ ```
93
+
94
+ **This is a proxy object, not a dict**. You cannot iterate over it or
95
+ get a list of worker names. This is intentional because worker
96
+ startup timing is non-deterministic.
97
+ """
10
98
 
11
- # this solves an inconsistency between Pyodide and MicroPython
12
- # @see https://github.com/pyscript/pyscript/issues/2106
13
- class _ReadOnlyProxy:
14
99
  def __getitem__(self, name):
15
- return _get(_workers, name)
100
+ """
101
+ Get a named worker by `name`. It returns a promise that resolves to
102
+ the worker reference when ready.
103
+
104
+ This is useful if the underlying worker name is not a valid Python
105
+ identifier.
106
+
107
+ ```python
108
+ worker = await workers["my-worker"]
109
+ ```
110
+ """
111
+ return js.Reflect.get(_polyscript_workers, name)
16
112
 
17
113
  def __getattr__(self, name):
18
- return _get(_workers, name)
114
+ """
115
+ Get a named worker as an attribute. It returns a promise that resolves
116
+ to the worker reference when ready.
19
117
 
118
+ This allows accessing workers via dot notation as an alternative
119
+ to bracket notation.
20
120
 
21
- workers = _ReadOnlyProxy()
121
+ ```python
122
+ worker = await workers.my_worker
123
+ ```
124
+ """
125
+ return js.Reflect.get(_polyscript_workers, name)
22
126
 
23
127
 
24
- async def create_named_worker(src="", name="", config=None, type="py"):
25
- from json import dumps
128
+ # Global workers proxy for accessing named workers.
129
+ workers = _ReadOnlyWorkersProxy()
130
+ """Global proxy for accessing named web workers."""
26
131
 
27
- if not src:
28
- msg = "Named workers require src"
29
- raise ValueError(msg)
30
132
 
31
- if not name:
32
- msg = "Named workers require a name"
33
- raise ValueError(msg)
133
+ async def create_named_worker(src, name, config=None, type="py"):
134
+ """
135
+ Dynamically create a web worker with a `src` Python file, a unique
136
+ `name` and optional `config` (dict or JSON string) and `type` (`py`
137
+ for Pyodide or `mpy` for MicroPython, the default is `py`).
34
138
 
35
- s = _js.document.createElement("script")
36
- s.type = type
37
- s.src = src
38
- _set(s, "worker")
39
- _set(s, "name", name)
139
+ This function creates a new web worker by injecting a `<script>` tag into
140
+ the document. The worker will be accessible via the `workers` proxy once
141
+ it's ready.
40
142
 
41
- if config:
42
- _set(s, "config", (isinstance(config, str) and config) or dumps(config))
143
+ It returns a promise that resolves to the worker reference when ready.
144
+
145
+ ```python
146
+ from pyscript import create_named_worker
43
147
 
44
- _js.document.body.append(s)
148
+
149
+ # Create a Pyodide worker.
150
+ worker = await create_named_worker(
151
+ src="./my_worker.py",
152
+ name="background-worker"
153
+ )
154
+
155
+ # Use the worker.
156
+ result = await worker.process_data()
157
+
158
+ # Create with standard PyScript configuration.
159
+ worker = await create_named_worker(
160
+ src="./processor.py",
161
+ name="data-processor",
162
+ config={"packages": ["numpy", "pandas"]}
163
+ )
164
+
165
+ # Use MicroPython instead.
166
+ worker = await create_named_worker(
167
+ src="./lightweight_worker.py",
168
+ name="micro-worker",
169
+ type="mpy"
170
+ )
171
+ ```
172
+
173
+ !!! info
174
+
175
+ **The worker script should define** `__export__` to specify which
176
+ functions or objects are accessible from the main thread.
177
+ """
178
+ # Create script element for the worker.
179
+ script = js.document.createElement("script")
180
+ script.type = type
181
+ script.src = src
182
+ # Mark as a worker with a name.
183
+ script.setAttribute("worker", "")
184
+ script.setAttribute("name", name)
185
+ # Add configuration if provided.
186
+ if config:
187
+ if isinstance(config, str):
188
+ config_str = config
189
+ else:
190
+ config_str = json.dumps(config)
191
+ script.setAttribute("config", config_str)
192
+ # Inject the script into the document and await the result.
193
+ js.document.body.append(script)
45
194
  return await workers[name]