shellsim 0.1.13__tar.gz → 0.1.14__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.14}/Cargo.lock +1 -1
  2. {shellsim-0.1.13 → shellsim-0.1.14}/Cargo.toml +1 -1
  3. {shellsim-0.1.13 → shellsim-0.1.14}/PKG-INFO +1 -1
  4. {shellsim-0.1.13 → shellsim-0.1.14}/python/native/Cargo.lock +2 -2
  5. {shellsim-0.1.13 → shellsim-0.1.14}/python/native/Cargo.toml +1 -1
  6. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/protocol.rs +63 -0
  7. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/asyncio.py +190 -28
  8. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/vm/calls.rs +14 -0
  9. {shellsim-0.1.13 → shellsim-0.1.14}/LICENSE +0 -0
  10. {shellsim-0.1.13 → shellsim-0.1.14}/README.md +0 -0
  11. {shellsim-0.1.13 → shellsim-0.1.14}/pyproject.toml +0 -0
  12. {shellsim-0.1.13 → shellsim-0.1.14}/python/native/src/lib.rs +0 -0
  13. {shellsim-0.1.13 → shellsim-0.1.14}/python/shellsim/__init__.py +0 -0
  14. {shellsim-0.1.13 → shellsim-0.1.14}/python/shellsim/__main__.py +0 -0
  15. {shellsim-0.1.13 → shellsim-0.1.14}/python/shellsim/_api.py +0 -0
  16. {shellsim-0.1.13 → shellsim-0.1.14}/python/shellsim/_cli.py +0 -0
  17. {shellsim-0.1.13 → shellsim-0.1.14}/python/shellsim/py.typed +0 -0
  18. {shellsim-0.1.13 → shellsim-0.1.14}/python/shellsim/python.py +0 -0
  19. {shellsim-0.1.13 → shellsim-0.1.14}/rust-toolchain.toml +0 -0
  20. {shellsim-0.1.13 → shellsim-0.1.14}/src/bin/shellsim-python.rs +0 -0
  21. {shellsim-0.1.13 → shellsim-0.1.14}/src/clock.rs +0 -0
  22. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/archives.rs +0 -0
  23. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/awk/ast.rs +0 -0
  24. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/awk/eval.rs +0 -0
  25. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/awk/lexer.rs +0 -0
  26. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/awk/parser.rs +0 -0
  27. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/awk.rs +0 -0
  28. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/builtins.rs +0 -0
  29. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/dd.rs +0 -0
  30. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/echo.rs +0 -0
  31. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/find.rs +0 -0
  32. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/fs.rs +0 -0
  33. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/git/apply.rs +0 -0
  34. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/git/commit.rs +0 -0
  35. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/git/compare.rs +0 -0
  36. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/git/config.rs +0 -0
  37. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/git/conflict.rs +0 -0
  38. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/git/diff.rs +0 -0
  39. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/git/ignore.rs +0 -0
  40. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/git/import.rs +0 -0
  41. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/git/log.rs +0 -0
  42. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/git/merge.rs +0 -0
  43. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/git/mod.rs +0 -0
  44. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/git/plumbing.rs +0 -0
  45. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/git/rebase.rs +0 -0
  46. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/git/refs.rs +0 -0
  47. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/git/repo.rs +0 -0
  48. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/git/stash.rs +0 -0
  49. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/git/switch.rs +0 -0
  50. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/git/worktree.rs +0 -0
  51. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/hashing.rs +0 -0
  52. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/makecmd.rs +0 -0
  53. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/mod.rs +0 -0
  54. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/net.rs +0 -0
  55. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/options.rs +0 -0
  56. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/patch.rs +0 -0
  57. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/pkg.rs +0 -0
  58. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/printf.rs +0 -0
  59. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/proc.rs +0 -0
  60. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/regex_compat.rs +0 -0
  61. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/rgcmd.rs +0 -0
  62. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/sed.rs +0 -0
  63. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/sort.rs +0 -0
  64. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/system.rs +0 -0
  65. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/tarcmd.rs +0 -0
  66. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/text.rs +0 -0
  67. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/unavailable.rs +0 -0
  68. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/util.rs +0 -0
  69. {shellsim-0.1.13 → shellsim-0.1.14}/src/commands/zipcmd.rs +0 -0
  70. {shellsim-0.1.13 → shellsim-0.1.14}/src/descriptors.rs +0 -0
  71. {shellsim-0.1.13 → shellsim-0.1.14}/src/exec.rs +0 -0
  72. {shellsim-0.1.13 → shellsim-0.1.14}/src/expand.rs +0 -0
  73. {shellsim-0.1.13 → shellsim-0.1.14}/src/harness.rs +0 -0
  74. {shellsim-0.1.13 → shellsim-0.1.14}/src/harness_manager.rs +0 -0
  75. {shellsim-0.1.13 → shellsim-0.1.14}/src/hashes.rs +0 -0
  76. {shellsim-0.1.13 → shellsim-0.1.14}/src/host_ingest.rs +0 -0
  77. {shellsim-0.1.13 → shellsim-0.1.14}/src/interp.rs +0 -0
  78. {shellsim-0.1.13 → shellsim-0.1.14}/src/jqcmd.rs +0 -0
  79. {shellsim-0.1.13 → shellsim-0.1.14}/src/lib.rs +0 -0
  80. {shellsim-0.1.13 → shellsim-0.1.14}/src/main.rs +0 -0
  81. {shellsim-0.1.13 → shellsim-0.1.14}/src/mcp.rs +0 -0
  82. {shellsim-0.1.13 → shellsim-0.1.14}/src/net.rs +0 -0
  83. {shellsim-0.1.13 → shellsim-0.1.14}/src/netcmd.rs +0 -0
  84. {shellsim-0.1.13 → shellsim-0.1.14}/src/process.rs +0 -0
  85. {shellsim-0.1.13 → shellsim-0.1.14}/src/pseudo_fs.rs +0 -0
  86. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/ast.rs +0 -0
  87. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/bytecode.rs +0 -0
  88. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/compiler.rs +0 -0
  89. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/filesystem.rs +0 -0
  90. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/heap.rs +0 -0
  91. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/http.rs +0 -0
  92. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/lexer.rs +0 -0
  93. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/mapping.rs +0 -0
  94. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/mod.rs +0 -0
  95. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/native.rs +0 -0
  96. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/number.rs +0 -0
  97. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/object_model.rs +0 -0
  98. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/parser.rs +0 -0
  99. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/process.rs +0 -0
  100. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/slice.rs +0 -0
  101. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/source.rs +0 -0
  102. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/argparse.rs +0 -0
  103. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/asyncio.rs +0 -0
  104. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/base64.rs +0 -0
  105. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/bisect.rs +0 -0
  106. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/collections.rs +0 -0
  107. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/core.rs +0 -0
  108. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/dataclasses.rs +0 -0
  109. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/enum.rs +0 -0
  110. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/frozen.rs +0 -0
  111. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/functools.rs +0 -0
  112. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/hashlib.rs +0 -0
  113. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/heapq.rs +0 -0
  114. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/http.rs +0 -0
  115. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/importlib.rs +0 -0
  116. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/itertools.rs +0 -0
  117. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/json.rs +0 -0
  118. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/math.rs +0 -0
  119. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/mod.rs +0 -0
  120. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/numpy.rs +0 -0
  121. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/os.rs +0 -0
  122. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/pytest.rs +0 -0
  123. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/re.rs +0 -0
  124. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/abc.py +0 -0
  125. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/base64.py +0 -0
  126. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/cmath.py +0 -0
  127. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/codecs.py +0 -0
  128. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/collections.py +0 -0
  129. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/complex.py +0 -0
  130. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/copy.py +0 -0
  131. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/csv.py +0 -0
  132. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/datetime.py +0 -0
  133. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/glob.py +0 -0
  134. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/hashlib.py +0 -0
  135. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/http.py +0 -0
  136. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/http_client.py +0 -0
  137. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/importlib.py +0 -0
  138. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/io.py +0 -0
  139. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/json.py +0 -0
  140. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/logging.py +0 -0
  141. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/numpy_random.py +0 -0
  142. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/os.py +0 -0
  143. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/pathlib.py +0 -0
  144. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/pytest.py +0 -0
  145. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/random.py +0 -0
  146. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/shutil.py +0 -0
  147. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/statistics.py +0 -0
  148. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/struct.py +0 -0
  149. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/subprocess.py +0 -0
  150. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/tempfile.py +0 -0
  151. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/textwrap.py +0 -0
  152. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/urllib.py +0 -0
  153. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/urllib_error.py +0 -0
  154. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/urllib_request.py +0 -0
  155. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/uuid.py +0 -0
  156. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/zipfile.py +0 -0
  157. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/source/zlib.py +0 -0
  158. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/string.rs +0 -0
  159. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/struct.rs +0 -0
  160. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/subprocess.rs +0 -0
  161. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/sys.rs +0 -0
  162. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/time.rs +0 -0
  163. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/typing.rs +0 -0
  164. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/unittest.rs +0 -0
  165. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/vfs.rs +0 -0
  166. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/stdlib/zlib.rs +0 -0
  167. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/string.rs +0 -0
  168. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/token.rs +0 -0
  169. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/vm/dispatch.rs +0 -0
  170. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/vm/host.rs +0 -0
  171. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/vm/iteration.rs +0 -0
  172. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/vm/namespace.rs +0 -0
  173. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/vm/native_runtime.rs +0 -0
  174. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/vm/objects.rs +0 -0
  175. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/vm/operations.rs +0 -0
  176. {shellsim-0.1.13 → shellsim-0.1.14}/src/python/vm.rs +0 -0
  177. {shellsim-0.1.13 → shellsim-0.1.14}/src/resources.rs +0 -0
  178. {shellsim-0.1.13 → shellsim-0.1.14}/src/sandbox.rs +0 -0
  179. {shellsim-0.1.13 → shellsim-0.1.14}/src/scenario.rs +0 -0
  180. {shellsim-0.1.13 → shellsim-0.1.14}/src/scheduler.rs +0 -0
  181. {shellsim-0.1.13 → shellsim-0.1.14}/src/shell.rs +0 -0
  182. {shellsim-0.1.13 → shellsim-0.1.14}/src/telemetry.rs +0 -0
  183. {shellsim-0.1.13 → shellsim-0.1.14}/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.14"
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.14"
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.14
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.14"
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.14"
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.14"
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,82 @@ 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
+
90
200
  class Task:
91
201
  def __init__(self, coroutine, loop, name=None):
92
202
  if not _is_coroutine(coroutine):
@@ -145,7 +255,7 @@ class Task:
145
255
 
146
256
  def add_done_callback(self, callback, context=None):
147
257
  if self._done:
148
- callback(self)
258
+ self._loop.call_soon(callback, self)
149
259
  else:
150
260
  self._callbacks.append(callback)
151
261
 
@@ -583,6 +693,9 @@ class _Loop:
583
693
  self._ready.append(task)
584
694
  return task
585
695
 
696
+ def create_future(self):
697
+ return Future(loop=self)
698
+
586
699
  def _create_awaited_task(self, coroutine, parent):
587
700
  task = self.create_task(coroutine)
588
701
  task._logical_task = parent._logical_task
@@ -591,6 +704,22 @@ class _Loop:
591
704
  def time(self):
592
705
  return _monotonic_ns() / 1000000000
593
706
 
707
+ def call_soon(self, callback, *args, context=None):
708
+ handle = Handle(callback, args)
709
+ self._ready.append(handle)
710
+ return handle
711
+
712
+ def call_soon_threadsafe(self, callback, *args, context=None):
713
+ return self.call_soon(callback, *args, context=context)
714
+
715
+ def call_at(self, when, callback, *args, context=None):
716
+ handle = TimerHandle(when, callback, args)
717
+ self._timers.append((int(when * 1000000000), handle))
718
+ return handle
719
+
720
+ def call_later(self, delay, callback, *args, context=None):
721
+ return self.call_at(self.time() + delay, callback, *args, context=context)
722
+
594
723
  def _schedule(self, task, value):
595
724
  if not task._done:
596
725
  self._resource_waiters = [
@@ -608,6 +737,8 @@ class _Loop:
608
737
  self._ready.append(task)
609
738
 
610
739
  def _cancel(self, task):
740
+ if isinstance(task._waiting_on, Future) and task._waiting_on.cancel():
741
+ return
611
742
  if not task._started:
612
743
  task._done = True
613
744
  task._cancelled = True
@@ -627,11 +758,15 @@ class _Loop:
627
758
  now = _monotonic_ns()
628
759
  pending = []
629
760
  for timer_deadline, timer in self._timers:
761
+ if isinstance(timer, TimerHandle) and timer.cancelled():
762
+ continue
630
763
  if timer_deadline <= now:
631
764
  if isinstance(timer, _TimeoutWait):
632
765
  if timer.parent._waiting_on is timer:
633
766
  timer.timed_out = True
634
767
  timer.child.cancel()
768
+ elif isinstance(timer, TimerHandle):
769
+ self._ready.append(timer)
635
770
  else:
636
771
  self._schedule(timer, None)
637
772
  else:
@@ -639,10 +774,15 @@ class _Loop:
639
774
  self._timers = pending
640
775
 
641
776
  def _next_timer_token(self):
642
- if not self._timers:
777
+ active = [
778
+ item
779
+ for item in self._timers
780
+ if not isinstance(item[1], TimerHandle) or not item[1].cancelled()
781
+ ]
782
+ if not active:
643
783
  return None
644
- deadline = self._timers[0][0]
645
- for timer_deadline, timer in self._timers:
784
+ deadline = active[0][0]
785
+ for timer_deadline, timer in active:
646
786
  if timer_deadline < deadline:
647
787
  deadline = timer_deadline
648
788
  return ("timer", deadline)
@@ -661,31 +801,38 @@ class _Loop:
661
801
  for task, token in waiters:
662
802
  self._schedule(task, None)
663
803
 
664
- def _wake_waiters(self, task):
665
- waiters = task._waiters
666
- task._waiters = []
804
+ def _wake_waiters(self, future):
805
+ waiters = future._waiters
806
+ future._waiters = []
667
807
  for waiter in waiters:
668
808
  if isinstance(waiter, _TimeoutWait):
669
809
  waiter.parent._waiting_on = None
670
810
  if waiter.timed_out:
671
811
  self._schedule_failure(waiter.parent, TimeoutError())
672
- elif task._exception is not None:
673
- self._schedule_failure(waiter.parent, task._exception)
812
+ elif future._cancelled:
813
+ self._schedule_failure(waiter.parent, CancelledError())
814
+ elif future._exception is not None:
815
+ self._schedule_failure(waiter.parent, future._exception)
674
816
  else:
675
- self._schedule(waiter.parent, task._result)
676
- elif task._exception is not None:
677
- self._schedule_failure(waiter, task._exception)
817
+ self._schedule(waiter.parent, future._result)
818
+ elif future._cancelled:
819
+ if waiter._waiting_on is future:
820
+ waiter._waiting_on = None
821
+ self._schedule_failure(waiter, CancelledError())
822
+ elif future._exception is not None:
823
+ if waiter._waiting_on is future:
824
+ waiter._waiting_on = None
825
+ self._schedule_failure(waiter, future._exception)
678
826
  else:
679
- self._schedule(waiter, task._result)
827
+ if waiter._waiting_on is future:
828
+ waiter._waiting_on = None
829
+ self._schedule(waiter, future._result)
680
830
 
681
- def _run_done_callbacks(self, task):
682
- callbacks = task._callbacks
683
- task._callbacks = []
831
+ def _run_done_callbacks(self, future):
832
+ callbacks = future._callbacks
833
+ future._callbacks = []
684
834
  for callback in callbacks:
685
- try:
686
- callback(task)
687
- except Exception:
688
- pass
835
+ self.call_soon(callback, future)
689
836
 
690
837
  def _finish(self, task, result):
691
838
  task._done = True
@@ -700,18 +847,22 @@ class _Loop:
700
847
  self._wake_waiters(task)
701
848
  self._run_done_callbacks(task)
702
849
 
703
- def _block_on_task(self, task, awaited):
850
+ def _block_on_future(self, task, awaited):
704
851
  if awaited._done:
705
- if awaited._exception is not None:
852
+ if awaited._cancelled:
853
+ self._schedule_failure(task, CancelledError())
854
+ elif awaited._exception is not None:
706
855
  self._schedule_failure(task, awaited._exception)
707
856
  else:
708
857
  self._schedule(task, awaited._result)
709
858
  else:
859
+ if isinstance(awaited, Future):
860
+ task._waiting_on = awaited
710
861
  awaited._waiters.append(task)
711
862
 
712
863
  def _dispatch_yield(self, task, awaited):
713
- if isinstance(awaited, Task):
714
- self._block_on_task(task, awaited)
864
+ if isinstance(awaited, (Task, Future)):
865
+ self._block_on_future(task, awaited)
715
866
  elif isinstance(awaited, _Sleep):
716
867
  self._sleep(task, awaited.delay)
717
868
  elif isinstance(awaited, _EventWait):
@@ -738,7 +889,7 @@ class _Loop:
738
889
  awaited.condition._waiters.append(task)
739
890
  elif isinstance(awaited, _WaitFor):
740
891
  if awaited.task._done:
741
- self._block_on_task(task, awaited.task)
892
+ self._block_on_future(task, awaited.task)
742
893
  else:
743
894
  timeout = _TimeoutWait(task, awaited.task)
744
895
  task._waiting_on = timeout
@@ -749,7 +900,7 @@ class _Loop:
749
900
  elif isinstance(awaited, _ResourceWait):
750
901
  self._resource_waiters.append((task, awaited.token))
751
902
  elif _is_coroutine(awaited):
752
- self._block_on_task(task, self._create_awaited_task(awaited, task))
903
+ self._block_on_future(task, self._create_awaited_task(awaited, task))
753
904
  else:
754
905
  raise TypeError("object cannot be used in 'await' expression")
755
906
 
@@ -761,6 +912,9 @@ class _Loop:
761
912
  if not self._ready:
762
913
  self._wait_for_resources()
763
914
  task = self._ready.pop(0)
915
+ if isinstance(task, Handle):
916
+ task._run()
917
+ continue
764
918
  if task._done:
765
919
  continue
766
920
  task._started = True
@@ -783,6 +937,10 @@ def get_running_loop():
783
937
  return _running_loop
784
938
 
785
939
 
940
+ def get_event_loop():
941
+ return get_running_loop()
942
+
943
+
786
944
  def create_task(coroutine, name=None, context=None):
787
945
  return get_running_loop().create_task(coroutine, name)
788
946
 
@@ -791,6 +949,10 @@ def ensure_future(awaitable):
791
949
  return _ensure_task(awaitable)
792
950
 
793
951
 
952
+ def isfuture(value):
953
+ return isinstance(value, (Future, Task))
954
+
955
+
794
956
  def current_task(loop=None):
795
957
  if loop is None:
796
958
  loop = get_running_loop()
@@ -806,7 +968,7 @@ def all_tasks(loop=None):
806
968
 
807
969
 
808
970
  def _ensure_task(awaitable):
809
- if isinstance(awaitable, Task):
971
+ if isinstance(awaitable, (Task, Future)):
810
972
  return awaitable
811
973
  return create_task(awaitable)
812
974
 
@@ -923,7 +1085,7 @@ async def wait(awaitables, timeout=None, return_when=ALL_COMPLETED):
923
1085
  if not tasks:
924
1086
  raise ValueError("Set of Tasks/Futures is empty")
925
1087
  for task in tasks:
926
- if not isinstance(task, Task):
1088
+ if not isinstance(task, (Task, Future)):
927
1089
  raise TypeError("Passing coroutines is forbidden, use tasks explicitly")
928
1090
  changed = Event()
929
1091
 
@@ -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
File without changes
File without changes
File without changes
File without changes
File without changes