shellsim 0.1.13__tar.gz → 0.1.15__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 (183) hide show
  1. {shellsim-0.1.13 → shellsim-0.1.15}/Cargo.lock +1 -1
  2. {shellsim-0.1.13 → shellsim-0.1.15}/Cargo.toml +1 -1
  3. {shellsim-0.1.13 → shellsim-0.1.15}/PKG-INFO +1 -1
  4. {shellsim-0.1.13 → shellsim-0.1.15}/python/native/Cargo.lock +2 -2
  5. {shellsim-0.1.13 → shellsim-0.1.15}/python/native/Cargo.toml +1 -1
  6. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/protocol.rs +63 -0
  7. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/asyncio.py +269 -38
  8. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/vm/calls.rs +14 -0
  9. {shellsim-0.1.13 → shellsim-0.1.15}/LICENSE +0 -0
  10. {shellsim-0.1.13 → shellsim-0.1.15}/README.md +0 -0
  11. {shellsim-0.1.13 → shellsim-0.1.15}/pyproject.toml +0 -0
  12. {shellsim-0.1.13 → shellsim-0.1.15}/python/native/src/lib.rs +0 -0
  13. {shellsim-0.1.13 → shellsim-0.1.15}/python/shellsim/__init__.py +0 -0
  14. {shellsim-0.1.13 → shellsim-0.1.15}/python/shellsim/__main__.py +0 -0
  15. {shellsim-0.1.13 → shellsim-0.1.15}/python/shellsim/_api.py +0 -0
  16. {shellsim-0.1.13 → shellsim-0.1.15}/python/shellsim/_cli.py +0 -0
  17. {shellsim-0.1.13 → shellsim-0.1.15}/python/shellsim/py.typed +0 -0
  18. {shellsim-0.1.13 → shellsim-0.1.15}/python/shellsim/python.py +0 -0
  19. {shellsim-0.1.13 → shellsim-0.1.15}/rust-toolchain.toml +0 -0
  20. {shellsim-0.1.13 → shellsim-0.1.15}/src/bin/shellsim-python.rs +0 -0
  21. {shellsim-0.1.13 → shellsim-0.1.15}/src/clock.rs +0 -0
  22. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/archives.rs +0 -0
  23. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/awk/ast.rs +0 -0
  24. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/awk/eval.rs +0 -0
  25. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/awk/lexer.rs +0 -0
  26. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/awk/parser.rs +0 -0
  27. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/awk.rs +0 -0
  28. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/builtins.rs +0 -0
  29. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/dd.rs +0 -0
  30. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/echo.rs +0 -0
  31. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/find.rs +0 -0
  32. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/fs.rs +0 -0
  33. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/git/apply.rs +0 -0
  34. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/git/commit.rs +0 -0
  35. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/git/compare.rs +0 -0
  36. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/git/config.rs +0 -0
  37. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/git/conflict.rs +0 -0
  38. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/git/diff.rs +0 -0
  39. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/git/ignore.rs +0 -0
  40. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/git/import.rs +0 -0
  41. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/git/log.rs +0 -0
  42. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/git/merge.rs +0 -0
  43. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/git/mod.rs +0 -0
  44. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/git/plumbing.rs +0 -0
  45. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/git/rebase.rs +0 -0
  46. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/git/refs.rs +0 -0
  47. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/git/repo.rs +0 -0
  48. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/git/stash.rs +0 -0
  49. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/git/switch.rs +0 -0
  50. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/git/worktree.rs +0 -0
  51. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/hashing.rs +0 -0
  52. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/makecmd.rs +0 -0
  53. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/mod.rs +0 -0
  54. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/net.rs +0 -0
  55. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/options.rs +0 -0
  56. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/patch.rs +0 -0
  57. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/pkg.rs +0 -0
  58. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/printf.rs +0 -0
  59. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/proc.rs +0 -0
  60. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/regex_compat.rs +0 -0
  61. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/rgcmd.rs +0 -0
  62. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/sed.rs +0 -0
  63. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/sort.rs +0 -0
  64. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/system.rs +0 -0
  65. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/tarcmd.rs +0 -0
  66. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/text.rs +0 -0
  67. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/unavailable.rs +0 -0
  68. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/util.rs +0 -0
  69. {shellsim-0.1.13 → shellsim-0.1.15}/src/commands/zipcmd.rs +0 -0
  70. {shellsim-0.1.13 → shellsim-0.1.15}/src/descriptors.rs +0 -0
  71. {shellsim-0.1.13 → shellsim-0.1.15}/src/exec.rs +0 -0
  72. {shellsim-0.1.13 → shellsim-0.1.15}/src/expand.rs +0 -0
  73. {shellsim-0.1.13 → shellsim-0.1.15}/src/harness.rs +0 -0
  74. {shellsim-0.1.13 → shellsim-0.1.15}/src/harness_manager.rs +0 -0
  75. {shellsim-0.1.13 → shellsim-0.1.15}/src/hashes.rs +0 -0
  76. {shellsim-0.1.13 → shellsim-0.1.15}/src/host_ingest.rs +0 -0
  77. {shellsim-0.1.13 → shellsim-0.1.15}/src/interp.rs +0 -0
  78. {shellsim-0.1.13 → shellsim-0.1.15}/src/jqcmd.rs +0 -0
  79. {shellsim-0.1.13 → shellsim-0.1.15}/src/lib.rs +0 -0
  80. {shellsim-0.1.13 → shellsim-0.1.15}/src/main.rs +0 -0
  81. {shellsim-0.1.13 → shellsim-0.1.15}/src/mcp.rs +0 -0
  82. {shellsim-0.1.13 → shellsim-0.1.15}/src/net.rs +0 -0
  83. {shellsim-0.1.13 → shellsim-0.1.15}/src/netcmd.rs +0 -0
  84. {shellsim-0.1.13 → shellsim-0.1.15}/src/process.rs +0 -0
  85. {shellsim-0.1.13 → shellsim-0.1.15}/src/pseudo_fs.rs +0 -0
  86. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/ast.rs +0 -0
  87. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/bytecode.rs +0 -0
  88. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/compiler.rs +0 -0
  89. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/filesystem.rs +0 -0
  90. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/heap.rs +0 -0
  91. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/http.rs +0 -0
  92. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/lexer.rs +0 -0
  93. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/mapping.rs +0 -0
  94. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/mod.rs +0 -0
  95. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/native.rs +0 -0
  96. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/number.rs +0 -0
  97. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/object_model.rs +0 -0
  98. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/parser.rs +0 -0
  99. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/process.rs +0 -0
  100. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/slice.rs +0 -0
  101. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/source.rs +0 -0
  102. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/argparse.rs +0 -0
  103. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/asyncio.rs +0 -0
  104. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/base64.rs +0 -0
  105. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/bisect.rs +0 -0
  106. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/collections.rs +0 -0
  107. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/core.rs +0 -0
  108. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/dataclasses.rs +0 -0
  109. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/enum.rs +0 -0
  110. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/frozen.rs +0 -0
  111. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/functools.rs +0 -0
  112. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/hashlib.rs +0 -0
  113. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/heapq.rs +0 -0
  114. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/http.rs +0 -0
  115. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/importlib.rs +0 -0
  116. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/itertools.rs +0 -0
  117. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/json.rs +0 -0
  118. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/math.rs +0 -0
  119. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/mod.rs +0 -0
  120. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/numpy.rs +0 -0
  121. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/os.rs +0 -0
  122. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/pytest.rs +0 -0
  123. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/re.rs +0 -0
  124. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/abc.py +0 -0
  125. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/base64.py +0 -0
  126. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/cmath.py +0 -0
  127. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/codecs.py +0 -0
  128. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/collections.py +0 -0
  129. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/complex.py +0 -0
  130. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/copy.py +0 -0
  131. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/csv.py +0 -0
  132. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/datetime.py +0 -0
  133. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/glob.py +0 -0
  134. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/hashlib.py +0 -0
  135. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/http.py +0 -0
  136. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/http_client.py +0 -0
  137. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/importlib.py +0 -0
  138. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/io.py +0 -0
  139. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/json.py +0 -0
  140. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/logging.py +0 -0
  141. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/numpy_random.py +0 -0
  142. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/os.py +0 -0
  143. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/pathlib.py +0 -0
  144. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/pytest.py +0 -0
  145. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/random.py +0 -0
  146. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/shutil.py +0 -0
  147. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/statistics.py +0 -0
  148. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/struct.py +0 -0
  149. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/subprocess.py +0 -0
  150. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/tempfile.py +0 -0
  151. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/textwrap.py +0 -0
  152. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/urllib.py +0 -0
  153. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/urllib_error.py +0 -0
  154. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/urllib_request.py +0 -0
  155. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/uuid.py +0 -0
  156. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/zipfile.py +0 -0
  157. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/source/zlib.py +0 -0
  158. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/string.rs +0 -0
  159. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/struct.rs +0 -0
  160. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/subprocess.rs +0 -0
  161. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/sys.rs +0 -0
  162. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/time.rs +0 -0
  163. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/typing.rs +0 -0
  164. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/unittest.rs +0 -0
  165. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/vfs.rs +0 -0
  166. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/stdlib/zlib.rs +0 -0
  167. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/string.rs +0 -0
  168. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/token.rs +0 -0
  169. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/vm/dispatch.rs +0 -0
  170. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/vm/host.rs +0 -0
  171. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/vm/iteration.rs +0 -0
  172. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/vm/namespace.rs +0 -0
  173. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/vm/native_runtime.rs +0 -0
  174. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/vm/objects.rs +0 -0
  175. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/vm/operations.rs +0 -0
  176. {shellsim-0.1.13 → shellsim-0.1.15}/src/python/vm.rs +0 -0
  177. {shellsim-0.1.13 → shellsim-0.1.15}/src/resources.rs +0 -0
  178. {shellsim-0.1.13 → shellsim-0.1.15}/src/sandbox.rs +0 -0
  179. {shellsim-0.1.13 → shellsim-0.1.15}/src/scenario.rs +0 -0
  180. {shellsim-0.1.13 → shellsim-0.1.15}/src/scheduler.rs +0 -0
  181. {shellsim-0.1.13 → shellsim-0.1.15}/src/shell.rs +0 -0
  182. {shellsim-0.1.13 → shellsim-0.1.15}/src/telemetry.rs +0 -0
  183. {shellsim-0.1.13 → shellsim-0.1.15}/src/vfs.rs +0 -0
