wasm-action 0.0.8__tar.gz → 0.0.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. {wasm_action-0.0.8 → wasm_action-0.0.9}/PKG-INFO +2 -1
  2. {wasm_action-0.0.8 → wasm_action-0.0.9}/README.md +1 -0
  3. {wasm_action-0.0.8 → wasm_action-0.0.9}/pyproject.toml +1 -1
  4. wasm_action-0.0.9/src/wasm_action/cache.py +58 -0
  5. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/wasm_action/cli.py +11 -0
  6. wasm_action-0.0.9/src/wasm_action/python.py +108 -0
  7. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/wasm_action/wasm/runtime.py +51 -2
  8. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/__init__.py +0 -0
  9. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/api/__init__.py +0 -0
  10. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/api/content_api.py +0 -0
  11. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/api/fetch_api.py +0 -0
  12. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/api/ledger_api.py +0 -0
  13. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/api/monitor_api.py +0 -0
  14. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/api/package_api.py +0 -0
  15. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/api/proof_api.py +0 -0
  16. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/api_client.py +0 -0
  17. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/api_response.py +0 -0
  18. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/configuration.py +0 -0
  19. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/exceptions.py +0 -0
  20. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/__init__.py +0 -0
  21. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/bundle_failure_error.py +0 -0
  22. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/checkpoint.py +0 -0
  23. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/checkpoint_verification_response.py +0 -0
  24. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/content_sources_response.py +0 -0
  25. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/envelope_body.py +0 -0
  26. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/error.py +0 -0
  27. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/fetch_logs404_response.py +0 -0
  28. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/fetch_logs_id_not_found_error.py +0 -0
  29. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/fetch_logs_log_length_not_found_error.py +0 -0
  30. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/fetch_logs_request.py +0 -0
  31. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/fetch_logs_response.py +0 -0
  32. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/fetch_names404_response.py +0 -0
  33. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/fetch_package_names_request.py +0 -0
  34. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/fetch_package_names_response.py +0 -0
  35. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/fetch_warning.py +0 -0
  36. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/get_content_sources404_response.py +0 -0
  37. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/get_package_record404_response.py +0 -0
  38. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/http_get.py +0 -0
  39. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/http_upload.py +0 -0
  40. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/http_upload_headers.py +0 -0
  41. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/incorrect_proof_error.py +0 -0
  42. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/ledger_sources_response.py +0 -0
  43. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/ledger_sources_response_sources_inner.py +0 -0
  44. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/missing_content.py +0 -0
  45. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/package_not_included_error.py +0 -0
  46. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/package_record.py +0 -0
  47. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/processing_record.py +0 -0
  48. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/prove_consistency404_response.py +0 -0
  49. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/prove_consistency_request.py +0 -0
  50. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/prove_consistency_response.py +0 -0
  51. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/prove_inclusion404_response.py +0 -0
  52. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/prove_inclusion422_response.py +0 -0
  53. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/prove_inclusion_request.py +0 -0
  54. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/prove_inclusion_response.py +0 -0
  55. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/publish_package_record404_response.py +0 -0
  56. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/publish_package_record409_response.py +0 -0
  57. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/publish_package_record_request.py +0 -0
  58. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/published_record.py +0 -0
  59. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/published_record_envelope.py +0 -0
  60. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/rejected_record.py +0 -0
  61. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/signature.py +0 -0
  62. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/signed_checkpoint.py +0 -0
  63. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/sourcing_record.py +0 -0
  64. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/models/timestamped_checkpoint.py +0 -0
  65. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/py.typed +0 -0
  66. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/warg_openapi/rest.py +0 -0
  67. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/wasm_action/__init__.py +0 -0
  68. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/wasm_action/lib.py +0 -0
  69. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/wasm_action/registry.py +0 -0
  70. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/wasm_action/util.py +0 -0
  71. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/wasm_action/warg/__init__.py +0 -0
  72. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/wasm_action/warg/actions.py +0 -0
  73. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/wasm_action/warg/client.py +0 -0
  74. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/wasm_action/warg/crypto.py +0 -0
  75. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/wasm_action/warg/proto.py +0 -0
  76. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/wasm_action/wasm/__init__.py +0 -0
  77. {wasm_action-0.0.8 → wasm_action-0.0.9}/src/wasm_action/wasm/expression.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: wasm-action
3
- Version: 0.0.8
3
+ Version: 0.0.9
4
4
  Summary: Interact with WebAssembly registries.
5
5
  Requires-Dist: click>=8.2.1
6
6
  Requires-Dist: cryptography>=45.0.6
@@ -27,6 +27,7 @@ Description-Content-Type: text/markdown
27
27
  * Supported artifact types: wasm
28
28
  * Supported actions: push, pull
29
29
  * Supports Python 3.10+ on Linux, MacOS and Windows
30
+ * Python sandbox using wasm build of cpython 3.14
30
31
 
31
32
  #### Planned
32
33
  * OCI registry support (a.k.a. Docker registry)
@@ -8,6 +8,7 @@
8
8
  * Supported artifact types: wasm
9
9
  * Supported actions: push, pull
10
10
  * Supports Python 3.10+ on Linux, MacOS and Windows
11
+ * Python sandbox using wasm build of cpython 3.14
11
12
 
12
13
  #### Planned
13
14
  * OCI registry support (a.k.a. Docker registry)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "wasm-action"
3
- version = "0.0.8"
3
+ version = "0.0.9"
4
4
  description = "Interact with WebAssembly registries."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -0,0 +1,58 @@
1
+
2
+ import os
3
+ import hashlib
4
+
5
+ CACHE = os.path.join(os.environ['HOME'], '.cache', 'wasm-action')
6
+
7
+
8
+ def fetch(content_hash: str) -> bytes:
9
+ """Fetch object from cache"""
10
+ if not os.path.exists(CACHE):
11
+ os.makedirs(CACHE)
12
+
13
+ filename = os.path.join(CACHE, content_hash)
14
+ if not os.path.exists(filename):
15
+ return None
16
+
17
+ with open(filename, 'rb') as f:
18
+ content = f.read()
19
+
20
+ computed_hash = hashlib.sha256(content).hexdigest()
21
+ if computed_hash != content_hash:
22
+ raise ValueError('content with hash has been tempered: {}'.format(content_hash))
23
+ return content
24
+
25
+
26
+ def store(content: bytes):
27
+ """Store object in cache"""
28
+ if not os.path.exists(CACHE):
29
+ os.makedirs(CACHE)
30
+
31
+ content_hash = hashlib.sha256(content).hexdigest()
32
+ filename = os.path.join(CACHE, content_hash)
33
+ with open(filename, 'wb') as f:
34
+ f.write(content)
35
+
36
+
37
+ def exists(content_hash: str) -> bool:
38
+ """Check if object exists"""
39
+ if not os.path.exists(CACHE):
40
+ os.makedirs(CACHE)
41
+
42
+ filename = os.path.join(CACHE, content_hash)
43
+ return os.path.exists(filename)
44
+
45
+
46
+ def compute_hash(content: bytes) -> str:
47
+ """Compute hash without storing the object"""
48
+ return hashlib.sha256(content).hexdigest()
49
+
50
+
51
+ def size(content_hash: str) -> int:
52
+ """Return size of stored object"""
53
+ if not os.path.exists(CACHE):
54
+ os.makedirs(CACHE)
55
+ filename = os.path.join(CACHE, content_hash)
56
+ if not os.path.exists(filename):
57
+ return 0
58
+ return os.stat(filename).st_size
@@ -5,6 +5,7 @@ import importlib.metadata
5
5
  import json
6
6
 
7
7
  from . import lib
8
+ from . import python
8
9
  from .warg.crypto import generate_key
9
10
  from .wasm import runtime
10
11
  from .util import cli_error_handler
@@ -141,5 +142,15 @@ def evaluate(filename, expression):
141
142
  print(e)
142
143
 
143
144
 
145
+ @cli.command('python', help="Python in a sandbox", add_help_option=False, context_settings=dict(
146
+ ignore_unknown_options=True,
147
+ ))
148
+ @click.argument('args', nargs=-1, type=click.UNPROCESSED)
149
+ @cli_error_handler
150
+ def run_python(args):
151
+ """Run a WASI-compiled wasm build of cpython"""
152
+ python.run_python(args)
153
+
154
+
144
155
  if __name__ == "__main__":
145
156
  cli()
@@ -0,0 +1,108 @@
1
+
2
+ """
3
+ Python in a sandbox using WASM/WASI.
4
+
5
+ uvx --python 3.14 wasm-action python
6
+ """
7
+
8
+ import os
9
+ import sys
10
+ import tempfile
11
+ import shutil
12
+
13
+ from . import cache
14
+ from . import lib
15
+ from .wasm import runtime
16
+
17
+ PYTHON = {
18
+
19
+ '3.14': {
20
+ 'registry': 'wa.dev',
21
+ 'package': 'xelato:python314',
22
+ 'version': '26.2.6',
23
+ 'sha256': '6a9e23d3db2ea0883fb74bfe9540bcb27bd167be1dab39546a5376112f4beea0',
24
+ },
25
+
26
+ }
27
+
28
+
29
+ def run_python(args):
30
+ v = sys.version_info
31
+ version = "{}.{}".format(v.major, v.minor)
32
+ python = PYTHON.get(version) or PYTHON['3.14']
33
+
34
+ if cache.exists(python['sha256']):
35
+ print("Found object in cache")
36
+ content = cache.fetch(python['sha256'])
37
+ else:
38
+ print("Downloading python build")
39
+ download = lib.pull(
40
+ registry=python['registry'],
41
+ package="{}@{}".format(python['package'], python['version'])
42
+ )
43
+ if download.digest != "sha256:{}".format(python['sha256']):
44
+ raise ValueError('unexpected digest while downloading build')
45
+
46
+ cache.store(download.content)
47
+ print("Stored object in local cache")
48
+ content = download.content
49
+
50
+ # Lib folder
51
+ # Reusing the host Python installation
52
+ python_lib = os.path.dirname(os.__file__)
53
+ print("Using python lib {}".format(python_lib))
54
+
55
+ argv = ['python']
56
+ argv.extend(args)
57
+
58
+ tmp = tempfile.mkdtemp('py')
59
+
60
+ instance = (runtime
61
+ .module(content)
62
+ .wasi()
63
+ # pass all cli arguments to the wasm "process"
64
+ .argv(argv)
65
+
66
+ # configure python lib
67
+ .env('PYTHONPATH', '/lib:/build')
68
+ .mount(python_lib, '/lib', readonly=True)
69
+
70
+ # todo: ModuleNotFoundError: No module named '_sysconfigdata__wasi_wasm32-wasi'
71
+ #.mount('{}/github/python/cpython/builddir/wasi/build/lib.wasi-wasm32-3.14'.format(os.environ['HOME']), '/build')
72
+
73
+ # provide a /tmp folder
74
+ .mount(tmp, '/tmp', readonly=False)
75
+
76
+ # Get access to CWD for running user code.
77
+ # note: it may be preferable to set this to a subdir of /
78
+ .mount(os.getcwd(), "/", readonly=True)
79
+
80
+ .instance()
81
+ )
82
+
83
+ # todo: exit code?
84
+ try:
85
+ instance.function('_start')()
86
+ finally:
87
+ # clean-up tmp dir
88
+ shutil.rmtree(tmp)
89
+
90
+ """
91
+ https://github.com/python/cpython/blob/main/Modules/getpath.py
92
+ Could not find platform independent libraries <prefix>
93
+ Could not find platform dependent libraries <exec_prefix>
94
+ """
95
+
96
+ # 3.14
97
+ """
98
+ Installations of Python now contain a new file, :file:`build-details.json`.
99
+ This is a static JSON document containing build details for CPython,
100
+ to allow for introspection without needing to run code.
101
+ This is helpful for use-cases such as Python launchers, cross-compilation,
102
+ and so on.
103
+
104
+ :file:`build-details.json` must be installed in the platform-independent
105
+ standard library directory. This corresponds to the :ref:`'stdlib'
106
+ <installation_paths>` :mod:`sysconfig` installation path,
107
+ which can be found by running ``sysconfig.get_path('stdlib')``.
108
+ """
@@ -24,16 +24,21 @@ class Module:
24
24
  self._store = wasmtime.Store()
25
25
  self._module = wasmtime.Module(self._store.engine, module_bytes)
26
26
 
27
+ def wasi(self):
28
+ return WASI(store=self._store, module=self._module)
29
+
27
30
  def instance(self):
28
31
  return Instance(store=self._store, module=self._module)
29
32
 
30
33
 
31
34
  class Instance:
32
35
 
33
- def __init__(self, store, module):
36
+ def __init__(self, store, module, instance=None):
34
37
  self._store = store
35
38
  self._module = module
36
- self._instance = None
39
+ if instance:
40
+ assert isinstance(instance, wasmtime.Instance)
41
+ self._instance = instance
37
42
  self._imports = []
38
43
 
39
44
  def __getattr__(self, name):
@@ -54,6 +59,50 @@ class Instance:
54
59
  return expression.evaluate(text or '', obj=self)
55
60
 
56
61
 
62
+ class WASI:
63
+
64
+ def __init__(self, store, module):
65
+ self._store = store
66
+ self._module = module
67
+
68
+ self.linker = wasmtime.Linker(self._store.engine)
69
+ self.linker.define_wasi()
70
+
71
+ self._wasi = wasmtime.WasiConfig()
72
+ self._wasi.inherit_stdin()
73
+ self._wasi.inherit_stdout()
74
+ self._wasi.inherit_stderr()
75
+
76
+ self._environ = {}
77
+
78
+ def mount(self, host_path, guest_path, readonly=True):
79
+ """Bind mount `host_path` as `guest_path` in the WASM instance."""
80
+ self._wasi.preopen_dir(
81
+ host_path,
82
+ guest_path,
83
+ dir_perms=wasmtime.DirPerms.READ_ONLY if readonly else wasmtime.DirPerms.READ_WRITE,
84
+ file_perms=wasmtime.FilePerms.READ_ONLY if readonly else wasmtime.FilePerms.READ_WRITE,
85
+ )
86
+ return self
87
+
88
+ def argv(self, argv):
89
+ self._wasi.argv = argv
90
+ return self
91
+
92
+ def env(self, key, value):
93
+ """Configure an environment variable in the WASM instance"""
94
+ self._environ[key] = value
95
+ return self
96
+
97
+ def instance(self):
98
+ self._wasi.env = [
99
+ (k, v) for (k, v) in self._environ.items()
100
+ ]
101
+ self._store.set_wasi(self._wasi)
102
+ instance = self.linker.instantiate(self._store, self._module)
103
+ return Instance(store=self._store, module=self._module, instance=instance)
104
+
105
+
57
106
  class Function:
58
107
 
59
108
  _types = {