@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyscript/core",
3
- "version": "0.7.11",
3
+ "version": "0.7.13",
4
4
  "type": "module",
5
5
  "description": "PyScript",
6
6
  "module": "./index.js",
@@ -48,6 +48,7 @@
48
48
  "build:plugins": "node rollup/plugins.cjs",
49
49
  "build:stdlib": "node rollup/stdlib.cjs",
50
50
  "build:3rd-party": "node rollup/3rd-party.cjs",
51
+ "build:offline": "node rollup/offline.cjs | bash",
51
52
  "build:tests-index": "node rollup/build_test_index.cjs",
52
53
  "clean:3rd-party": "rm src/3rd-party/*.js && rm src/3rd-party/*.css",
53
54
  "test:integration": "npm run test:ws; static-handler --coi . 2>/dev/null & SH_PID=$!; EXIT_CODE=0; (playwright test tests/js_tests.spec.js && playwright test tests/py_tests.main.spec.js && playwright test tests/py_tests.worker.spec.js) || EXIT_CODE=$?; kill $SH_PID 2>/dev/null; exit $EXIT_CODE",
@@ -70,32 +71,32 @@
70
71
  "@webreflection/utils": "^0.1.1",
71
72
  "add-promise-listener": "^0.1.3",
72
73
  "basic-devtools": "^0.1.6",
73
- "polyscript": "^0.19.10",
74
+ "polyscript": "^0.20.4",
74
75
  "sticky-module": "^0.1.1",
75
76
  "to-json-callback": "^0.1.1",
76
77
  "type-checked-collections": "^0.1.7"
77
78
  },