@@ -330,7 +330,7 @@ dependencies = [
330
330
 
331
331
  [[package]]
332
332
  name = "shellsim"
333
- version = "0.1.13"
333
+ version = "0.1.15"
334
334
  dependencies = [
335
335
  "base64",
336
336
  "crc32fast",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "shellsim"
3
- version = "0.1.13"
3
+ version = "0.1.15"
4
4
  edition = "2021"
5
5
  description = "In-process BusyBox-like environment for RL rollouts and agent tests"
6
6
  license = "Apache-2.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shellsim
3
- Version: 0.1.13
3
+ Version: 0.1.15
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: License :: OSI Approved :: Apache Software License
6
6
  Classifier: Operating System :: MacOS :: MacOS X
@@ -406,7 +406,7 @@ dependencies = [
406
406
 
407
407
  [[package]]
408
408
  name = "shellsim"
409
- version = "0.1.13"
409
+ version = "0.1.15"
410
410
  dependencies = [
411
411
  "base64",
412
412
  "crc32fast",
@@ -427,7 +427,7 @@ dependencies = [
427
427
 
428
428
  [[package]]
429
429
  name = "shellsim-python-bindings"
430
- version = "0.1.13"
430
+ version = "0.1.15"
431
431
  dependencies = [
432
432
  "pyo3",
433
433
  "serde",
@@ -2,7 +2,7 @@
2
2
 
3
3
  [package]
4
4
  name = "shellsim-python-bindings"
5
- version = "0.1.13"
5
+ version = "0.1.15"
6
6
  edition = "2021"
7
7
  description = "Python bindings for shellsim"
8
8
  license = "Apache-2.0"
@@ -20,6 +20,25 @@ pub fn display(heap: &Heap, value: &Value) -> Result<String, String> {
20
20
  if let Some((kind, message)) = exception_parts(heap, value)? {
21
21
  return Ok(if message.is_empty() { kind } else { message });
22
22
  }
23
+ if let Some((_, args)) = user_exception_parts(heap, value)? {
24
+ return match args.as_slice() {
25
+ [] => Ok(String::new()),
26
+ [only] => {
27
+ if let Some(value) = string_value(heap, only)? {
28
+ Ok(value)
29
+ } else {
30
+ repr(heap, only)
31
+ }
32
+ }
33
+ _ => {
34
+ let values = args
35
+ .iter()
36
+ .map(|value| repr(heap, value))
37
+ .collect::<Result<Vec<_>, _>>()?;
38
+ Ok(format!("({})", values.join(", ")))
39
+ }
40
+ };
41
+ }
23
42
  repr(heap, value)
24
43
  }
25
44
 
@@ -100,6 +119,41 @@ pub fn exception_parts(heap: &Heap, value: &Value) -> Result<Option<(String, Str
100
119
  })
101
120
  }
102
121
 
122
+ fn user_exception_parts(
123
+ heap: &Heap,
124
+ value: &Value,
125
+ ) -> Result<Option<(String, Vec<Value>)>, String> {
126
+ let Some(id) = value.object_id() else {
127
+ return Ok(None);
128
+ };
129
+ let Object::Instance { class, .. } = heap.get(id)? else {
130
+ return Ok(None);
131
+ };
132
+ let Object::Class {
133
+ name,
134
+ exception_base,
135
+ ..
136
+ } = heap.get(*class)?
137
+ else {
138
+ return Ok(None);
139
+ };
140
+ if exception_base.is_none() {
141
+ return Ok(None);
142
+ }
143
+ let args = if let Some(symbol) = heap.symbol_id("args") {
144
+ heap.attribute_by_symbol(id, symbol)?
145
+ .and_then(|value| value.object_id())
146
+ .and_then(|args| match heap.get(args).ok()? {
147
+ Object::Tuple(values) => Some(values.clone()),
148
+ _ => None,
149
+ })
150
+ .unwrap_or_default()
151
+ } else {
152
+ Vec::new()
153
+ };
154
+ Ok(Some((name.clone(), args)))
155
+ }
156
+
103
157
  fn bigint_value<'a>(heap: &'a Heap, value: &Value) -> Option<&'a BigInt> {
104
158
  let id = value.object_id()?;
105
159
  match heap.get(id).ok()? {
@@ -244,6 +298,15 @@ fn render(heap: &Heap, value: &Value, active: &mut BTreeSet<ObjectId>) -> Result
244
298
  Object::Instance { class, payload, .. } => match payload {
245
299
  InstancePayload::Int(value) => value.to_string(),
246
300
  InstancePayload::Object => match heap.get(*class)? {
301
+ Object::Class {
302
+ name,
303
+ exception_base: Some(_),
304
+ ..
305
+ } => {
306
+ let (_, args) = user_exception_parts(heap, value)?
307
+ .ok_or("exception instance lost its native base")?;
308
+ format!("{name}({})", render_values(heap, &args, active)?.join(", "))
309
+ }
247
310
  Object::Class { name, .. } => format!("<{name} object>"),
248
311
  _ => return Err("instance has an invalid class".into()),
249
312
  },
@@ -33,6 +33,40 @@ class QueueFull(Exception):
33
33
  pass
34
34
 
35
35
 
36
+ class InvalidStateError(Exception):
37
+ pass
38
+
39
+
40
+ class Handle:
41
+ def __init__(self, callback, args):
42
+ self._callback = callback
43
+ self._args = args
44
+ self._cancelled = False
45
+
46
+ def cancel(self):
47
+ self._cancelled = True
48
+
49
+ def cancelled(self):
50
+ return self._cancelled
51
+
52
+ def _run(self):
53
+ if self._cancelled:
54
+ return
55
+ try:
56
+ self._callback(*self._args)
57
+ except Exception:
58
+ pass
59
+
60
+
61
+ class TimerHandle(Handle):
62
+ def __init__(self, when, callback, args):
63
+ Handle.__init__(self, callback, args)
64
+ self._when = when
65
+
66
+ def when(self):
67
+ return self._when
68
+
69
+
36
70
  class _Sleep:
37
71
  def __init__(self, delay):
38
72
  self.delay = delay
@@ -87,6 +121,100 @@ class _TimeoutWait:
87
121
  self.timed_out = False
88
122
 
89
123
 
124
+ class Future:
125
+ def __init__(self, *, loop=None):
126
+ if loop is None:
127
+ loop = get_running_loop()
128
+ self._loop = loop
129
+ self._done = False
130
+ self._cancelled = False
131
+ self._result = None
132
+ self._exception = None
133
+ self._waiters = []
134
+ self._callbacks = []
135
+
136
+ def get_loop(self):
137
+ return self._loop
138
+
139
+ def done(self):
140
+ return self._done
141
+
142
+ def cancelled(self):
143
+ return self._cancelled
144
+
145
+ def result(self):
146
+ if not self._done:
147
+ raise InvalidStateError("Result is not ready")
148
+ if self._cancelled:
149
+ raise CancelledError()
150
+ if self._exception is not None:
151
+ raise self._exception
152
+ return self._result
153
+
154
+ def exception(self):
155
+ if not self._done:
156
+ raise InvalidStateError("Exception is not set")
157
+ if self._cancelled:
158
+ raise CancelledError()
159
+ return self._exception
160
+
161
+ def add_done_callback(self, callback, *, context=None):
162
+ if self._done:
163
+ self._loop.call_soon(callback, self)
164
+ else:
165
+ self._callbacks.append(callback)
166
+
167
+ def remove_done_callback(self, callback):
168
+ previous = len(self._callbacks)
169
+ self._callbacks = [item for item in self._callbacks if item is not callback]
170
+ return previous - len(self._callbacks)
171
+
172
+ def cancel(self, msg=None):
173
+ if self._done:
174
+ return False
175
+ self._done = True
176
+ self._cancelled = True
177
+ self._loop._run_done_callbacks(self)
178
+ self._loop._wake_waiters(self)
179
+ return True
180
+
181
+ def set_result(self, result):
182
+ if self._done:
183
+ raise InvalidStateError("invalid state")
184
+ self._done = True
185
+ self._result = result
186
+ self._loop._run_done_callbacks(self)
187
+ self._loop._wake_waiters(self)
188
+
189
+ def set_exception(self, exception):
190
+ if self._done:
191
+ raise InvalidStateError("invalid state")
192
+ if not isinstance(exception, BaseException):
193
+ raise TypeError("invalid exception object")
194
+ self._done = True
195
+ self._exception = exception
196
+ self._loop._run_done_callbacks(self)
197
+ self._loop._wake_waiters(self)
198
+
199
+
200
+ class _GatheringFuture(Future):
201
+ def __init__(self, children, *, loop):
202
+ Future.__init__(self, loop=loop)
203
+ self._children = children
204
+ self._cancel_requested = False
205
+
206
+ def cancel(self, msg=None):
207
+ if self._done:
208
+ return False
209
+ cancelled = False
210
+ for child in self._children:
211
+ if child.cancel(msg=msg):
212
+ cancelled = True
213
+ if cancelled:
214
+ self._cancel_requested = True
215
+ return cancelled
216
+
217
+
90
218
  class Task:
91
219
  def __init__(self, coroutine, loop, name=None):
92
220
  if not _is_coroutine(coroutine):
@@ -145,7 +273,7 @@ class Task:
145
273
 
146
274
  def add_done_callback(self, callback, context=None):
147
275
  if self._done:
148
- callback(self)
276
+ self._loop.call_soon(callback, self)
149
277
  else:
150
278
  self._callbacks.append(callback)
151
279
 
@@ -583,6 +711,9 @@ class _Loop:
583
711
  self._ready.append(task)
584
712
  return task
585
713
 
714
+ def create_future(self):
715
+ return Future(loop=self)
716
+
586
717
  def _create_awaited_task(self, coroutine, parent):
587
718
  task = self.create_task(coroutine)
588
719
  task._logical_task = parent._logical_task
@@ -591,6 +722,22 @@ class _Loop:
591
722
  def time(self):
592
723
  return _monotonic_ns() / 1000000000
593
724
 
725
+ def call_soon(self, callback, *args, context=None):
726
+ handle = Handle(callback, args)
727
+ self._ready.append(handle)
728
+ return handle
729
+
730
+ def call_soon_threadsafe(self, callback, *args, context=None):
731
+ return self.call_soon(callback, *args, context=context)
732
+
733
+ def call_at(self, when, callback, *args, context=None):
734
+ handle = TimerHandle(when, callback, args)
735
+ self._timers.append((int(when * 1000000000), handle))
736
+ return handle
737
+
738
+ def call_later(self, delay, callback, *args, context=None):
739
+ return self.call_at(self.time() + delay, callback, *args, context=context)
740
+
594
741
  def _schedule(self, task, value):
595
742
  if not task._done:
596
743
  self._resource_waiters = [
@@ -608,6 +755,11 @@ class _Loop:
608
755
  self._ready.append(task)
609
756
 
610
757
  def _cancel(self, task):
758
+ dependency = task._waiting_on
759
+ if isinstance(dependency, (Task, Future)) and dependency.cancel():
760
+ return
761
+ if isinstance(dependency, _TimeoutWait) and dependency.child.cancel():
762
+ return
611
763
  if not task._started:
612
764
  task._done = True
613
765
  task._cancelled = True
@@ -627,11 +779,15 @@ class _Loop:
627
779
  now = _monotonic_ns()
628
780
  pending = []
629
781
  for timer_deadline, timer in self._timers:
782
+ if isinstance(timer, TimerHandle) and timer.cancelled():
783
+ continue
630
784
  if timer_deadline <= now:
631
785
  if isinstance(timer, _TimeoutWait):
632
786
  if timer.parent._waiting_on is timer:
633
787
  timer.timed_out = True
634
788
  timer.child.cancel()
789
+ elif isinstance(timer, TimerHandle):
790
+ self._ready.append(timer)
635
791
  else:
636
792
  self._schedule(timer, None)
637
793
  else:
@@ -639,10 +795,15 @@ class _Loop:
639
795
  self._timers = pending
640
796
 
641
797
  def _next_timer_token(self):
642
- if not self._timers:
798
+ active = [
799
+ item
800
+ for item in self._timers
801
+ if not isinstance(item[1], TimerHandle) or not item[1].cancelled()
802
+ ]
803
+ if not active:
643
804
  return None
644
- deadline = self._timers[0][0]
645
- for timer_deadline, timer in self._timers:
805
+ deadline = active[0][0]
806
+ for timer_deadline, timer in active:
646
807
  if timer_deadline < deadline:
647
808
  deadline = timer_deadline
648
809
  return ("timer", deadline)
@@ -661,31 +822,38 @@ class _Loop:
661
822
  for task, token in waiters:
662
823
  self._schedule(task, None)
663
824
 
664
- def _wake_waiters(self, task):
665
- waiters = task._waiters
666
- task._waiters = []
825
+ def _wake_waiters(self, future):
826
+ waiters = future._waiters
827
+ future._waiters = []
667
828
  for waiter in waiters:
668
829
  if isinstance(waiter, _TimeoutWait):
669
830
  waiter.parent._waiting_on = None
670
831
  if waiter.timed_out:
671
832
  self._schedule_failure(waiter.parent, TimeoutError())
672
- elif task._exception is not None:
673
- self._schedule_failure(waiter.parent, task._exception)
833
+ elif future._cancelled:
834
+ self._schedule_failure(waiter.parent, CancelledError())
835
+ elif future._exception is not None:
836
+ self._schedule_failure(waiter.parent, future._exception)
674
837
  else:
675
- self._schedule(waiter.parent, task._result)
676
- elif task._exception is not None:
677
- self._schedule_failure(waiter, task._exception)
838
+ self._schedule(waiter.parent, future._result)
839
+ elif future._cancelled:
840
+ if waiter._waiting_on is future:
841
+ waiter._waiting_on = None
842
+ self._schedule_failure(waiter, CancelledError())
843
+ elif future._exception is not None:
844
+ if waiter._waiting_on is future:
845
+ waiter._waiting_on = None
846
+ self._schedule_failure(waiter, future._exception)
678
847
  else:
679
- self._schedule(waiter, task._result)
848
+ if waiter._waiting_on is future:
849
+ waiter._waiting_on = None
850
+ self._schedule(waiter, future._result)
680
851
 
681
- def _run_done_callbacks(self, task):
682
- callbacks = task._callbacks
683
- task._callbacks = []
852
+ def _run_done_callbacks(self, future):
853
+ callbacks = future._callbacks
854
+ future._callbacks = []
684
855
  for callback in callbacks:
685
- try:
686
- callback(task)
687
- except Exception:
688
- pass
856
+ self.call_soon(callback, future)
689
857
 
690
858
  def _finish(self, task, result):
691
859
  task._done = True
@@ -700,18 +868,21 @@ class _Loop:
700
868
  self._wake_waiters(task)
701
869
  self._run_done_callbacks(task)
702
870
 
703
- def _block_on_task(self, task, awaited):
871
+ def _block_on_future(self, task, awaited):
704
872
  if awaited._done:
705
- if awaited._exception is not None:
873
+ if awaited._cancelled:
874
+ self._schedule_failure(task, CancelledError())
875
+ elif awaited._exception is not None:
706
876
  self._schedule_failure(task, awaited._exception)
707
877
  else:
708
878
  self._schedule(task, awaited._result)
709
879
  else:
880
+ task._waiting_on = awaited
710
881
  awaited._waiters.append(task)
711
882
 
712
883
  def _dispatch_yield(self, task, awaited):
713
- if isinstance(awaited, Task):
714
- self._block_on_task(task, awaited)
884
+ if isinstance(awaited, (Task, Future)):
885
+ self._block_on_future(task, awaited)
715
886
  elif isinstance(awaited, _Sleep):
716
887
  self._sleep(task, awaited.delay)
717
888
  elif isinstance(awaited, _EventWait):
@@ -738,7 +909,7 @@ class _Loop:
738
909
  awaited.condition._waiters.append(task)
739
910
  elif isinstance(awaited, _WaitFor):
740
911
  if awaited.task._done:
741
- self._block_on_task(task, awaited.task)
912
+ self._block_on_future(task, awaited.task)
742
913
  else:
743
914
  timeout = _TimeoutWait(task, awaited.task)
744
915
  task._waiting_on = timeout
@@ -749,7 +920,7 @@ class _Loop:
749
920
  elif isinstance(awaited, _ResourceWait):
750
921
  self._resource_waiters.append((task, awaited.token))
751
922
  elif _is_coroutine(awaited):
752
- self._block_on_task(task, self._create_awaited_task(awaited, task))
923
+ self._block_on_future(task, self._create_awaited_task(awaited, task))
753
924
  else:
754
925
  raise TypeError("object cannot be used in 'await' expression")
755
926
 
@@ -761,6 +932,9 @@ class _Loop:
761
932
  if not self._ready:
762
933
  self._wait_for_resources()
763
934
  task = self._ready.pop(0)
935
+ if isinstance(task, Handle):
936
+ task._run()
937
+ continue
764
938
  if task._done:
765
939
  continue
766
940
  task._started = True
@@ -783,6 +957,10 @@ def get_running_loop():
783
957
  return _running_loop
784
958
 
785
959
 
960
+ def get_event_loop():
961
+ return get_running_loop()
962
+
963
+
786
964
  def create_task(coroutine, name=None, context=None):
787
965
  return get_running_loop().create_task(coroutine, name)
788
966
 
@@ -791,6 +969,10 @@ def ensure_future(awaitable):
791
969
  return _ensure_task(awaitable)
792
970
 
793
971
 
972
+ def isfuture(value):
973
+ return isinstance(value, (Future, Task))
974
+
975
+
794
976
  def current_task(loop=None):
795
977
  if loop is None:
796
978
  loop = get_running_loop()
@@ -806,7 +988,7 @@ def all_tasks(loop=None):
806
988
 
807
989
 
808
990
  def _ensure_task(awaitable):
809
- if isinstance(awaitable, Task):
991
+ if isinstance(awaitable, (Task, Future)):
810
992
  return awaitable
811
993
  return create_task(awaitable)
812
994
 
@@ -816,17 +998,50 @@ async def sleep(delay, result=None):
816
998
  return result
817
999
 
818
1000
 
819
- async def gather(*coroutines, return_exceptions=False):
1001
+ def gather(*coroutines, return_exceptions=False):
820
1002
  tasks = [_ensure_task(coroutine) for coroutine in coroutines]
821
- results = []
1003
+ observed = []
822
1004
  for task in tasks:
823
- try:
824
- results.append(await task)
825
- except Exception as error:
1005
+ if task not in observed:
1006
+ observed.append(task)
1007
+ loop = get_running_loop()
1008
+ outer = _GatheringFuture(observed, loop=loop)
1009
+ if not tasks:
1010
+ outer.set_result([])
1011
+ return outer
1012
+
1013
+ results = [None for task in tasks]
1014
+ completed = [False for task in tasks]
1015
+
1016
+ def child_completed(child):
1017
+ if outer.done():
1018
+ return
1019
+ if outer._cancel_requested:
1020
+ if all(task.done() for task in observed):
1021
+ outer.set_exception(CancelledError())
1022
+ return
1023
+ if child.cancelled():
1024
+ if not return_exceptions:
1025
+ outer.set_exception(CancelledError())
1026
+ return
1027
+ result = CancelledError()
1028
+ elif child.exception() is not None:
826
1029
  if not return_exceptions:
827
- raise
828
- results.append(error)
829
- return results
1030
+ outer.set_exception(child.exception())
1031
+ return
1032
+ result = child.exception()
1033
+ else:
1034
+ result = child.result()
1035
+ for index in range(len(tasks)):
1036
+ if tasks[index] is child:
1037
+ completed[index] = True
1038
+ results[index] = result
1039
+ if all(completed):
1040
+ outer.set_result(results)
1041
+
1042
+ for task in observed:
1043
+ task.add_done_callback(child_completed)
1044
+ return outer
830
1045
 
831
1046
 
832
1047
  async def wait_for(awaitable, timeout):
@@ -923,7 +1138,7 @@ async def wait(awaitables, timeout=None, return_when=ALL_COMPLETED):
923
1138
  if not tasks:
924
1139
  raise ValueError("Set of Tasks/Futures is empty")
925
1140
  for task in tasks:
926
- if not isinstance(task, Task):
1141
+ if not isinstance(task, (Task, Future)):
927
1142
  raise TypeError("Passing coroutines is forbidden, use tasks explicitly")
928
1143
  changed = Event()
929
1144
 
@@ -985,8 +1200,24 @@ def as_completed(awaitables, timeout=None):
985
1200
  yield next_result()
986
1201
 
987
1202
 
988
- async def shield(awaitable):
989
- return await _ensure_task(awaitable)
1203
+ def shield(awaitable):
1204
+ inner = _ensure_task(awaitable)
1205
+ if inner.done():
1206
+ return inner
1207
+ outer = get_running_loop().create_future()
1208
+
1209
+ def inner_completed(completed):
1210
+ if outer.done():
1211
+ return
1212
+ if completed.cancelled():
1213
+ outer.cancel()
1214
+ elif completed.exception() is not None:
1215
+ outer.set_exception(completed.exception())
1216
+ else:
1217
+ outer.set_result(completed.result())
1218
+
1219
+ inner.add_done_callback(inner_completed)
1220
+ return outer
990
1221
 
991
1222
 
992
1223
  async def create_subprocess_exec(program, *args, stdin=None, stdout=None, stderr=None,
@@ -200,6 +200,7 @@ impl Vm<'_> {
200
200
  name,
201
201
  metaclass,
202
202
  layout,
203
+ exception_base,
203
204
  is_dataclass,
204
205
  dataclass_fields,
205
206
  enum_members,
@@ -339,6 +340,16 @@ impl Vm<'_> {
339
340
  payload,
340
341
  attributes: InstanceAttributes::default(),
341
342
  })?;
343
+ if exception_base.is_some() {
344
+ let exception_args =
345
+ self.allocate_object(Object::Tuple(arguments.clone()))?;
346
+ self.state.heap.insert_attribute(
347
+ instance.object_id().expect("instances are heap objects"),
348
+ "args".into(),
349
+ exception_args,
350
+ &mut self.interp.resources,
351
+ )?;
352
+ }
342
353
  if is_dataclass {
343
354
  let mut values = Vec::new();
344
355
  for (index, (field, default)) in dataclass_fields.iter().enumerate() {
@@ -428,7 +439,10 @@ impl Vm<'_> {
428
439
  unreachable!("immediate initializer cannot suspend")
429
440
  }
430
441
  }
442
+ } else if exception_base.is_some() && !keyword_arguments.is_empty() {
443
+ return Err(format!("{name}() does not accept keyword arguments"));
431
444
  } else if layout == ClassLayout::Object
445
+ && exception_base.is_none()
432
446
  && (!arguments.is_empty() || !keyword_arguments.is_empty())
433
447
  {
434
448
  return Err(format!("{name}() takes no arguments"));
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes