sandboxedjs 0.1.92 → 0.1.93
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.
- package/dist/index.cjs +488 -10
- package/dist/index.js +488 -10
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -20074,6 +20074,24 @@ var wheels_default = {
|
|
|
20074
20074
|
patches: []
|
|
20075
20075
|
}
|
|
20076
20076
|
},
|
|
20077
|
+
{
|
|
20078
|
+
name: "brotli",
|
|
20079
|
+
version: "1.2.0",
|
|
20080
|
+
filename: "brotli-1.2.0-cp313-cp313-emscripten_5_0_6_wasm32.whl",
|
|
20081
|
+
sha256: "b67d1b9169ba3e0790d04b8020adb1a58f0b89f8961aca360dca5f020de07a21",
|
|
20082
|
+
abiId: "sbxabi1-c2637d04695ad927",
|
|
20083
|
+
requires: [],
|
|
20084
|
+
provenance: {
|
|
20085
|
+
sourceTreeDigest: "7b563fa2e7f20edac204cc80004cae35c844b36a83758e5957184383332d1209",
|
|
20086
|
+
recipeSha256: "09bf4f6072d637866a6b3c3deaec419dc3ff8a32a6f3b0813f57e06316bfcf39",
|
|
20087
|
+
buildTools: [
|
|
20088
|
+
"setuptools==84.0.0",
|
|
20089
|
+
"pkgconfig==1.6.0"
|
|
20090
|
+
],
|
|
20091
|
+
nativeDependencies: [],
|
|
20092
|
+
patches: []
|
|
20093
|
+
}
|
|
20094
|
+
},
|
|
20077
20095
|
{
|
|
20078
20096
|
name: "cffi",
|
|
20079
20097
|
version: "2.1.1",
|
|
@@ -20099,14 +20117,14 @@ var wheels_default = {
|
|
|
20099
20117
|
name: "cryptography",
|
|
20100
20118
|
version: "50.0.1",
|
|
20101
20119
|
filename: "cryptography-50.0.1-cp313-cp313-emscripten_5_0_6_wasm32.whl",
|
|
20102
|
-
sha256: "
|
|
20120
|
+
sha256: "10bb6568bf4368a593ea3a990ea870e57d4eeb1d3278af89bf24a8713473238e",
|
|
20103
20121
|
abiId: "sbxabi1-c2637d04695ad927",
|
|
20104
20122
|
requires: [
|
|
20105
20123
|
'cffi>=2.0.0; platform_python_implementation != "PyPy"'
|
|
20106
20124
|
],
|
|
20107
20125
|
provenance: {
|
|
20108
20126
|
sourceTreeDigest: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
20109
|
-
recipeSha256: "
|
|
20127
|
+
recipeSha256: "082169d1d317ba001e37ccfb7a40a0dafd8d3d34e42034da8590f0207ae73ea5",
|
|
20110
20128
|
buildTools: [
|
|
20111
20129
|
"setuptools==84.0.0",
|
|
20112
20130
|
"cffi==2.1.1"
|
|
@@ -20168,6 +20186,32 @@ var wheels_default = {
|
|
|
20168
20186
|
patches: []
|
|
20169
20187
|
}
|
|
20170
20188
|
},
|
|
20189
|
+
{
|
|
20190
|
+
name: "lz4",
|
|
20191
|
+
version: "4.4.5",
|
|
20192
|
+
filename: "lz4-4.4.5-cp313-cp313-emscripten_5_0_6_wasm32.whl",
|
|
20193
|
+
sha256: "f94c0ef7d84c9bf700cc86f69a28382699262e442e29d9763736afdb61141ffe",
|
|
20194
|
+
abiId: "sbxabi1-c2637d04695ad927",
|
|
20195
|
+
requires: [
|
|
20196
|
+
'pytest!=3.3.0; extra == "tests"',
|
|
20197
|
+
'psutil; extra == "tests"',
|
|
20198
|
+
'pytest-cov; extra == "tests"',
|
|
20199
|
+
'sphinx>=1.6.0; extra == "docs"',
|
|
20200
|
+
'sphinx_bootstrap_theme; extra == "docs"',
|
|
20201
|
+
'flake8; extra == "flake8"'
|
|
20202
|
+
],
|
|
20203
|
+
provenance: {
|
|
20204
|
+
sourceTreeDigest: "5f7bda9bfb026d0a30bb5e3383a4b25ae6b7f44fd6dc74950653d57f84c9d88e",
|
|
20205
|
+
recipeSha256: "80250de0ae159cd9f977bdfad059de0d192f28555225b084b42b5a180f684e0c",
|
|
20206
|
+
buildTools: [
|
|
20207
|
+
"setuptools==84.0.0",
|
|
20208
|
+
"pkgconfig==1.6.0",
|
|
20209
|
+
"setuptools-scm==10.2.3"
|
|
20210
|
+
],
|
|
20211
|
+
nativeDependencies: [],
|
|
20212
|
+
patches: []
|
|
20213
|
+
}
|
|
20214
|
+
},
|
|
20171
20215
|
{
|
|
20172
20216
|
name: "msgpack",
|
|
20173
20217
|
version: "1.1.0",
|
|
@@ -20228,16 +20272,65 @@ var wheels_default = {
|
|
|
20228
20272
|
name: "ormsgpack",
|
|
20229
20273
|
version: "1.12.2",
|
|
20230
20274
|
filename: "ormsgpack-1.12.2-cp313-cp313-emscripten_5_0_6_wasm32.whl",
|
|
20231
|
-
sha256: "
|
|
20275
|
+
sha256: "7fa4463a69aad2fe057c111642cce71c8f72af8465c22769d839415dbc15ac79",
|
|
20232
20276
|
abiId: "sbxabi1-c2637d04695ad927",
|
|
20233
20277
|
requires: [],
|
|
20234
20278
|
provenance: {
|
|
20235
|
-
sourceTreeDigest: "
|
|
20236
|
-
recipeSha256: "
|
|
20279
|
+
sourceTreeDigest: "eff3a7be3bc6de2a15e479c0e3e94905c35203fd83369124073f2df5cce46409",
|
|
20280
|
+
recipeSha256: "bd398762801fce51d35b33439f5e57037e1d4d274f25bd795683e2ad17d34b62",
|
|
20237
20281
|
buildTools: [
|
|
20238
20282
|
"setuptools==84.0.0"
|
|
20239
20283
|
],
|
|
20240
20284
|
nativeDependencies: [],
|
|
20285
|
+
patches: [
|
|
20286
|
+
"0001-pylong-asbytearray-313-signature.patch"
|
|
20287
|
+
]
|
|
20288
|
+
}
|
|
20289
|
+
},
|
|
20290
|
+
{
|
|
20291
|
+
name: "pillow",
|
|
20292
|
+
version: "12.3.0",
|
|
20293
|
+
filename: "pillow-12.3.0-cp313-cp313-emscripten_5_0_6_wasm32.whl",
|
|
20294
|
+
sha256: "7e585c71bf943029d8201283308a1b43f2be5f166d9beb36697d1f6bfba865e9",
|
|
20295
|
+
abiId: "sbxabi1-c2637d04695ad927",
|
|
20296
|
+
requires: [
|
|
20297
|
+
'furo; extra == "docs"',
|
|
20298
|
+
'olefile; extra == "docs"',
|
|
20299
|
+
'sphinx>=8.2; extra == "docs"',
|
|
20300
|
+
'sphinx-autobuild; extra == "docs"',
|
|
20301
|
+
'sphinx-copybutton; extra == "docs"',
|
|
20302
|
+
'sphinx-inline-tabs; extra == "docs"',
|
|
20303
|
+
'sphinxext-opengraph; extra == "docs"',
|
|
20304
|
+
'olefile; extra == "fpx"',
|
|
20305
|
+
'olefile; extra == "mic"',
|
|
20306
|
+
'arro3-compute; extra == "test-arrow"',
|
|
20307
|
+
'arro3-core; extra == "test-arrow"',
|
|
20308
|
+
'nanoarrow; extra == "test-arrow"',
|
|
20309
|
+
'pyarrow; extra == "test-arrow"',
|
|
20310
|
+
'coverage>=7.4.2; extra == "tests"',
|
|
20311
|
+
'defusedxml; extra == "tests"',
|
|
20312
|
+
'markdown2; extra == "tests"',
|
|
20313
|
+
'olefile; extra == "tests"',
|
|
20314
|
+
'packaging; extra == "tests"',
|
|
20315
|
+
'pytest; extra == "tests"',
|
|
20316
|
+
'pytest-cov; extra == "tests"',
|
|
20317
|
+
'pytest-timeout; extra == "tests"',
|
|
20318
|
+
'pytest-xdist; extra == "tests"',
|
|
20319
|
+
'setuptools; extra == "tests"',
|
|
20320
|
+
'trove-classifiers>=2024.10.12; extra == "tests"',
|
|
20321
|
+
'defusedxml; extra == "xmp"'
|
|
20322
|
+
],
|
|
20323
|
+
provenance: {
|
|
20324
|
+
sourceTreeDigest: "5427eff55a6a0e5f667d70527c2c3749f2c0786598903d7bbe7aaf16c4e95e31",
|
|
20325
|
+
recipeSha256: "77418b7e139e4779f2a422a06abbea27c941174b1f86597a2f3c4483bb236b7f",
|
|
20326
|
+
buildTools: [
|
|
20327
|
+
"setuptools==84.0.0",
|
|
20328
|
+
"pybind11==3.1.0"
|
|
20329
|
+
],
|
|
20330
|
+
nativeDependencies: [
|
|
20331
|
+
"z==1.3.1",
|
|
20332
|
+
"jpeg==9f"
|
|
20333
|
+
],
|
|
20241
20334
|
patches: []
|
|
20242
20335
|
}
|
|
20243
20336
|
},
|
|
@@ -20503,6 +20596,23 @@ var wheels_default = {
|
|
|
20503
20596
|
patches: []
|
|
20504
20597
|
}
|
|
20505
20598
|
},
|
|
20599
|
+
{
|
|
20600
|
+
name: "simplejson",
|
|
20601
|
+
version: "4.1.2",
|
|
20602
|
+
filename: "simplejson-4.1.2-cp313-cp313-emscripten_5_0_6_wasm32.whl",
|
|
20603
|
+
sha256: "80671c2b02beb0d429108df8cefb6f27857b34e032bb5a5f1e0f957e7bf3cab6",
|
|
20604
|
+
abiId: "sbxabi1-c2637d04695ad927",
|
|
20605
|
+
requires: [],
|
|
20606
|
+
provenance: {
|
|
20607
|
+
sourceTreeDigest: "055af71755ff30d71076fdebc6f605e66f5876e6f01350a7d6075e8679006111",
|
|
20608
|
+
recipeSha256: "e163a1f2e78f32265c3ffdbf743dab27bc9e0829f21c553cabf0bafafd7d69fd",
|
|
20609
|
+
buildTools: [
|
|
20610
|
+
"setuptools==84.0.0"
|
|
20611
|
+
],
|
|
20612
|
+
nativeDependencies: [],
|
|
20613
|
+
patches: []
|
|
20614
|
+
}
|
|
20615
|
+
},
|
|
20506
20616
|
{
|
|
20507
20617
|
name: "siphash24",
|
|
20508
20618
|
version: "1.9",
|
|
@@ -20546,6 +20656,24 @@ var wheels_default = {
|
|
|
20546
20656
|
patches: []
|
|
20547
20657
|
}
|
|
20548
20658
|
},
|
|
20659
|
+
{
|
|
20660
|
+
name: "ujson",
|
|
20661
|
+
version: "6.0.0",
|
|
20662
|
+
filename: "ujson-6.0.0-cp313-cp313-emscripten_5_0_6_wasm32.whl",
|
|
20663
|
+
sha256: "3d4fd009acb9fad7a2070b0e2e498f6257e9c0cc28f806950d8c04cbb6849c24",
|
|
20664
|
+
abiId: "sbxabi1-c2637d04695ad927",
|
|
20665
|
+
requires: [],
|
|
20666
|
+
provenance: {
|
|
20667
|
+
sourceTreeDigest: "2a0c67ecb71628cb89b46090372d11dc4cf6e91c2406260a4e544c17331a31da",
|
|
20668
|
+
recipeSha256: "e9f3fe48e14241bd38d1d20dc691413d8c646984d67ae3898f4b27d10af4bf80",
|
|
20669
|
+
buildTools: [
|
|
20670
|
+
"setuptools==84.0.0",
|
|
20671
|
+
"setuptools-scm==10.2.3"
|
|
20672
|
+
],
|
|
20673
|
+
nativeDependencies: [],
|
|
20674
|
+
patches: []
|
|
20675
|
+
}
|
|
20676
|
+
},
|
|
20549
20677
|
{
|
|
20550
20678
|
name: "uuid-utils",
|
|
20551
20679
|
version: "0.17.1",
|
|
@@ -20628,7 +20756,7 @@ var wheels_default = {
|
|
|
20628
20756
|
|
|
20629
20757
|
// package.json
|
|
20630
20758
|
var package_default = {
|
|
20631
|
-
version: "0.1.
|
|
20759
|
+
version: "0.1.93"};
|
|
20632
20760
|
|
|
20633
20761
|
// src/python/extension-abi.ts
|
|
20634
20762
|
var EXTENSION_ABI = {
|
|
@@ -22733,7 +22861,13 @@ async function serve(ctx, connection) {
|
|
|
22733
22861
|
headers: forwarded,
|
|
22734
22862
|
...request.body.length > 0 ? { body: request.body } : {}
|
|
22735
22863
|
});
|
|
22736
|
-
|
|
22864
|
+
const {
|
|
22865
|
+
"content-encoding": _encoding,
|
|
22866
|
+
"content-length": _length,
|
|
22867
|
+
"transfer-encoding": _chunked,
|
|
22868
|
+
...responseHeaders
|
|
22869
|
+
} = result.headers;
|
|
22870
|
+
writeResponse(connection, result.status, result.statusText, responseHeaders, result.body);
|
|
22737
22871
|
} catch (error) {
|
|
22738
22872
|
const message = error instanceof Error ? error.message : String(error);
|
|
22739
22873
|
try {
|
|
@@ -23032,16 +23166,33 @@ def run(main, *, loop_factory=new_event_loop, debug=None, **run_kwargs):
|
|
|
23032
23166
|
with __asyncio.Runner(loop_factory=loop_factory, debug=debug) as runner:
|
|
23033
23167
|
return runner.run(main)
|
|
23034
23168
|
`;
|
|
23035
|
-
|
|
23169
|
+
var HTTPX2_JSFETCH_VERSION = "1.0";
|
|
23170
|
+
var HTTPX2_JSFETCH_SOURCE = `"""SandboxedJs stand-in for httpx2-jsfetch.
|
|
23171
|
+
|
|
23172
|
+
The real package drives the browser fetch API through Pyodide's \`js\` module.
|
|
23173
|
+
This runtime has no \`js\` module but does have sockets, so httpx2's regular
|
|
23174
|
+
httpcore2 transports are used under the names httpx2 imports on Emscripten.
|
|
23175
|
+
"""
|
|
23176
|
+
|
|
23177
|
+
from httpx2._transports.default import (
|
|
23178
|
+
AsyncHTTPTransport as AsyncJavascriptFetchTransport,
|
|
23179
|
+
HTTPTransport as JavascriptFetchTransport,
|
|
23180
|
+
)
|
|
23181
|
+
|
|
23182
|
+
__all__ = ["AsyncJavascriptFetchTransport", "JavascriptFetchTransport"]
|
|
23183
|
+
`;
|
|
23184
|
+
function builtinDistribution(name, version, summary, files, requires = []) {
|
|
23036
23185
|
return {
|
|
23037
23186
|
version,
|
|
23187
|
+
requires,
|
|
23038
23188
|
files: {
|
|
23039
23189
|
...files,
|
|
23040
23190
|
[`${name}-${version}.dist-info/METADATA`]: `Metadata-Version: 2.1
|
|
23041
23191
|
Name: ${name}
|
|
23042
23192
|
Version: ${version}
|
|
23043
23193
|
Summary: ${summary}
|
|
23044
|
-
|
|
23194
|
+
` + requires.map((r) => `Requires-Dist: ${r}
|
|
23195
|
+
`).join(""),
|
|
23045
23196
|
[`${name}-${version}.dist-info/WHEEL`]: "Wheel-Version: 1.0\nGenerator: sandboxedjs\nRoot-Is-Purelib: true\nTag: py3-none-any\n",
|
|
23046
23197
|
[`${name}-${version}.dist-info/INSTALLER`]: "sandboxedjs\n",
|
|
23047
23198
|
[`${name}-${version}.dist-info/RECORD`]: ""
|
|
@@ -23049,6 +23200,13 @@ Summary: ${summary}
|
|
|
23049
23200
|
};
|
|
23050
23201
|
}
|
|
23051
23202
|
var BUILTINS3 = {
|
|
23203
|
+
"httpx2-jsfetch": builtinDistribution(
|
|
23204
|
+
"httpx2_jsfetch",
|
|
23205
|
+
HTTPX2_JSFETCH_VERSION,
|
|
23206
|
+
"SandboxedJs stand-in: httpx2's socket transports under the jsfetch names",
|
|
23207
|
+
{ "httpx2_jsfetch/__init__.py": HTTPX2_JSFETCH_SOURCE },
|
|
23208
|
+
["httpcore2[asyncio]", "anyio>=4.10", "truststore>=0.10"]
|
|
23209
|
+
),
|
|
23052
23210
|
greenlet: builtinDistribution(
|
|
23053
23211
|
"greenlet",
|
|
23054
23212
|
GREENLET_VERSION,
|
|
@@ -23073,7 +23231,7 @@ function builtinCandidates(name) {
|
|
|
23073
23231
|
filename: `${name}-${builtin.version}-py3-none-any.whl`,
|
|
23074
23232
|
sha256: "",
|
|
23075
23233
|
metadataUrl: null,
|
|
23076
|
-
indexedRequires:
|
|
23234
|
+
indexedRequires: builtin.requires
|
|
23077
23235
|
}];
|
|
23078
23236
|
}
|
|
23079
23237
|
function isBuiltinOnly(name) {
|
|
@@ -23969,6 +24127,36 @@ if sys.platform in ("emscripten", "wasi"):
|
|
|
23969
24127
|
|
|
23970
24128
|
sys.meta_path.insert(0, _SbxAsyncioWatcherHook())
|
|
23971
24129
|
|
|
24130
|
+
class _SbxPyodideFetchHook:
|
|
24131
|
+
"""Libraries read \`sys.platform == "emscripten"\` as "running in Pyodide"
|
|
24132
|
+
and switch to a browser-fetch backend that imports Pyodide's \`js\`
|
|
24133
|
+
module. This runtime has no \`js\` but does have sockets, so those
|
|
24134
|
+
backends are replaced by modules that leave the socket path in place.
|
|
24135
|
+
urllib3 (and so requests) imports its backend at package import, which
|
|
24136
|
+
made anything using requests fail with "No module named 'js'"."""
|
|
24137
|
+
|
|
24138
|
+
_STANDINS = {
|
|
24139
|
+
# urllib3/__init__.py calls inject_into_urllib3() on emscripten.
|
|
24140
|
+
"urllib3.contrib.emscripten": "def inject_into_urllib3():\\n pass\\n",
|
|
24141
|
+
}
|
|
24142
|
+
|
|
24143
|
+
def find_spec(self, name, path=None, target=None):
|
|
24144
|
+
source = self._STANDINS.get(name)
|
|
24145
|
+
if source is None:
|
|
24146
|
+
return None
|
|
24147
|
+
import importlib.util
|
|
24148
|
+
|
|
24149
|
+
class _Loader:
|
|
24150
|
+
def create_module(self, spec):
|
|
24151
|
+
return None
|
|
24152
|
+
|
|
24153
|
+
def exec_module(self, module):
|
|
24154
|
+
exec(compile(source, f"<sandboxedjs {name}>", "exec"), module.__dict__)
|
|
24155
|
+
|
|
24156
|
+
return importlib.util.spec_from_loader(name, _Loader(), is_package=False)
|
|
24157
|
+
|
|
24158
|
+
sys.meta_path.insert(0, _SbxPyodideFetchHook())
|
|
24159
|
+
|
|
23972
24160
|
|
|
23973
24161
|
_egress = os.environ.get("SBX_HTTP_EGRESS")
|
|
23974
24162
|
if _egress:
|
|
@@ -24050,6 +24238,255 @@ if _egress:
|
|
|
24050
24238
|
http.client.HTTPConnection = _SbxEgressConnection
|
|
24051
24239
|
http.client.HTTPSConnection = _SbxEgressTlsConnection
|
|
24052
24240
|
|
|
24241
|
+
# Everything above routes code that reaches for the standard library. The
|
|
24242
|
+
# popular HTTP clients do not: urllib3 (so requests), httpcore (so httpx)
|
|
24243
|
+
# and httpcore2 (so httpx2) open sockets themselves, and raw sockets do not
|
|
24244
|
+
# leave this container -- connect() to anything but loopback is
|
|
24245
|
+
# ENETUNREACH, and CPython's ssl cannot wrap these sockets anyway. So each
|
|
24246
|
+
# client's connection layer is replaced by one that speaks plain HTTP to
|
|
24247
|
+
# the egress endpoint with the absolute URL in the request line, which is
|
|
24248
|
+
# what the standard-library shims above already do.
|
|
24249
|
+
|
|
24250
|
+
def _sbx_egress_request(method, url, headers, body, timeout=None):
|
|
24251
|
+
"""One request through the egress. Returns (status, reason, headers, body)."""
|
|
24252
|
+
connection = _RealHTTPConnection(_egress_host, _egress_port, timeout=timeout)
|
|
24253
|
+
try:
|
|
24254
|
+
connection.request(method, url, body, {k: v for k, v in headers if k.lower() != "host"})
|
|
24255
|
+
response = connection.getresponse()
|
|
24256
|
+
payload = response.read()
|
|
24257
|
+
if response.getheader("x-sbx-error"):
|
|
24258
|
+
raise OSError(payload.decode("utf-8", "replace").strip())
|
|
24259
|
+
return response.status, response.reason, response.getheaders(), payload
|
|
24260
|
+
finally:
|
|
24261
|
+
connection.close()
|
|
24262
|
+
|
|
24263
|
+
async def _sbx_egress_request_async(method, url, headers, body):
|
|
24264
|
+
"""The same hop as _sbx_egress_request, over asyncio streams."""
|
|
24265
|
+
import asyncio
|
|
24266
|
+
reader, writer = await asyncio.open_connection(_egress_host, _egress_port)
|
|
24267
|
+
try:
|
|
24268
|
+
payload = body or b""
|
|
24269
|
+
if isinstance(payload, str):
|
|
24270
|
+
payload = payload.encode()
|
|
24271
|
+
lines = [f"{method} {url} HTTP/1.1", f"Host: {_egress_host}:{_egress_port}"]
|
|
24272
|
+
for key, value in headers:
|
|
24273
|
+
if key.lower() in ("host", "connection", "content-length", "transfer-encoding"):
|
|
24274
|
+
continue
|
|
24275
|
+
lines.append(f"{key}: {value}")
|
|
24276
|
+
lines.append(f"Content-Length: {len(payload)}")
|
|
24277
|
+
# Read to EOF rather than tracking keep-alive framing: one request
|
|
24278
|
+
# per connection, which is what this hop is for.
|
|
24279
|
+
lines.append("Connection: close")
|
|
24280
|
+
writer.write(("\\r\\n".join(lines) + "\\r\\n\\r\\n").encode("latin-1") + payload)
|
|
24281
|
+
await writer.drain()
|
|
24282
|
+
|
|
24283
|
+
head = await reader.readuntil(b"\\r\\n\\r\\n")
|
|
24284
|
+
start, _, rest = head.decode("latin-1").partition("\\r\\n")
|
|
24285
|
+
_, _, status_text = start.partition(" ")
|
|
24286
|
+
code, _, reason = status_text.partition(" ")
|
|
24287
|
+
received = []
|
|
24288
|
+
for line in rest.split("\\r\\n"):
|
|
24289
|
+
key, sep, value = line.partition(":")
|
|
24290
|
+
if sep:
|
|
24291
|
+
received.append((key.strip(), value.strip()))
|
|
24292
|
+
content = dict((k.lower(), v) for k, v in received)
|
|
24293
|
+
if "content-length" in content:
|
|
24294
|
+
data = await reader.readexactly(int(content["content-length"]))
|
|
24295
|
+
else:
|
|
24296
|
+
data = await reader.read()
|
|
24297
|
+
if content.get("x-sbx-error"):
|
|
24298
|
+
raise OSError(data.decode("utf-8", "replace").strip())
|
|
24299
|
+
return int(code), reason, received, data
|
|
24300
|
+
finally:
|
|
24301
|
+
writer.close()
|
|
24302
|
+
|
|
24303
|
+
class _SbxUrllib3Hook:
|
|
24304
|
+
"""urllib3's emscripten backend is Pyodide's \`js\` fetch, which this
|
|
24305
|
+
runtime has no module for; its socket backend cannot leave the
|
|
24306
|
+
container. Both connection classes become egress ones."""
|
|
24307
|
+
|
|
24308
|
+
def find_spec(self, name, path=None, target=None):
|
|
24309
|
+
if name != "urllib3.contrib.emscripten":
|
|
24310
|
+
return None
|
|
24311
|
+
import importlib.util
|
|
24312
|
+
|
|
24313
|
+
def inject_into_urllib3():
|
|
24314
|
+
import urllib3.connection
|
|
24315
|
+
from urllib3.connectionpool import HTTPConnectionPool, HTTPSConnectionPool
|
|
24316
|
+
|
|
24317
|
+
class _Plain(urllib3.connection.HTTPConnection):
|
|
24318
|
+
"""urllib3's own connection, dialled at the egress.
|
|
24319
|
+
|
|
24320
|
+
urllib3.connection.HTTPConnection still derives from
|
|
24321
|
+
http.client.HTTPConnection, so request building is
|
|
24322
|
+
inherited; only where it connects and what goes in the
|
|
24323
|
+
request line change.
|
|
24324
|
+
"""
|
|
24325
|
+
|
|
24326
|
+
_sbx_scheme = "http"
|
|
24327
|
+
default_port = 80
|
|
24328
|
+
is_verified = True
|
|
24329
|
+
proxy_is_verified = None
|
|
24330
|
+
|
|
24331
|
+
def __init__(self, host, port=None, **kwargs):
|
|
24332
|
+
# A distinct name from the http.client shim's
|
|
24333
|
+
# _sbx_origin: that class is further up this MRO and
|
|
24334
|
+
# its __init__ runs after this one, so sharing the name
|
|
24335
|
+
# overwrote the real destination with the egress and
|
|
24336
|
+
# every request went out addressed to the egress itself.
|
|
24337
|
+
self._sbx_target = (host, port or self.default_port)
|
|
24338
|
+
for unsupported in ("ssl_context", "ssl_version", "ssl_minimum_version",
|
|
24339
|
+
"ssl_maximum_version", "assert_hostname",
|
|
24340
|
+
"assert_fingerprint", "server_hostname", "ca_certs",
|
|
24341
|
+
"ca_cert_dir", "ca_cert_data", "cert_file", "key_file",
|
|
24342
|
+
"key_password", "cert_reqs"):
|
|
24343
|
+
kwargs.pop(unsupported, None)
|
|
24344
|
+
super().__init__(_egress_host, port=_egress_port, **kwargs)
|
|
24345
|
+
|
|
24346
|
+
def _sbx_absolute(self, url):
|
|
24347
|
+
host, port = self._sbx_target
|
|
24348
|
+
if url.startswith("http://") or url.startswith("https://"):
|
|
24349
|
+
return url
|
|
24350
|
+
netloc = host if port == self.default_port else f"{host}:{port}"
|
|
24351
|
+
return f"{self._sbx_scheme}://{netloc}{url}"
|
|
24352
|
+
|
|
24353
|
+
# urllib3 v2 writes the request line in its own request()
|
|
24354
|
+
# rather than through http.client's putrequest, so both
|
|
24355
|
+
# are absolutised; the egress refuses an origin-form target.
|
|
24356
|
+
def request(self, method, url, *args, **kwargs):
|
|
24357
|
+
return super().request(method, self._sbx_absolute(url), *args, **kwargs)
|
|
24358
|
+
|
|
24359
|
+
def putrequest(self, method, url, *args, **kwargs):
|
|
24360
|
+
return super().putrequest(method, self._sbx_absolute(url), *args, **kwargs)
|
|
24361
|
+
|
|
24362
|
+
def set_tunnel(self, *args, **kwargs):
|
|
24363
|
+
raise OSError("this container reaches the network through its egress, not CONNECT")
|
|
24364
|
+
|
|
24365
|
+
class _Tls(_Plain):
|
|
24366
|
+
_sbx_scheme = "https"
|
|
24367
|
+
default_port = 443
|
|
24368
|
+
|
|
24369
|
+
HTTPConnectionPool.ConnectionCls = _Plain
|
|
24370
|
+
HTTPSConnectionPool.ConnectionCls = _Tls
|
|
24371
|
+
urllib3.connection.HTTPConnection = _Plain
|
|
24372
|
+
urllib3.connection.HTTPSConnection = _Tls
|
|
24373
|
+
|
|
24374
|
+
class _Loader:
|
|
24375
|
+
def create_module(self, spec):
|
|
24376
|
+
return None
|
|
24377
|
+
|
|
24378
|
+
def exec_module(self, module):
|
|
24379
|
+
module.inject_into_urllib3 = inject_into_urllib3
|
|
24380
|
+
|
|
24381
|
+
return importlib.util.spec_from_loader(name, _Loader(), is_package=True)
|
|
24382
|
+
|
|
24383
|
+
sys.meta_path.insert(0, _SbxUrllib3Hook())
|
|
24384
|
+
|
|
24385
|
+
def _sbx_httpx_transports(httpx_module):
|
|
24386
|
+
"""Sync and async httpx transports that send through the egress."""
|
|
24387
|
+
|
|
24388
|
+
class _Transport(httpx_module.BaseTransport):
|
|
24389
|
+
def __init__(self, *args, **kwargs):
|
|
24390
|
+
pass
|
|
24391
|
+
|
|
24392
|
+
def handle_request(self, request):
|
|
24393
|
+
body = request.read()
|
|
24394
|
+
sent = [(k.decode("latin-1"), v.decode("latin-1")) for k, v in request.headers.raw]
|
|
24395
|
+
status, reason, headers, payload = _sbx_egress_request(
|
|
24396
|
+
request.method, str(request.url), sent, body,
|
|
24397
|
+
)
|
|
24398
|
+
# Content-Length/Transfer-Encoding describe the egress hop's
|
|
24399
|
+
# framing, and httpx re-derives them from the body it is given.
|
|
24400
|
+
keep = [(k, v) for k, v in headers
|
|
24401
|
+
if k.lower() not in ("content-length", "transfer-encoding", "content-encoding")]
|
|
24402
|
+
return httpx_module.Response(
|
|
24403
|
+
status, headers=keep, content=payload, request=request,
|
|
24404
|
+
extensions={"reason_phrase": (reason or "").encode()},
|
|
24405
|
+
)
|
|
24406
|
+
|
|
24407
|
+
def close(self):
|
|
24408
|
+
pass
|
|
24409
|
+
|
|
24410
|
+
def __enter__(self):
|
|
24411
|
+
return self
|
|
24412
|
+
|
|
24413
|
+
def __exit__(self, *args):
|
|
24414
|
+
pass
|
|
24415
|
+
|
|
24416
|
+
class _AsyncTransport(httpx_module.AsyncBaseTransport):
|
|
24417
|
+
def __init__(self, *args, **kwargs):
|
|
24418
|
+
pass
|
|
24419
|
+
|
|
24420
|
+
async def handle_async_request(self, request):
|
|
24421
|
+
# Not the blocking client on the loop thread: this image
|
|
24422
|
+
# proxies every syscall to one thread, so a blocking recv
|
|
24423
|
+
# inside a coroutine stops the loop that must service it. The
|
|
24424
|
+
# egress is loopback, which asyncio streams reach.
|
|
24425
|
+
import asyncio
|
|
24426
|
+
body = await request.aread()
|
|
24427
|
+
sent = [(k.decode("latin-1"), v.decode("latin-1")) for k, v in request.headers.raw]
|
|
24428
|
+
status, reason, headers, payload = await _sbx_egress_request_async(
|
|
24429
|
+
request.method, str(request.url), sent, body,
|
|
24430
|
+
)
|
|
24431
|
+
keep = [(k, v) for k, v in headers
|
|
24432
|
+
if k.lower() not in ("content-length", "transfer-encoding", "content-encoding")]
|
|
24433
|
+
return httpx_module.Response(
|
|
24434
|
+
status, headers=keep, content=payload, request=request,
|
|
24435
|
+
extensions={"reason_phrase": (reason or "").encode()},
|
|
24436
|
+
)
|
|
24437
|
+
|
|
24438
|
+
async def aclose(self):
|
|
24439
|
+
pass
|
|
24440
|
+
|
|
24441
|
+
async def __aenter__(self):
|
|
24442
|
+
return self
|
|
24443
|
+
|
|
24444
|
+
async def __aexit__(self, *args):
|
|
24445
|
+
pass
|
|
24446
|
+
|
|
24447
|
+
return _Transport, _AsyncTransport
|
|
24448
|
+
|
|
24449
|
+
class _SbxHttpxHook:
|
|
24450
|
+
"""httpx and httpx2 build their default transports on httpcore, which
|
|
24451
|
+
opens sockets. Their default transport classes are replaced as the
|
|
24452
|
+
module loads, so \`httpx.get(...)\` and every client built from the
|
|
24453
|
+
defaults leave through the egress."""
|
|
24454
|
+
|
|
24455
|
+
_PACKAGES = {"httpx": "httpx", "httpx2": "httpx2"}
|
|
24456
|
+
|
|
24457
|
+
def find_spec(self, name, path=None, target=None):
|
|
24458
|
+
package = self._PACKAGES.get(name)
|
|
24459
|
+
if package is None or getattr(self, "_loading", None) == name:
|
|
24460
|
+
return None
|
|
24461
|
+
import importlib.machinery
|
|
24462
|
+
spec = importlib.machinery.PathFinder.find_spec(name, path)
|
|
24463
|
+
if spec is None or spec.loader is None:
|
|
24464
|
+
return spec
|
|
24465
|
+
load = spec.loader.exec_module
|
|
24466
|
+
|
|
24467
|
+
def exec_module(module):
|
|
24468
|
+
self._loading = name
|
|
24469
|
+
try:
|
|
24470
|
+
load(module)
|
|
24471
|
+
finally:
|
|
24472
|
+
self._loading = None
|
|
24473
|
+
transport, async_transport = _sbx_httpx_transports(module)
|
|
24474
|
+
module.HTTPTransport = transport
|
|
24475
|
+
module.AsyncHTTPTransport = async_transport
|
|
24476
|
+
defaults = sys.modules.get(f"{package}._transports.default")
|
|
24477
|
+
if defaults is not None:
|
|
24478
|
+
defaults.HTTPTransport = transport
|
|
24479
|
+
defaults.AsyncHTTPTransport = async_transport
|
|
24480
|
+
client = sys.modules.get(f"{package}._client")
|
|
24481
|
+
if client is not None:
|
|
24482
|
+
client.HTTPTransport = transport
|
|
24483
|
+
client.AsyncHTTPTransport = async_transport
|
|
24484
|
+
|
|
24485
|
+
spec.loader.exec_module = exec_module
|
|
24486
|
+
return spec
|
|
24487
|
+
|
|
24488
|
+
sys.meta_path.insert(0, _SbxHttpxHook())
|
|
24489
|
+
|
|
24053
24490
|
|
|
24054
24491
|
if os.environ.get("SBX_SERIAL_HOST_CALLS") == "1":
|
|
24055
24492
|
import asyncio
|
|
@@ -24078,6 +24515,47 @@ if os.environ.get("SBX_SERIAL_HOST_CALLS") == "1":
|
|
|
24078
24515
|
)
|
|
24079
24516
|
|
|
24080
24517
|
asyncio.to_thread = _to_thread
|
|
24518
|
+
|
|
24519
|
+
class _SbxAnyioThreadHook:
|
|
24520
|
+
"""anyio's thread offload, run inline.
|
|
24521
|
+
|
|
24522
|
+
anyio has its own worker threads, so it never reaches asyncio.to_thread
|
|
24523
|
+
and the refusal above -- it deadlocks instead, for the reason described
|
|
24524
|
+
there. Starlette sends every *synchronous* endpoint through
|
|
24525
|
+
anyio.to_thread.run_sync, so a plain def FastAPI route hung forever
|
|
24526
|
+
while an async def one answered.
|
|
24527
|
+
|
|
24528
|
+
Running the function on the loop thread keeps such an endpoint working.
|
|
24529
|
+
The cost is real and worth stating: a synchronous handler blocks the
|
|
24530
|
+
loop for its duration, so it does not overlap with other requests.
|
|
24531
|
+
"""
|
|
24532
|
+
|
|
24533
|
+
def find_spec(self, name, path=None, target=None):
|
|
24534
|
+
if name != "anyio.to_thread":
|
|
24535
|
+
return None
|
|
24536
|
+
import importlib.machinery
|
|
24537
|
+
spec = importlib.machinery.PathFinder.find_spec(name, path)
|
|
24538
|
+
if spec is None or spec.loader is None:
|
|
24539
|
+
return spec
|
|
24540
|
+
load = spec.loader.exec_module
|
|
24541
|
+
|
|
24542
|
+
def exec_module(module):
|
|
24543
|
+
load(module)
|
|
24544
|
+
|
|
24545
|
+
async def run_sync(func, *args, abandon_on_cancel=False,
|
|
24546
|
+
cancellable=None, limiter=None):
|
|
24547
|
+
return func(*args)
|
|
24548
|
+
|
|
24549
|
+
async def run_sync_in_worker_thread(func, *args, **kwargs):
|
|
24550
|
+
return func(*args)
|
|
24551
|
+
|
|
24552
|
+
module.run_sync = run_sync
|
|
24553
|
+
module.run_sync_in_worker_thread = run_sync_in_worker_thread
|
|
24554
|
+
|
|
24555
|
+
spec.loader.exec_module = exec_module
|
|
24556
|
+
return spec
|
|
24557
|
+
|
|
24558
|
+
sys.meta_path.insert(0, _SbxAnyioThreadHook())
|
|
24081
24559
|
`;
|
|
24082
24560
|
function ensureProcessShims(ctx) {
|
|
24083
24561
|
const hook = `${SITE_PACKAGES}/sitecustomize.py`;
|
package/dist/index.js
CHANGED
|
@@ -20057,6 +20057,24 @@ var wheels_default = {
|
|
|
20057
20057
|
patches: []
|
|
20058
20058
|
}
|
|
20059
20059
|
},
|
|
20060
|
+
{
|
|
20061
|
+
name: "brotli",
|
|
20062
|
+
version: "1.2.0",
|
|
20063
|
+
filename: "brotli-1.2.0-cp313-cp313-emscripten_5_0_6_wasm32.whl",
|
|
20064
|
+
sha256: "b67d1b9169ba3e0790d04b8020adb1a58f0b89f8961aca360dca5f020de07a21",
|
|
20065
|
+
abiId: "sbxabi1-c2637d04695ad927",
|
|
20066
|
+
requires: [],
|
|
20067
|
+
provenance: {
|
|
20068
|
+
sourceTreeDigest: "7b563fa2e7f20edac204cc80004cae35c844b36a83758e5957184383332d1209",
|
|
20069
|
+
recipeSha256: "09bf4f6072d637866a6b3c3deaec419dc3ff8a32a6f3b0813f57e06316bfcf39",
|
|
20070
|
+
buildTools: [
|
|
20071
|
+
"setuptools==84.0.0",
|
|
20072
|
+
"pkgconfig==1.6.0"
|
|
20073
|
+
],
|
|
20074
|
+
nativeDependencies: [],
|
|
20075
|
+
patches: []
|
|
20076
|
+
}
|
|
20077
|
+
},
|
|
20060
20078
|
{
|
|
20061
20079
|
name: "cffi",
|
|
20062
20080
|
version: "2.1.1",
|
|
@@ -20082,14 +20100,14 @@ var wheels_default = {
|
|
|
20082
20100
|
name: "cryptography",
|
|
20083
20101
|
version: "50.0.1",
|
|
20084
20102
|
filename: "cryptography-50.0.1-cp313-cp313-emscripten_5_0_6_wasm32.whl",
|
|
20085
|
-
sha256: "
|
|
20103
|
+
sha256: "10bb6568bf4368a593ea3a990ea870e57d4eeb1d3278af89bf24a8713473238e",
|
|
20086
20104
|
abiId: "sbxabi1-c2637d04695ad927",
|
|
20087
20105
|
requires: [
|
|
20088
20106
|
'cffi>=2.0.0; platform_python_implementation != "PyPy"'
|
|
20089
20107
|
],
|
|
20090
20108
|
provenance: {
|
|
20091
20109
|
sourceTreeDigest: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
20092
|
-
recipeSha256: "
|
|
20110
|
+
recipeSha256: "082169d1d317ba001e37ccfb7a40a0dafd8d3d34e42034da8590f0207ae73ea5",
|
|
20093
20111
|
buildTools: [
|
|
20094
20112
|
"setuptools==84.0.0",
|
|
20095
20113
|
"cffi==2.1.1"
|
|
@@ -20151,6 +20169,32 @@ var wheels_default = {
|
|
|
20151
20169
|
patches: []
|
|
20152
20170
|
}
|
|
20153
20171
|
},
|
|
20172
|
+
{
|
|
20173
|
+
name: "lz4",
|
|
20174
|
+
version: "4.4.5",
|
|
20175
|
+
filename: "lz4-4.4.5-cp313-cp313-emscripten_5_0_6_wasm32.whl",
|
|
20176
|
+
sha256: "f94c0ef7d84c9bf700cc86f69a28382699262e442e29d9763736afdb61141ffe",
|
|
20177
|
+
abiId: "sbxabi1-c2637d04695ad927",
|
|
20178
|
+
requires: [
|
|
20179
|
+
'pytest!=3.3.0; extra == "tests"',
|
|
20180
|
+
'psutil; extra == "tests"',
|
|
20181
|
+
'pytest-cov; extra == "tests"',
|
|
20182
|
+
'sphinx>=1.6.0; extra == "docs"',
|
|
20183
|
+
'sphinx_bootstrap_theme; extra == "docs"',
|
|
20184
|
+
'flake8; extra == "flake8"'
|
|
20185
|
+
],
|
|
20186
|
+
provenance: {
|
|
20187
|
+
sourceTreeDigest: "5f7bda9bfb026d0a30bb5e3383a4b25ae6b7f44fd6dc74950653d57f84c9d88e",
|
|
20188
|
+
recipeSha256: "80250de0ae159cd9f977bdfad059de0d192f28555225b084b42b5a180f684e0c",
|
|
20189
|
+
buildTools: [
|
|
20190
|
+
"setuptools==84.0.0",
|
|
20191
|
+
"pkgconfig==1.6.0",
|
|
20192
|
+
"setuptools-scm==10.2.3"
|
|
20193
|
+
],
|
|
20194
|
+
nativeDependencies: [],
|
|
20195
|
+
patches: []
|
|
20196
|
+
}
|
|
20197
|
+
},
|
|
20154
20198
|
{
|
|
20155
20199
|
name: "msgpack",
|
|
20156
20200
|
version: "1.1.0",
|
|
@@ -20211,16 +20255,65 @@ var wheels_default = {
|
|
|
20211
20255
|
name: "ormsgpack",
|
|
20212
20256
|
version: "1.12.2",
|
|
20213
20257
|
filename: "ormsgpack-1.12.2-cp313-cp313-emscripten_5_0_6_wasm32.whl",
|
|
20214
|
-
sha256: "
|
|
20258
|
+
sha256: "7fa4463a69aad2fe057c111642cce71c8f72af8465c22769d839415dbc15ac79",
|
|
20215
20259
|
abiId: "sbxabi1-c2637d04695ad927",
|
|
20216
20260
|
requires: [],
|
|
20217
20261
|
provenance: {
|
|
20218
|
-
sourceTreeDigest: "
|
|
20219
|
-
recipeSha256: "
|
|
20262
|
+
sourceTreeDigest: "eff3a7be3bc6de2a15e479c0e3e94905c35203fd83369124073f2df5cce46409",
|
|
20263
|
+
recipeSha256: "bd398762801fce51d35b33439f5e57037e1d4d274f25bd795683e2ad17d34b62",
|
|
20220
20264
|
buildTools: [
|
|
20221
20265
|
"setuptools==84.0.0"
|
|
20222
20266
|
],
|
|
20223
20267
|
nativeDependencies: [],
|
|
20268
|
+
patches: [
|
|
20269
|
+
"0001-pylong-asbytearray-313-signature.patch"
|
|
20270
|
+
]
|
|
20271
|
+
}
|
|
20272
|
+
},
|
|
20273
|
+
{
|
|
20274
|
+
name: "pillow",
|
|
20275
|
+
version: "12.3.0",
|
|
20276
|
+
filename: "pillow-12.3.0-cp313-cp313-emscripten_5_0_6_wasm32.whl",
|
|
20277
|
+
sha256: "7e585c71bf943029d8201283308a1b43f2be5f166d9beb36697d1f6bfba865e9",
|
|
20278
|
+
abiId: "sbxabi1-c2637d04695ad927",
|
|
20279
|
+
requires: [
|
|
20280
|
+
'furo; extra == "docs"',
|
|
20281
|
+
'olefile; extra == "docs"',
|
|
20282
|
+
'sphinx>=8.2; extra == "docs"',
|
|
20283
|
+
'sphinx-autobuild; extra == "docs"',
|
|
20284
|
+
'sphinx-copybutton; extra == "docs"',
|
|
20285
|
+
'sphinx-inline-tabs; extra == "docs"',
|
|
20286
|
+
'sphinxext-opengraph; extra == "docs"',
|
|
20287
|
+
'olefile; extra == "fpx"',
|
|
20288
|
+
'olefile; extra == "mic"',
|
|
20289
|
+
'arro3-compute; extra == "test-arrow"',
|
|
20290
|
+
'arro3-core; extra == "test-arrow"',
|
|
20291
|
+
'nanoarrow; extra == "test-arrow"',
|
|
20292
|
+
'pyarrow; extra == "test-arrow"',
|
|
20293
|
+
'coverage>=7.4.2; extra == "tests"',
|
|
20294
|
+
'defusedxml; extra == "tests"',
|
|
20295
|
+
'markdown2; extra == "tests"',
|
|
20296
|
+
'olefile; extra == "tests"',
|
|
20297
|
+
'packaging; extra == "tests"',
|
|
20298
|
+
'pytest; extra == "tests"',
|
|
20299
|
+
'pytest-cov; extra == "tests"',
|
|
20300
|
+
'pytest-timeout; extra == "tests"',
|
|
20301
|
+
'pytest-xdist; extra == "tests"',
|
|
20302
|
+
'setuptools; extra == "tests"',
|
|
20303
|
+
'trove-classifiers>=2024.10.12; extra == "tests"',
|
|
20304
|
+
'defusedxml; extra == "xmp"'
|
|
20305
|
+
],
|
|
20306
|
+
provenance: {
|
|
20307
|
+
sourceTreeDigest: "5427eff55a6a0e5f667d70527c2c3749f2c0786598903d7bbe7aaf16c4e95e31",
|
|
20308
|
+
recipeSha256: "77418b7e139e4779f2a422a06abbea27c941174b1f86597a2f3c4483bb236b7f",
|
|
20309
|
+
buildTools: [
|
|
20310
|
+
"setuptools==84.0.0",
|
|
20311
|
+
"pybind11==3.1.0"
|
|
20312
|
+
],
|
|
20313
|
+
nativeDependencies: [
|
|
20314
|
+
"z==1.3.1",
|
|
20315
|
+
"jpeg==9f"
|
|
20316
|
+
],
|
|
20224
20317
|
patches: []
|
|
20225
20318
|
}
|
|
20226
20319
|
},
|
|
@@ -20486,6 +20579,23 @@ var wheels_default = {
|
|
|
20486
20579
|
patches: []
|
|
20487
20580
|
}
|
|
20488
20581
|
},
|
|
20582
|
+
{
|
|
20583
|
+
name: "simplejson",
|
|
20584
|
+
version: "4.1.2",
|
|
20585
|
+
filename: "simplejson-4.1.2-cp313-cp313-emscripten_5_0_6_wasm32.whl",
|
|
20586
|
+
sha256: "80671c2b02beb0d429108df8cefb6f27857b34e032bb5a5f1e0f957e7bf3cab6",
|
|
20587
|
+
abiId: "sbxabi1-c2637d04695ad927",
|
|
20588
|
+
requires: [],
|
|
20589
|
+
provenance: {
|
|
20590
|
+
sourceTreeDigest: "055af71755ff30d71076fdebc6f605e66f5876e6f01350a7d6075e8679006111",
|
|
20591
|
+
recipeSha256: "e163a1f2e78f32265c3ffdbf743dab27bc9e0829f21c553cabf0bafafd7d69fd",
|
|
20592
|
+
buildTools: [
|
|
20593
|
+
"setuptools==84.0.0"
|
|
20594
|
+
],
|
|
20595
|
+
nativeDependencies: [],
|
|
20596
|
+
patches: []
|
|
20597
|
+
}
|
|
20598
|
+
},
|
|
20489
20599
|
{
|
|
20490
20600
|
name: "siphash24",
|
|
20491
20601
|
version: "1.9",
|
|
@@ -20529,6 +20639,24 @@ var wheels_default = {
|
|
|
20529
20639
|
patches: []
|
|
20530
20640
|
}
|
|
20531
20641
|
},
|
|
20642
|
+
{
|
|
20643
|
+
name: "ujson",
|
|
20644
|
+
version: "6.0.0",
|
|
20645
|
+
filename: "ujson-6.0.0-cp313-cp313-emscripten_5_0_6_wasm32.whl",
|
|
20646
|
+
sha256: "3d4fd009acb9fad7a2070b0e2e498f6257e9c0cc28f806950d8c04cbb6849c24",
|
|
20647
|
+
abiId: "sbxabi1-c2637d04695ad927",
|
|
20648
|
+
requires: [],
|
|
20649
|
+
provenance: {
|
|
20650
|
+
sourceTreeDigest: "2a0c67ecb71628cb89b46090372d11dc4cf6e91c2406260a4e544c17331a31da",
|
|
20651
|
+
recipeSha256: "e9f3fe48e14241bd38d1d20dc691413d8c646984d67ae3898f4b27d10af4bf80",
|
|
20652
|
+
buildTools: [
|
|
20653
|
+
"setuptools==84.0.0",
|
|
20654
|
+
"setuptools-scm==10.2.3"
|
|
20655
|
+
],
|
|
20656
|
+
nativeDependencies: [],
|
|
20657
|
+
patches: []
|
|
20658
|
+
}
|
|
20659
|
+
},
|
|
20532
20660
|
{
|
|
20533
20661
|
name: "uuid-utils",
|
|
20534
20662
|
version: "0.17.1",
|
|
@@ -20611,7 +20739,7 @@ var wheels_default = {
|
|
|
20611
20739
|
|
|
20612
20740
|
// package.json
|
|
20613
20741
|
var package_default = {
|
|
20614
|
-
version: "0.1.
|
|
20742
|
+
version: "0.1.93"};
|
|
20615
20743
|
|
|
20616
20744
|
// src/python/extension-abi.ts
|
|
20617
20745
|
var EXTENSION_ABI = {
|
|
@@ -22716,7 +22844,13 @@ async function serve(ctx, connection) {
|
|
|
22716
22844
|
headers: forwarded,
|
|
22717
22845
|
...request.body.length > 0 ? { body: request.body } : {}
|
|
22718
22846
|
});
|
|
22719
|
-
|
|
22847
|
+
const {
|
|
22848
|
+
"content-encoding": _encoding,
|
|
22849
|
+
"content-length": _length,
|
|
22850
|
+
"transfer-encoding": _chunked,
|
|
22851
|
+
...responseHeaders
|
|
22852
|
+
} = result.headers;
|
|
22853
|
+
writeResponse(connection, result.status, result.statusText, responseHeaders, result.body);
|
|
22720
22854
|
} catch (error) {
|
|
22721
22855
|
const message = error instanceof Error ? error.message : String(error);
|
|
22722
22856
|
try {
|
|
@@ -23015,16 +23149,33 @@ def run(main, *, loop_factory=new_event_loop, debug=None, **run_kwargs):
|
|
|
23015
23149
|
with __asyncio.Runner(loop_factory=loop_factory, debug=debug) as runner:
|
|
23016
23150
|
return runner.run(main)
|
|
23017
23151
|
`;
|
|
23018
|
-
|
|
23152
|
+
var HTTPX2_JSFETCH_VERSION = "1.0";
|
|
23153
|
+
var HTTPX2_JSFETCH_SOURCE = `"""SandboxedJs stand-in for httpx2-jsfetch.
|
|
23154
|
+
|
|
23155
|
+
The real package drives the browser fetch API through Pyodide's \`js\` module.
|
|
23156
|
+
This runtime has no \`js\` module but does have sockets, so httpx2's regular
|
|
23157
|
+
httpcore2 transports are used under the names httpx2 imports on Emscripten.
|
|
23158
|
+
"""
|
|
23159
|
+
|
|
23160
|
+
from httpx2._transports.default import (
|
|
23161
|
+
AsyncHTTPTransport as AsyncJavascriptFetchTransport,
|
|
23162
|
+
HTTPTransport as JavascriptFetchTransport,
|
|
23163
|
+
)
|
|
23164
|
+
|
|
23165
|
+
__all__ = ["AsyncJavascriptFetchTransport", "JavascriptFetchTransport"]
|
|
23166
|
+
`;
|
|
23167
|
+
function builtinDistribution(name, version, summary, files, requires = []) {
|
|
23019
23168
|
return {
|
|
23020
23169
|
version,
|
|
23170
|
+
requires,
|
|
23021
23171
|
files: {
|
|
23022
23172
|
...files,
|
|
23023
23173
|
[`${name}-${version}.dist-info/METADATA`]: `Metadata-Version: 2.1
|
|
23024
23174
|
Name: ${name}
|
|
23025
23175
|
Version: ${version}
|
|
23026
23176
|
Summary: ${summary}
|
|
23027
|
-
|
|
23177
|
+
` + requires.map((r) => `Requires-Dist: ${r}
|
|
23178
|
+
`).join(""),
|
|
23028
23179
|
[`${name}-${version}.dist-info/WHEEL`]: "Wheel-Version: 1.0\nGenerator: sandboxedjs\nRoot-Is-Purelib: true\nTag: py3-none-any\n",
|
|
23029
23180
|
[`${name}-${version}.dist-info/INSTALLER`]: "sandboxedjs\n",
|
|
23030
23181
|
[`${name}-${version}.dist-info/RECORD`]: ""
|
|
@@ -23032,6 +23183,13 @@ Summary: ${summary}
|
|
|
23032
23183
|
};
|
|
23033
23184
|
}
|
|
23034
23185
|
var BUILTINS3 = {
|
|
23186
|
+
"httpx2-jsfetch": builtinDistribution(
|
|
23187
|
+
"httpx2_jsfetch",
|
|
23188
|
+
HTTPX2_JSFETCH_VERSION,
|
|
23189
|
+
"SandboxedJs stand-in: httpx2's socket transports under the jsfetch names",
|
|
23190
|
+
{ "httpx2_jsfetch/__init__.py": HTTPX2_JSFETCH_SOURCE },
|
|
23191
|
+
["httpcore2[asyncio]", "anyio>=4.10", "truststore>=0.10"]
|
|
23192
|
+
),
|
|
23035
23193
|
greenlet: builtinDistribution(
|
|
23036
23194
|
"greenlet",
|
|
23037
23195
|
GREENLET_VERSION,
|
|
@@ -23056,7 +23214,7 @@ function builtinCandidates(name) {
|
|
|
23056
23214
|
filename: `${name}-${builtin.version}-py3-none-any.whl`,
|
|
23057
23215
|
sha256: "",
|
|
23058
23216
|
metadataUrl: null,
|
|
23059
|
-
indexedRequires:
|
|
23217
|
+
indexedRequires: builtin.requires
|
|
23060
23218
|
}];
|
|
23061
23219
|
}
|
|
23062
23220
|
function isBuiltinOnly(name) {
|
|
@@ -23952,6 +24110,36 @@ if sys.platform in ("emscripten", "wasi"):
|
|
|
23952
24110
|
|
|
23953
24111
|
sys.meta_path.insert(0, _SbxAsyncioWatcherHook())
|
|
23954
24112
|
|
|
24113
|
+
class _SbxPyodideFetchHook:
|
|
24114
|
+
"""Libraries read \`sys.platform == "emscripten"\` as "running in Pyodide"
|
|
24115
|
+
and switch to a browser-fetch backend that imports Pyodide's \`js\`
|
|
24116
|
+
module. This runtime has no \`js\` but does have sockets, so those
|
|
24117
|
+
backends are replaced by modules that leave the socket path in place.
|
|
24118
|
+
urllib3 (and so requests) imports its backend at package import, which
|
|
24119
|
+
made anything using requests fail with "No module named 'js'"."""
|
|
24120
|
+
|
|
24121
|
+
_STANDINS = {
|
|
24122
|
+
# urllib3/__init__.py calls inject_into_urllib3() on emscripten.
|
|
24123
|
+
"urllib3.contrib.emscripten": "def inject_into_urllib3():\\n pass\\n",
|
|
24124
|
+
}
|
|
24125
|
+
|
|
24126
|
+
def find_spec(self, name, path=None, target=None):
|
|
24127
|
+
source = self._STANDINS.get(name)
|
|
24128
|
+
if source is None:
|
|
24129
|
+
return None
|
|
24130
|
+
import importlib.util
|
|
24131
|
+
|
|
24132
|
+
class _Loader:
|
|
24133
|
+
def create_module(self, spec):
|
|
24134
|
+
return None
|
|
24135
|
+
|
|
24136
|
+
def exec_module(self, module):
|
|
24137
|
+
exec(compile(source, f"<sandboxedjs {name}>", "exec"), module.__dict__)
|
|
24138
|
+
|
|
24139
|
+
return importlib.util.spec_from_loader(name, _Loader(), is_package=False)
|
|
24140
|
+
|
|
24141
|
+
sys.meta_path.insert(0, _SbxPyodideFetchHook())
|
|
24142
|
+
|
|
23955
24143
|
|
|
23956
24144
|
_egress = os.environ.get("SBX_HTTP_EGRESS")
|
|
23957
24145
|
if _egress:
|
|
@@ -24033,6 +24221,255 @@ if _egress:
|
|
|
24033
24221
|
http.client.HTTPConnection = _SbxEgressConnection
|
|
24034
24222
|
http.client.HTTPSConnection = _SbxEgressTlsConnection
|
|
24035
24223
|
|
|
24224
|
+
# Everything above routes code that reaches for the standard library. The
|
|
24225
|
+
# popular HTTP clients do not: urllib3 (so requests), httpcore (so httpx)
|
|
24226
|
+
# and httpcore2 (so httpx2) open sockets themselves, and raw sockets do not
|
|
24227
|
+
# leave this container -- connect() to anything but loopback is
|
|
24228
|
+
# ENETUNREACH, and CPython's ssl cannot wrap these sockets anyway. So each
|
|
24229
|
+
# client's connection layer is replaced by one that speaks plain HTTP to
|
|
24230
|
+
# the egress endpoint with the absolute URL in the request line, which is
|
|
24231
|
+
# what the standard-library shims above already do.
|
|
24232
|
+
|
|
24233
|
+
def _sbx_egress_request(method, url, headers, body, timeout=None):
|
|
24234
|
+
"""One request through the egress. Returns (status, reason, headers, body)."""
|
|
24235
|
+
connection = _RealHTTPConnection(_egress_host, _egress_port, timeout=timeout)
|
|
24236
|
+
try:
|
|
24237
|
+
connection.request(method, url, body, {k: v for k, v in headers if k.lower() != "host"})
|
|
24238
|
+
response = connection.getresponse()
|
|
24239
|
+
payload = response.read()
|
|
24240
|
+
if response.getheader("x-sbx-error"):
|
|
24241
|
+
raise OSError(payload.decode("utf-8", "replace").strip())
|
|
24242
|
+
return response.status, response.reason, response.getheaders(), payload
|
|
24243
|
+
finally:
|
|
24244
|
+
connection.close()
|
|
24245
|
+
|
|
24246
|
+
async def _sbx_egress_request_async(method, url, headers, body):
|
|
24247
|
+
"""The same hop as _sbx_egress_request, over asyncio streams."""
|
|
24248
|
+
import asyncio
|
|
24249
|
+
reader, writer = await asyncio.open_connection(_egress_host, _egress_port)
|
|
24250
|
+
try:
|
|
24251
|
+
payload = body or b""
|
|
24252
|
+
if isinstance(payload, str):
|
|
24253
|
+
payload = payload.encode()
|
|
24254
|
+
lines = [f"{method} {url} HTTP/1.1", f"Host: {_egress_host}:{_egress_port}"]
|
|
24255
|
+
for key, value in headers:
|
|
24256
|
+
if key.lower() in ("host", "connection", "content-length", "transfer-encoding"):
|
|
24257
|
+
continue
|
|
24258
|
+
lines.append(f"{key}: {value}")
|
|
24259
|
+
lines.append(f"Content-Length: {len(payload)}")
|
|
24260
|
+
# Read to EOF rather than tracking keep-alive framing: one request
|
|
24261
|
+
# per connection, which is what this hop is for.
|
|
24262
|
+
lines.append("Connection: close")
|
|
24263
|
+
writer.write(("\\r\\n".join(lines) + "\\r\\n\\r\\n").encode("latin-1") + payload)
|
|
24264
|
+
await writer.drain()
|
|
24265
|
+
|
|
24266
|
+
head = await reader.readuntil(b"\\r\\n\\r\\n")
|
|
24267
|
+
start, _, rest = head.decode("latin-1").partition("\\r\\n")
|
|
24268
|
+
_, _, status_text = start.partition(" ")
|
|
24269
|
+
code, _, reason = status_text.partition(" ")
|
|
24270
|
+
received = []
|
|
24271
|
+
for line in rest.split("\\r\\n"):
|
|
24272
|
+
key, sep, value = line.partition(":")
|
|
24273
|
+
if sep:
|
|
24274
|
+
received.append((key.strip(), value.strip()))
|
|
24275
|
+
content = dict((k.lower(), v) for k, v in received)
|
|
24276
|
+
if "content-length" in content:
|
|
24277
|
+
data = await reader.readexactly(int(content["content-length"]))
|
|
24278
|
+
else:
|
|
24279
|
+
data = await reader.read()
|
|
24280
|
+
if content.get("x-sbx-error"):
|
|
24281
|
+
raise OSError(data.decode("utf-8", "replace").strip())
|
|
24282
|
+
return int(code), reason, received, data
|
|
24283
|
+
finally:
|
|
24284
|
+
writer.close()
|
|
24285
|
+
|
|
24286
|
+
class _SbxUrllib3Hook:
|
|
24287
|
+
"""urllib3's emscripten backend is Pyodide's \`js\` fetch, which this
|
|
24288
|
+
runtime has no module for; its socket backend cannot leave the
|
|
24289
|
+
container. Both connection classes become egress ones."""
|
|
24290
|
+
|
|
24291
|
+
def find_spec(self, name, path=None, target=None):
|
|
24292
|
+
if name != "urllib3.contrib.emscripten":
|
|
24293
|
+
return None
|
|
24294
|
+
import importlib.util
|
|
24295
|
+
|
|
24296
|
+
def inject_into_urllib3():
|
|
24297
|
+
import urllib3.connection
|
|
24298
|
+
from urllib3.connectionpool import HTTPConnectionPool, HTTPSConnectionPool
|
|
24299
|
+
|
|
24300
|
+
class _Plain(urllib3.connection.HTTPConnection):
|
|
24301
|
+
"""urllib3's own connection, dialled at the egress.
|
|
24302
|
+
|
|
24303
|
+
urllib3.connection.HTTPConnection still derives from
|
|
24304
|
+
http.client.HTTPConnection, so request building is
|
|
24305
|
+
inherited; only where it connects and what goes in the
|
|
24306
|
+
request line change.
|
|
24307
|
+
"""
|
|
24308
|
+
|
|
24309
|
+
_sbx_scheme = "http"
|
|
24310
|
+
default_port = 80
|
|
24311
|
+
is_verified = True
|
|
24312
|
+
proxy_is_verified = None
|
|
24313
|
+
|
|
24314
|
+
def __init__(self, host, port=None, **kwargs):
|
|
24315
|
+
# A distinct name from the http.client shim's
|
|
24316
|
+
# _sbx_origin: that class is further up this MRO and
|
|
24317
|
+
# its __init__ runs after this one, so sharing the name
|
|
24318
|
+
# overwrote the real destination with the egress and
|
|
24319
|
+
# every request went out addressed to the egress itself.
|
|
24320
|
+
self._sbx_target = (host, port or self.default_port)
|
|
24321
|
+
for unsupported in ("ssl_context", "ssl_version", "ssl_minimum_version",
|
|
24322
|
+
"ssl_maximum_version", "assert_hostname",
|
|
24323
|
+
"assert_fingerprint", "server_hostname", "ca_certs",
|
|
24324
|
+
"ca_cert_dir", "ca_cert_data", "cert_file", "key_file",
|
|
24325
|
+
"key_password", "cert_reqs"):
|
|
24326
|
+
kwargs.pop(unsupported, None)
|
|
24327
|
+
super().__init__(_egress_host, port=_egress_port, **kwargs)
|
|
24328
|
+
|
|
24329
|
+
def _sbx_absolute(self, url):
|
|
24330
|
+
host, port = self._sbx_target
|
|
24331
|
+
if url.startswith("http://") or url.startswith("https://"):
|
|
24332
|
+
return url
|
|
24333
|
+
netloc = host if port == self.default_port else f"{host}:{port}"
|
|
24334
|
+
return f"{self._sbx_scheme}://{netloc}{url}"
|
|
24335
|
+
|
|
24336
|
+
# urllib3 v2 writes the request line in its own request()
|
|
24337
|
+
# rather than through http.client's putrequest, so both
|
|
24338
|
+
# are absolutised; the egress refuses an origin-form target.
|
|
24339
|
+
def request(self, method, url, *args, **kwargs):
|
|
24340
|
+
return super().request(method, self._sbx_absolute(url), *args, **kwargs)
|
|
24341
|
+
|
|
24342
|
+
def putrequest(self, method, url, *args, **kwargs):
|
|
24343
|
+
return super().putrequest(method, self._sbx_absolute(url), *args, **kwargs)
|
|
24344
|
+
|
|
24345
|
+
def set_tunnel(self, *args, **kwargs):
|
|
24346
|
+
raise OSError("this container reaches the network through its egress, not CONNECT")
|
|
24347
|
+
|
|
24348
|
+
class _Tls(_Plain):
|
|
24349
|
+
_sbx_scheme = "https"
|
|
24350
|
+
default_port = 443
|
|
24351
|
+
|
|
24352
|
+
HTTPConnectionPool.ConnectionCls = _Plain
|
|
24353
|
+
HTTPSConnectionPool.ConnectionCls = _Tls
|
|
24354
|
+
urllib3.connection.HTTPConnection = _Plain
|
|
24355
|
+
urllib3.connection.HTTPSConnection = _Tls
|
|
24356
|
+
|
|
24357
|
+
class _Loader:
|
|
24358
|
+
def create_module(self, spec):
|
|
24359
|
+
return None
|
|
24360
|
+
|
|
24361
|
+
def exec_module(self, module):
|
|
24362
|
+
module.inject_into_urllib3 = inject_into_urllib3
|
|
24363
|
+
|
|
24364
|
+
return importlib.util.spec_from_loader(name, _Loader(), is_package=True)
|
|
24365
|
+
|
|
24366
|
+
sys.meta_path.insert(0, _SbxUrllib3Hook())
|
|
24367
|
+
|
|
24368
|
+
def _sbx_httpx_transports(httpx_module):
|
|
24369
|
+
"""Sync and async httpx transports that send through the egress."""
|
|
24370
|
+
|
|
24371
|
+
class _Transport(httpx_module.BaseTransport):
|
|
24372
|
+
def __init__(self, *args, **kwargs):
|
|
24373
|
+
pass
|
|
24374
|
+
|
|
24375
|
+
def handle_request(self, request):
|
|
24376
|
+
body = request.read()
|
|
24377
|
+
sent = [(k.decode("latin-1"), v.decode("latin-1")) for k, v in request.headers.raw]
|
|
24378
|
+
status, reason, headers, payload = _sbx_egress_request(
|
|
24379
|
+
request.method, str(request.url), sent, body,
|
|
24380
|
+
)
|
|
24381
|
+
# Content-Length/Transfer-Encoding describe the egress hop's
|
|
24382
|
+
# framing, and httpx re-derives them from the body it is given.
|
|
24383
|
+
keep = [(k, v) for k, v in headers
|
|
24384
|
+
if k.lower() not in ("content-length", "transfer-encoding", "content-encoding")]
|
|
24385
|
+
return httpx_module.Response(
|
|
24386
|
+
status, headers=keep, content=payload, request=request,
|
|
24387
|
+
extensions={"reason_phrase": (reason or "").encode()},
|
|
24388
|
+
)
|
|
24389
|
+
|
|
24390
|
+
def close(self):
|
|
24391
|
+
pass
|
|
24392
|
+
|
|
24393
|
+
def __enter__(self):
|
|
24394
|
+
return self
|
|
24395
|
+
|
|
24396
|
+
def __exit__(self, *args):
|
|
24397
|
+
pass
|
|
24398
|
+
|
|
24399
|
+
class _AsyncTransport(httpx_module.AsyncBaseTransport):
|
|
24400
|
+
def __init__(self, *args, **kwargs):
|
|
24401
|
+
pass
|
|
24402
|
+
|
|
24403
|
+
async def handle_async_request(self, request):
|
|
24404
|
+
# Not the blocking client on the loop thread: this image
|
|
24405
|
+
# proxies every syscall to one thread, so a blocking recv
|
|
24406
|
+
# inside a coroutine stops the loop that must service it. The
|
|
24407
|
+
# egress is loopback, which asyncio streams reach.
|
|
24408
|
+
import asyncio
|
|
24409
|
+
body = await request.aread()
|
|
24410
|
+
sent = [(k.decode("latin-1"), v.decode("latin-1")) for k, v in request.headers.raw]
|
|
24411
|
+
status, reason, headers, payload = await _sbx_egress_request_async(
|
|
24412
|
+
request.method, str(request.url), sent, body,
|
|
24413
|
+
)
|
|
24414
|
+
keep = [(k, v) for k, v in headers
|
|
24415
|
+
if k.lower() not in ("content-length", "transfer-encoding", "content-encoding")]
|
|
24416
|
+
return httpx_module.Response(
|
|
24417
|
+
status, headers=keep, content=payload, request=request,
|
|
24418
|
+
extensions={"reason_phrase": (reason or "").encode()},
|
|
24419
|
+
)
|
|
24420
|
+
|
|
24421
|
+
async def aclose(self):
|
|
24422
|
+
pass
|
|
24423
|
+
|
|
24424
|
+
async def __aenter__(self):
|
|
24425
|
+
return self
|
|
24426
|
+
|
|
24427
|
+
async def __aexit__(self, *args):
|
|
24428
|
+
pass
|
|
24429
|
+
|
|
24430
|
+
return _Transport, _AsyncTransport
|
|
24431
|
+
|
|
24432
|
+
class _SbxHttpxHook:
|
|
24433
|
+
"""httpx and httpx2 build their default transports on httpcore, which
|
|
24434
|
+
opens sockets. Their default transport classes are replaced as the
|
|
24435
|
+
module loads, so \`httpx.get(...)\` and every client built from the
|
|
24436
|
+
defaults leave through the egress."""
|
|
24437
|
+
|
|
24438
|
+
_PACKAGES = {"httpx": "httpx", "httpx2": "httpx2"}
|
|
24439
|
+
|
|
24440
|
+
def find_spec(self, name, path=None, target=None):
|
|
24441
|
+
package = self._PACKAGES.get(name)
|
|
24442
|
+
if package is None or getattr(self, "_loading", None) == name:
|
|
24443
|
+
return None
|
|
24444
|
+
import importlib.machinery
|
|
24445
|
+
spec = importlib.machinery.PathFinder.find_spec(name, path)
|
|
24446
|
+
if spec is None or spec.loader is None:
|
|
24447
|
+
return spec
|
|
24448
|
+
load = spec.loader.exec_module
|
|
24449
|
+
|
|
24450
|
+
def exec_module(module):
|
|
24451
|
+
self._loading = name
|
|
24452
|
+
try:
|
|
24453
|
+
load(module)
|
|
24454
|
+
finally:
|
|
24455
|
+
self._loading = None
|
|
24456
|
+
transport, async_transport = _sbx_httpx_transports(module)
|
|
24457
|
+
module.HTTPTransport = transport
|
|
24458
|
+
module.AsyncHTTPTransport = async_transport
|
|
24459
|
+
defaults = sys.modules.get(f"{package}._transports.default")
|
|
24460
|
+
if defaults is not None:
|
|
24461
|
+
defaults.HTTPTransport = transport
|
|
24462
|
+
defaults.AsyncHTTPTransport = async_transport
|
|
24463
|
+
client = sys.modules.get(f"{package}._client")
|
|
24464
|
+
if client is not None:
|
|
24465
|
+
client.HTTPTransport = transport
|
|
24466
|
+
client.AsyncHTTPTransport = async_transport
|
|
24467
|
+
|
|
24468
|
+
spec.loader.exec_module = exec_module
|
|
24469
|
+
return spec
|
|
24470
|
+
|
|
24471
|
+
sys.meta_path.insert(0, _SbxHttpxHook())
|
|
24472
|
+
|
|
24036
24473
|
|
|
24037
24474
|
if os.environ.get("SBX_SERIAL_HOST_CALLS") == "1":
|
|
24038
24475
|
import asyncio
|
|
@@ -24061,6 +24498,47 @@ if os.environ.get("SBX_SERIAL_HOST_CALLS") == "1":
|
|
|
24061
24498
|
)
|
|
24062
24499
|
|
|
24063
24500
|
asyncio.to_thread = _to_thread
|
|
24501
|
+
|
|
24502
|
+
class _SbxAnyioThreadHook:
|
|
24503
|
+
"""anyio's thread offload, run inline.
|
|
24504
|
+
|
|
24505
|
+
anyio has its own worker threads, so it never reaches asyncio.to_thread
|
|
24506
|
+
and the refusal above -- it deadlocks instead, for the reason described
|
|
24507
|
+
there. Starlette sends every *synchronous* endpoint through
|
|
24508
|
+
anyio.to_thread.run_sync, so a plain def FastAPI route hung forever
|
|
24509
|
+
while an async def one answered.
|
|
24510
|
+
|
|
24511
|
+
Running the function on the loop thread keeps such an endpoint working.
|
|
24512
|
+
The cost is real and worth stating: a synchronous handler blocks the
|
|
24513
|
+
loop for its duration, so it does not overlap with other requests.
|
|
24514
|
+
"""
|
|
24515
|
+
|
|
24516
|
+
def find_spec(self, name, path=None, target=None):
|
|
24517
|
+
if name != "anyio.to_thread":
|
|
24518
|
+
return None
|
|
24519
|
+
import importlib.machinery
|
|
24520
|
+
spec = importlib.machinery.PathFinder.find_spec(name, path)
|
|
24521
|
+
if spec is None or spec.loader is None:
|
|
24522
|
+
return spec
|
|
24523
|
+
load = spec.loader.exec_module
|
|
24524
|
+
|
|
24525
|
+
def exec_module(module):
|
|
24526
|
+
load(module)
|
|
24527
|
+
|
|
24528
|
+
async def run_sync(func, *args, abandon_on_cancel=False,
|
|
24529
|
+
cancellable=None, limiter=None):
|
|
24530
|
+
return func(*args)
|
|
24531
|
+
|
|
24532
|
+
async def run_sync_in_worker_thread(func, *args, **kwargs):
|
|
24533
|
+
return func(*args)
|
|
24534
|
+
|
|
24535
|
+
module.run_sync = run_sync
|
|
24536
|
+
module.run_sync_in_worker_thread = run_sync_in_worker_thread
|
|
24537
|
+
|
|
24538
|
+
spec.loader.exec_module = exec_module
|
|
24539
|
+
return spec
|
|
24540
|
+
|
|
24541
|
+
sys.meta_path.insert(0, _SbxAnyioThreadHook())
|
|
24064
24542
|
`;
|
|
24065
24543
|
function ensureProcessShims(ctx) {
|
|
24066
24544
|
const hook = `${SITE_PACKAGES}/sitecustomize.py`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sandboxedjs",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.93",
|
|
4
4
|
"description": "A Linux-like container that runs entirely inside Node.js — POSIX shell, ~140 coreutils, Node.js and Python runtimes, virtual filesystem and networking. No Docker, no VM, no native modules.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|