78
79
  "devDependencies": {
79
- "@codemirror/commands": "^6.10.0",
80
+ "@codemirror/commands": "^6.10.1",
80
81
  "@codemirror/lang-python": "^6.2.1",
81
- "@codemirror/language": "^6.11.3",
82
- "@codemirror/state": "^6.5.2",
83
- "@codemirror/view": "^6.38.6",
84
- "@playwright/test": "^1.56.1",
82
+ "@codemirror/language": "^6.12.1",
83
+ "@codemirror/state": "^6.5.4",
84
+ "@codemirror/view": "^6.39.11",
85
+ "@playwright/test": "^1.57.0",
85
86
  "@rollup/plugin-commonjs": "^29.0.0",
86
87
  "@rollup/plugin-node-resolve": "^16.0.3",
87
88
  "@rollup/plugin-terser": "^0.4.4",
88
89
  "@webreflection/toml-j0.4": "^1.1.4",
89
- "@xterm/addon-fit": "^0.10.0",
90
- "@xterm/addon-web-links": "^0.11.0",
91
- "@xterm/xterm": "^5.5.0",
92
- "bun": "^1.3.1",
93
- "chokidar": "^4.0.3",
90
+ "@xterm/addon-fit": "0.11.0",
91
+ "@xterm/addon-web-links": "0.12.0",
92
+ "@xterm/xterm": "5.5.0",
93
+ "bun": "^1.3.6",
94
+ "chokidar": "^5.0.0",
94
95
  "codedent": "^0.1.2",
95
96
  "codemirror": "^6.0.2",
96
- "eslint": "^9.39.1",
97
+ "eslint": "^9.39.2",
97
98
  "flatted": "^3.3.3",
98
- "rollup": "^4.52.5",
99
+ "rollup": "^4.55.3",
99
100
  "rollup-plugin-postcss": "^4.0.2",
100
101
  "rollup-plugin-string": "^3.0.0",
101
102
  "static-handler": "^0.5.3",
@@ -1,7 +1,19 @@
1
1
  /**
2
2
  * Bundled by jsDelivr using Rollup v2.79.2 and Terser v5.39.0.
3
- * Original file: /npm/@xterm/addon-fit@0.10.0/lib/addon-fit.js
3
+ * Original file: /npm/@xterm/addon-fit@0.11.0/lib/addon-fit.mjs
4
4
  *
5
5
  * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
6
6
  */
7
- var e,t,r={exports:{}};self;var s=r.exports=(e=t={},Object.defineProperty(e,"__esModule",{value:!0}),e.FitAddon=void 0,e.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){const e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;const t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal)return;if(!this._terminal.element||!this._terminal.element.parentElement)return;const e=this._terminal._core,t=e._renderService.dimensions;if(0===t.css.cell.width||0===t.css.cell.height)return;const r=0===this._terminal.options.scrollback?0:e.viewport.scrollBarWidth,s=window.getComputedStyle(this._terminal.element.parentElement),i=parseInt(s.getPropertyValue("height")),o=Math.max(0,parseInt(s.getPropertyValue("width"))),n=window.getComputedStyle(this._terminal.element),l=i-(parseInt(n.getPropertyValue("padding-top"))+parseInt(n.getPropertyValue("padding-bottom"))),a=o-(parseInt(n.getPropertyValue("padding-right"))+parseInt(n.getPropertyValue("padding-left")))-r;return{cols:Math.max(2,Math.floor(a/t.css.cell.width)),rows:Math.max(1,Math.floor(l/t.css.cell.height))}}},t),i=r.exports.FitAddon,o=r.exports.__esModule;export{i as FitAddon,o as __esModule,s as default};
7
+ /**
8
+ * Copyright (c) 2014-2024 The xterm.js authors. All rights reserved.
9
+ * @license MIT
10
+ *
11
+ * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
12
+ * @license MIT
13
+ *
14
+ * Originally forked from (with the author's permission):
15
+ * Fabrice Bellard's javascript vt100 for jslinux:
16
+ * http://bellard.org/jslinux/
17
+ * Copyright (c) 2011 Fabrice Bellard
18
+ */
19
+ var e=class{activate(e){this._terminal=e}dispose(){}fit(){let e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;let t=this._terminal._core;(this._terminal.rows!==e.rows||this._terminal.cols!==e.cols)&&(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal||!this._terminal.element||!this._terminal.element.parentElement)return;let e=this._terminal._core._renderService.dimensions;if(0===e.css.cell.width||0===e.css.cell.height)return;let t=0===this._terminal.options.scrollback?0:this._terminal.options.overviewRuler?.width||14,r=window.getComputedStyle(this._terminal.element.parentElement),i=parseInt(r.getPropertyValue("height")),s=Math.max(0,parseInt(r.getPropertyValue("width"))),l=window.getComputedStyle(this._terminal.element),a=i-(parseInt(l.getPropertyValue("padding-top"))+parseInt(l.getPropertyValue("padding-bottom"))),n=s-(parseInt(l.getPropertyValue("padding-right"))+parseInt(l.getPropertyValue("padding-left")))-t;return{cols:Math.max(2,Math.floor(n/e.css.cell.width)),rows:Math.max(1,Math.floor(a/e.css.cell.height))}}};export{e as FitAddon};export default null;
@@ -1,7 +1,19 @@
1
1
  /**
2
2
  * Bundled by jsDelivr using Rollup v2.79.2 and Terser v5.39.0.
3
- * Original file: /npm/@xterm/addon-web-links@0.11.0/lib/addon-web-links.js
3
+ * Original file: /npm/@xterm/addon-web-links@0.12.0/lib/addon-web-links.mjs
4
4
  *
5
5
  * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
6
6
  */
7
- var e={exports:{}};self;var t=e.exports=(()=>{var e={6:(e,t)=>{function r(e){try{const t=new URL(e),r=t.password&&t.username?`${t.protocol}//${t.username}:${t.password}@${t.host}`:t.username?`${t.protocol}//${t.username}@${t.host}`:`${t.protocol}//${t.host}`;return e.toLocaleLowerCase().startsWith(r.toLocaleLowerCase())}catch(e){return!1}}Object.defineProperty(t,"__esModule",{value:!0}),t.LinkComputer=t.WebLinkProvider=void 0,t.WebLinkProvider=class{constructor(e,t,r,n={}){this._terminal=e,this._regex=t,this._handler=r,this._options=n}provideLinks(e,t){const r=n.computeLink(e,this._regex,this._terminal,this._handler);t(this._addCallbacks(r))}_addCallbacks(e){return e.map((e=>(e.leave=this._options.leave,e.hover=(t,r)=>{if(this._options.hover){const{range:n}=e;this._options.hover(t,r,n)}},e)))}};class n{static computeLink(e,t,o,s){const i=new RegExp(t.source,(t.flags||"")+"g"),[a,l]=n._getWindowedLineStrings(e-1,o),c=a.join("");let d;const p=[];for(;d=i.exec(c);){const e=d[0];if(!r(e))continue;const[t,i]=n._mapStrIdx(o,l,0,d.index),[a,c]=n._mapStrIdx(o,t,i,e.length);if(-1===t||-1===i||-1===a||-1===c)continue;const h={start:{x:i+1,y:t+1},end:{x:c,y:a+1}};p.push({range:h,text:e,activate:s})}return p}static _getWindowedLineStrings(e,t){let r,n=e,o=e,s=0,i="";const a=[];if(r=t.buffer.active.getLine(e)){const e=r.translateToString(!0);if(r.isWrapped&&" "!==e[0]){for(s=0;(r=t.buffer.active.getLine(--n))&&s<2048&&(i=r.translateToString(!0),s+=i.length,a.push(i),r.isWrapped&&-1===i.indexOf(" ")););a.reverse()}for(a.push(e),s=0;(r=t.buffer.active.getLine(++o))&&r.isWrapped&&s<2048&&(i=r.translateToString(!0),s+=i.length,a.push(i),-1===i.indexOf(" ")););}return[a,n]}static _mapStrIdx(e,t,r,n){const o=e.buffer.active,s=o.getNullCell();let i=r;for(;n;){const e=o.getLine(t);if(!e)return[-1,-1];for(let r=i;r<e.length;++r){e.getCell(r,s);const i=s.getChars();if(s.getWidth()&&(n-=i.length||1,r===e.length-1&&""===i)){const e=o.getLine(t+1);e&&e.isWrapped&&(e.getCell(0,s),2===s.getWidth()&&(n+=1))}if(n<0)return[t,r]}t++,i=0}return[t,i]}}t.LinkComputer=n}},t={};function r(n){var o=t[n];if(void 0!==o)return o.exports;var s=t[n]={exports:{}};return e[n](s,s.exports,r),s.exports}var n={};return(()=>{var e=n;Object.defineProperty(e,"__esModule",{value:!0}),e.WebLinksAddon=void 0;const t=r(6),o=/(https?|HTTPS?):[/]{2}[^\s"'!*(){}|\\\^<>`]*[^\s"':,.!?{}|\\\^~\[\]`()<>]/;function s(e,t){const r=window.open();if(r){try{r.opener=null}catch{}r.location.href=t}else console.warn("Opening link blocked as opener could not be cleared")}e.WebLinksAddon=class{constructor(e=s,t={}){this._handler=e,this._options=t}activate(e){this._terminal=e;const r=this._options,n=r.urlRegex||o;this._linkProvider=this._terminal.registerLinkProvider(new t.WebLinkProvider(this._terminal,n,this._handler,r))}dispose(){this._linkProvider?.dispose()}}})(),n})(),r=e.exports.WebLinksAddon,n=e.exports.__esModule;export{r as WebLinksAddon,n as __esModule,t as default};
7
+ /**
8
+ * Copyright (c) 2014-2024 The xterm.js authors. All rights reserved.
9
+ * @license MIT
10
+ *
11
+ * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
12
+ * @license MIT
13
+ *
14
+ * Originally forked from (with the author's permission):
15
+ * Fabrice Bellard's javascript vt100 for jslinux:
16
+ * http://bellard.org/jslinux/
17
+ * Copyright (c) 2011 Fabrice Bellard
18
+ */
19
+ var e=class{constructor(e,t,r,i={}){this._terminal=e,this._regex=t,this._handler=r,this._options=i}provideLinks(e,t){let i=r.computeLink(e,this._regex,this._terminal,this._handler);t(this._addCallbacks(i))}_addCallbacks(e){return e.map((e=>(e.leave=this._options.leave,e.hover=(t,r)=>{if(this._options.hover){let{range:i}=e;this._options.hover(t,r,i)}},e)))}};function t(e){try{let t=new URL(e),r=t.password&&t.username?`${t.protocol}//${t.username}:${t.password}@${t.host}`:t.username?`${t.protocol}//${t.username}@${t.host}`:`${t.protocol}//${t.host}`;return e.toLocaleLowerCase().startsWith(r.toLocaleLowerCase())}catch{return!1}}var r=class e{static computeLink(r,i,n,s){let o,a=new RegExp(i.source,(i.flags||"")+"g"),[l,h]=e._getWindowedLineStrings(r-1,n),c=l.join(""),d=[];for(;o=a.exec(c);){let r=o[0];if(!t(r))continue;let[i,a]=e._mapStrIdx(n,h,0,o.index),[l,c]=e._mapStrIdx(n,i,a,r.length);if(-1===i||-1===a||-1===l||-1===c)continue;let p={start:{x:a+1,y:i+1},end:{x:c,y:l+1}};d.push({range:p,text:r,activate:s})}return d}static _getWindowedLineStrings(e,t){let r,i=e,n=e,s=0,o="",a=[];if(r=t.buffer.active.getLine(e)){let e=r.translateToString(!0);if(r.isWrapped&&" "!==e[0]){for(s=0;(r=t.buffer.active.getLine(--i))&&s<2048&&(o=r.translateToString(!0),s+=o.length,a.push(o),r.isWrapped&&-1===o.indexOf(" ")););a.reverse()}for(a.push(e),s=0;(r=t.buffer.active.getLine(++n))&&r.isWrapped&&s<2048&&(o=r.translateToString(!0),s+=o.length,a.push(o),-1===o.indexOf(" ")););}return[a,i]}static _mapStrIdx(e,t,r,i){let n=e.buffer.active,s=n.getNullCell(),o=r;for(;i;){let e=n.getLine(t);if(!e)return[-1,-1];for(let r=o;r<e.length;++r){e.getCell(r,s);let o=s.getChars();if(s.getWidth()&&(i-=o.length||1,r===e.length-1&&""===o)){let e=n.getLine(t+1);e&&e.isWrapped&&(e.getCell(0,s),2===s.getWidth()&&(i+=1))}if(i<0)return[t,r]}t++,o=0}return[t,o]}},i=/(https?|HTTPS?):[/]{2}[^\s"'!*(){}|\\\^<>`]*[^\s"':,.!?{}|\\\^~\[\]`()<>]/;function n(e,t){let r=window.open();if(r){try{r.opener=null}catch{}r.location.href=t}else console.warn("Opening link blocked as opener could not be cleared")}var s=class{constructor(e=n,t={}){this._handler=e,this._options=t}activate(t){this._terminal=t;let r=this._options,n=r.urlRegex||i;this._linkProvider=this._terminal.registerLinkProvider(new e(this._terminal,n,this._handler,r))}dispose(){this._linkProvider?.dispose()}};export{s as WebLinksAddon};export default null;
package/src/core.js CHANGED
@@ -117,7 +117,7 @@ for (const [TYPE, interpreter] of TYPES) {
117
117
  else dispatch(element, TYPE, "done");
118
118
  };
119
119
 
120
- const { config, configURL, plugins, error } = configs.get(TYPE);
120
+ let { config, configURL, plugins, error } = configs.get(TYPE);
121
121
 
122
122
  // create a unique identifier when/if needed
123
123
  let id = 0;
@@ -311,13 +311,24 @@ for (const [TYPE, interpreter] of TYPES) {
311
311
 
312
312
  hooked.set(TYPE, hooks);
313
313
 
314
+ // allow offline interpreter detection via [offline] attribute
315
+ let version = offline_interpreter(config);
316
+ if (!version) {
317
+ const css = "script[type='module'][offline]";
318
+ const s = document.querySelector(css)?.src;
319
+ if (s && import.meta.url.startsWith(s.replace(/\.js$/, ""))) {
320
+ version = `./pyscript/${interpreter}/${interpreter}.mjs`;
321
+ version = offline_interpreter({ interpreter: version });
322
+ }
323
+ }
324
+
314
325
  define(TYPE, {
315
326
  config,
316
327
  configURL,
317
328
  interpreter,
318
329
  hooks,
330
+ version,
319
331
  env: `${TYPE}-script`,
320
- version: offline_interpreter(config),
321
332
  onerror(error, element) {
322
333
  errors.set(element, error);
323
334
  },
@@ -1,36 +1,105 @@
1
- # Some notes about the naming conventions and the relationship between various
2
- # similar-but-different names.
3
- #
4
- # import pyscript
5
- # this package contains the main user-facing API offered by pyscript. All
6
- # the names which are supposed be used by end users should be made
7
- # available in pyscript/__init__.py (i.e., this file)
8
- #
9
- # import _pyscript
10
- # this is an internal module implemented in JS. It is used internally by
11
- # the pyscript package, end users should not use it directly. For its
12
- # implementation, grep for `interpreter.registerJsModule("_pyscript",
13
- # ...)` in core.js
14
- #
15
- # import js
16
- # this is the JS globalThis, as exported by pyodide and/or micropython's
17
- # FFIs. As such, it contains different things in the main thread or in a
18
- # worker.
19
- #
20
- # import pyscript.magic_js
21
- # this submodule abstracts away some of the differences between the main
22
- # thread and the worker. In particular, it defines `window` and `document`
23
- # in such a way that these names work in both cases: in the main thread,
24
- # they are the "real" objects, in the worker they are proxies which work
25
- # thanks to coincident.
26
- #
27
- # from pyscript import window, document
28
- # these are just the window and document objects as defined by
29
- # pyscript.magic_js. This is the blessed way to access them from pyscript,
30
- # as it works transparently in both the main thread and worker cases.
1
+ """
2
+ This is the main `pyscript` namespace. It provides the primary Pythonic API
3
+ for users to interact with the
4
+ [browser's own API](https://developer.mozilla.org/en-US/docs/Web/API). It
5
+ includes utilities for common activities such as displaying content, handling
6
+ events, fetching resources, managing local storage, and coordinating with
7
+ web workers.
8
+
9
+ The most important names provided by this namespace can be directly imported
10
+ from `pyscript`, for example:
11
+
12
+ ```python
13
+ from pyscript import display, HTML, fetch, when, storage, WebSocket
14
+ ```
15
+
16
+ The following names are available in the `pyscript` namespace:
17
+
18
+ - `RUNNING_IN_WORKER`: Boolean indicating if the code is running in a Web
19
+ Worker.
20
+ - `PyWorker`: Class for creating Web Workers running Python code.
21
+ - `config`: Configuration object for pyscript settings.
22
+ - `current_target`: The element in the DOM that is the current target for
23
+ output.
24
+ - `document`: The standard `document` object, proxied in workers.
25
+ - `window`: The standard `window` object, proxied in workers.
26
+ - `js_import`: Function to dynamically import JS modules.
27
+ - `js_modules`: Object containing JS modules available to Python.
28
+ - `sync`: Utility for synchronizing between worker and main thread.
29
+ - `display`: Function to render Python objects in the web page.
30
+ - `HTML`: Helper class to create HTML content for display.
31
+ - `fetch`: Function to perform HTTP requests.
32
+ - `Storage`: Class representing browser storage (local/session).
33
+ - `storage`: Object to interact with browser's local storage.
34
+ - `WebSocket`: Class to create and manage WebSocket connections.
35
+ - `when`: Function to register event handlers on DOM elements.
36
+ - `Event`: Class representing user defined or DOM events.
37
+ - `py_import`: Function to lazily import Pyodide related Python modules.
38
+
39
+ If running in the main thread, the following additional names are available:
40
+
41
+ - `create_named_worker`: Function to create a named Web Worker.
42
+ - `workers`: Object to manage and interact with existing Web Workers.
43
+
44
+ All of these names are defined in the various submodules of `pyscript` and
45
+ are imported and re-exported here for convenience. Please refer to the
46
+ respective submodule documentation for more details on each component.
47
+
48
+
49
+ !!! Note
50
+ Some notes about the naming conventions and the relationship between
51
+ various similar-but-different names found within this code base.
52
+
53
+ ```python
54
+ import pyscript
55
+ ```
56
+
57
+ The `pyscript` package contains the main user-facing API offered by
58
+ PyScript. All the names which are supposed be used by end users should
59
+ be made available in `pyscript/__init__.py` (i.e., this source file).
60
+
61
+ ```python
62
+ import _pyscript
63
+ ```
64
+
65
+ The `_pyscript` module is an internal API implemented in JS. **End users
66
+ should not use it directly**. For its implementation, grep for
67
+ `interpreter.registerJsModule("_pyscript",...)` in `core.js`.
68
+
69
+ ```python
70
+ import js
71
+ ```
72
+
73
+ The `js` object is
74
+ [the JS `globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis),
75
+ as exported by Pyodide and/or Micropython's foreign function interface
76
+ (FFI). As such, it contains different things in the main thread or in a
77
+ worker, as defined by web standards.
78
+
79
+ ```python
80
+ import pyscript.context
81
+ ```
82
+
83
+ The `context` submodule abstracts away some of the differences between
84
+ the main thread and a worker. Its most important features are made
85
+ available in the root `pyscript` namespace. All other functionality is
86
+ mostly for internal PyScript use or advanced users. In particular, it
87
+ defines `window` and `document` in such a way that these names work in
88
+ both cases: in the main thread, they are the "real" objects, in a worker
89
+ they are proxies which work thanks to
90
+ [coincident](https://github.com/WebReflection/coincident).
91
+
92
+ ```python
93
+ from pyscript import window, document
94
+ ```
95
+
96
+ These are just the `window` and `document` objects as defined by
97
+ `pyscript.context`. This is the blessed way to access them from `pyscript`,
98
+ as it works transparently in both the main thread and worker cases.
99
+ """
31
100
 
32
101
  from polyscript import lazy_py_modules as py_import
33
- from pyscript.magic_js import (
102
+ from pyscript.context import (
34
103
  RUNNING_IN_WORKER,
35
104
  PyWorker,
36
105
  config,
@@ -0,0 +1,198 @@
1
+ """
2
+ Execution context management for PyScript.
3
+
4
+ This module handles the differences between running in the
5
+ [main browser thread](https://developer.mozilla.org/en-US/docs/Glossary/Main_thread)
6
+ versus running in a
7
+ [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers),
8
+ providing a consistent API regardless of the execution context.
9
+
10
+ Key features:
11
+
12
+ - Detects whether code is running in a worker or main thread. Read this via
13
+ the boolean `pyscript.context.RUNNING_IN_WORKER`.
14
+ - Parses and normalizes configuration from `polyscript.config` and adds the
15
+ Python interpreter type via the `type` key in `pyscript.context.config`.
16
+ - Provides appropriate implementations of `window`, `document`, and `sync`.
17
+ - Sets up JavaScript module import system, including a lazy `js_import`
18
+ function.
19
+ - Manages `PyWorker` creation.
20
+ - Provides access to the current display target via
21
+ `pyscript.context.display_target`.
22
+
23
+ !!! warning
24
+
25
+ These are key differences between the main thread and worker contexts:
26
+
27
+ Main thread context:
28
+
29
+ - `window` and `document` are available directly.
30
+ - `PyWorker` can be created to spawn worker threads.
31
+ - `sync` is not available (raises `NotSupported`).
32
+
33
+ Worker context:
34
+
35
+ - `window` and `document` are proxied from main thread (if SharedArrayBuffer
36
+ available).
37
+ - `PyWorker` is not available (raises `NotSupported`).
38
+ - `sync` utilities are available for main thread communication.
39
+ """
40
+
41
+ import json
42
+ import sys
43
+
44
+ import js
45
+ from polyscript import config as _polyscript_config
46
+ from polyscript import js_modules
47
+ from pyscript.util import NotSupported
48
+
49
+ RUNNING_IN_WORKER = not hasattr(js, "document")
50
+ """Detect execution context: True if running in a worker, False if main thread."""
51
+
52
+ config = json.loads(js.JSON.stringify(_polyscript_config))
53
+ """Parsed and normalized configuration."""
54
+ if isinstance(config, str):
55
+ config = {}
56
+
57
+ js_import = None
58
+ """Function to import JavaScript modules dynamically."""
59
+
60
+ window = None
61
+ """The `window` object (proxied if in a worker)."""
62
+
63
+ document = None
64
+ """The `document` object (proxied if in a worker)."""
65
+
66
+ sync = None
67
+ """Sync utilities for worker-main thread communication (only in workers)."""
68
+
69
+ # Detect and add Python interpreter type to config.
70
+ if "MicroPython" in sys.version:
71
+ config["type"] = "mpy"
72
+ else:
73
+ config["type"] = "py"
74
+
75
+
76
+ class _JSModuleProxy:
77
+ """
78
+ Proxy for JavaScript modules imported via js_modules.
79
+
80
+ This allows Python code to import JavaScript modules using Python's
81
+ import syntax:
82
+
83
+ ```python
84
+ from pyscript.js_modules lodash import debounce
85
+ ```
86
+
87
+ The proxy lazily retrieves the actual JavaScript module when accessed.
88
+ """
89
+
90
+ def __init__(self, name):
91
+ """
92
+ Create a proxy for the named JavaScript module.
93
+ """
94
+ self.name = name
95
+
96
+ def __getattr__(self, field):
97
+ """
98
+ Retrieve a JavaScript object/function from the proxied JavaScript
99
+ module via the given `field` name.
100
+ """
101
+ # Avoid Pyodide looking for non-existent special methods.
102
+ if not field.startswith("_"):
103
+ return getattr(getattr(js_modules, self.name), field)
104
+ return None
105
+
106
+
107
+ # Register all available JavaScript modules in Python's module system.
108
+ # This enables: from pyscript.js_modules.xxx import yyy
109
+ for module_name in js.Reflect.ownKeys(js_modules):
110
+ sys.modules[f"pyscript.js_modules.{module_name}"] = _JSModuleProxy(module_name)
111
+ sys.modules["pyscript.js_modules"] = js_modules
112
+
113
+
114
+ # Context-specific setup: Worker vs Main Thread.
115
+ if RUNNING_IN_WORKER:
116
+ import polyscript
117
+
118
+ # PyWorker cannot be created from within a worker.
119
+ PyWorker = NotSupported(
120
+ "pyscript.PyWorker",
121
+ "pyscript.PyWorker works only when running in the main thread",
122
+ )
123
+
124
+ # Attempt to access main thread's window and document via SharedArrayBuffer.
125
+ try:
126
+ window = polyscript.xworker.window
127
+ document = window.document
128
+ js.document = document
129
+
130
+ # Create js_import function that runs imports on the main thread.
131
+ js_import = window.Function(
132
+ "return (...urls) => Promise.all(urls.map((url) => import(url)))"
133
+ )()
134
+
135
+ except:
136
+ # SharedArrayBuffer not available - window/document cannot be proxied.
137
+ sab_error_message = (
138
+ "Unable to use `window` or `document` in worker. "
139
+ "This requires SharedArrayBuffer support. "
140
+ "See: https://docs.pyscript.net/latest/faq/#sharedarraybuffer"
141
+ )
142
+ js.console.warn(sab_error_message)
143
+ window = NotSupported("pyscript.window", sab_error_message)
144
+ document = NotSupported("pyscript.document", sab_error_message)
145
+
146
+ # Worker-specific utilities for main thread communication.
147
+ sync = polyscript.xworker.sync
148
+
149
+ def current_target():
150
+ """
151
+ Get the current output target in worker context.
152
+ """
153
+ return polyscript.target
154
+
155
+ else:
156
+ # Main thread context setup.
157
+ import _pyscript
158
+ from _pyscript import PyWorker as _PyWorker
159
+ from pyscript.ffi import to_js
160
+
161
+ js_import = _pyscript.js_import
162
+
163
+ def PyWorker(url, **options):
164
+ """
165
+ Create a Web Worker running Python code.
166
+
167
+ This spawns a new worker thread that can execute Python code
168
+ found at the `url`, independently of the main thread. The
169
+ `**options` can be used to configure the worker.
170
+
171
+ ```python
172
+ from pyscript import PyWorker
173
+
174
+
175
+ # Create a worker to run background tasks.
176
+ # (`type` MUST be either `micropython` or `pyodide`)
177
+ worker = PyWorker("./worker.py", type="micropython")
178
+ ```
179
+
180
+ PyWorker **can only be created from the main thread**, not from
181
+ within another worker.
182
+ """
183
+ return _PyWorker(url, to_js(options))
184
+
185
+ # Main thread has direct access to window and document.
186
+ window = js
187
+ document = js.document
188
+
189
+ # sync is not available in main thread (only in workers).
190
+ sync = NotSupported(
191
+ "pyscript.sync", "pyscript.sync works only when running in a worker"
192
+ )
193
+
194
+ def current_target():
195
+ """
196
+ Get the current output target in main thread context.
197
+ """
198
+ return _pyscript.target