shellsim 0.1.11__tar.gz → 0.1.12__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.
- {shellsim-0.1.11 → shellsim-0.1.12}/Cargo.lock +1 -1
- {shellsim-0.1.11 → shellsim-0.1.12}/Cargo.toml +1 -1
- {shellsim-0.1.11 → shellsim-0.1.12}/PKG-INFO +1 -1
- {shellsim-0.1.11 → shellsim-0.1.12}/python/native/Cargo.lock +2 -2
- {shellsim-0.1.11 → shellsim-0.1.12}/python/native/Cargo.toml +1 -1
- {shellsim-0.1.11 → shellsim-0.1.12}/src/descriptors.rs +28 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/asyncio.py +324 -16
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/vm/native_runtime.rs +36 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/LICENSE +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/README.md +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/pyproject.toml +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/python/native/src/lib.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/python/shellsim/__init__.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/python/shellsim/__main__.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/python/shellsim/_api.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/python/shellsim/_cli.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/python/shellsim/py.typed +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/python/shellsim/python.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/rust-toolchain.toml +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/bin/shellsim-python.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/clock.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/archives.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/awk/ast.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/awk/eval.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/awk/lexer.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/awk/parser.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/awk.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/builtins.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/dd.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/echo.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/find.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/fs.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/git/apply.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/git/commit.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/git/compare.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/git/config.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/git/conflict.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/git/diff.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/git/ignore.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/git/import.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/git/log.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/git/merge.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/git/mod.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/git/plumbing.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/git/rebase.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/git/refs.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/git/repo.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/git/stash.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/git/switch.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/git/worktree.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/hashing.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/makecmd.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/mod.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/net.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/options.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/patch.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/pkg.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/printf.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/proc.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/regex_compat.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/rgcmd.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/sed.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/sort.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/system.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/tarcmd.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/text.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/unavailable.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/util.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/commands/zipcmd.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/exec.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/expand.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/harness.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/harness_manager.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/hashes.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/host_ingest.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/interp.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/jqcmd.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/lib.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/main.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/mcp.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/net.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/netcmd.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/process.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/pseudo_fs.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/ast.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/bytecode.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/compiler.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/filesystem.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/heap.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/http.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/lexer.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/mapping.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/mod.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/native.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/number.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/object_model.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/parser.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/process.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/protocol.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/slice.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/source.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/argparse.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/asyncio.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/base64.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/bisect.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/collections.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/core.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/dataclasses.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/enum.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/frozen.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/functools.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/hashlib.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/heapq.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/http.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/importlib.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/itertools.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/json.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/math.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/mod.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/numpy.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/os.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/pytest.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/re.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/abc.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/base64.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/cmath.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/codecs.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/collections.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/complex.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/copy.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/csv.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/datetime.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/glob.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/hashlib.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/http.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/http_client.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/importlib.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/io.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/json.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/logging.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/numpy_random.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/os.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/pathlib.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/pytest.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/random.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/shutil.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/statistics.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/struct.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/subprocess.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/tempfile.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/textwrap.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/urllib.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/urllib_error.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/urllib_request.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/uuid.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/zipfile.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/source/zlib.py +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/string.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/struct.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/subprocess.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/sys.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/time.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/typing.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/unittest.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/vfs.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/stdlib/zlib.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/string.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/token.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/vm/calls.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/vm/dispatch.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/vm/host.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/vm/iteration.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/vm/namespace.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/vm/objects.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/vm/operations.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/python/vm.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/resources.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/sandbox.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/scenario.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/scheduler.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/shell.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/telemetry.rs +0 -0
- {shellsim-0.1.11 → shellsim-0.1.12}/src/vfs.rs +0 -0
|
@@ -406,7 +406,7 @@ dependencies = [
|
|
|
406
406
|
|
|
407
407
|
[[package]]
|
|
408
408
|
name = "shellsim"
|
|
409
|
-
version = "0.1.
|
|
409
|
+
version = "0.1.12"
|
|
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.
|
|
430
|
+
version = "0.1.12"
|
|
431
431
|
dependencies = [
|
|
432
432
|
"pyo3",
|
|
433
433
|
"serde",
|
|
@@ -194,6 +194,34 @@ impl DescriptorArena {
|
|
|
194
194
|
})
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
+
/// Whether retrying a blocked input read can now make progress.
|
|
198
|
+
pub(crate) fn input_readable(&self, id: DescriptionId) -> bool {
|
|
199
|
+
self.descriptions
|
|
200
|
+
.get(&id)
|
|
201
|
+
.is_some_and(|entry| match &entry.description {
|
|
202
|
+
OpenDescription::Input {
|
|
203
|
+
bytes,
|
|
204
|
+
cursor,
|
|
205
|
+
closed,
|
|
206
|
+
} => *cursor < bytes.len() || *closed,
|
|
207
|
+
_ => false,
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/// Whether retrying a blocked pipe read can now return bytes or EOF.
|
|
212
|
+
pub(crate) fn pipe_readable(&self, id: PipeId) -> bool {
|
|
213
|
+
self.pipes
|
|
214
|
+
.get(&id)
|
|
215
|
+
.is_some_and(|pipe| !pipe.bytes.is_empty() || pipe.writers == 0)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/// Whether retrying a blocked pipe write can now write or report a closed reader.
|
|
219
|
+
pub(crate) fn pipe_writable(&self, id: PipeId) -> bool {
|
|
220
|
+
self.pipes
|
|
221
|
+
.get(&id)
|
|
222
|
+
.is_some_and(|pipe| pipe.readers == 0 || pipe.bytes.len() < pipe.capacity)
|
|
223
|
+
}
|
|
224
|
+
|
|
197
225
|
/// Append explicitly supplied bytes to a streaming input description.
|
|
198
226
|
pub fn append_input(&mut self, id: DescriptionId, input: &[u8]) -> Result<(), DescriptorError> {
|
|
199
227
|
let entry = self
|
|
@@ -15,6 +15,11 @@ from _asyncio import (
|
|
|
15
15
|
|
|
16
16
|
_running_loop = None
|
|
17
17
|
|
|
18
|
+
ALL_COMPLETED = "ALL_COMPLETED"
|
|
19
|
+
FIRST_COMPLETED = "FIRST_COMPLETED"
|
|
20
|
+
FIRST_EXCEPTION = "FIRST_EXCEPTION"
|
|
21
|
+
TimeoutError = TimeoutError
|
|
22
|
+
|
|
18
23
|
|
|
19
24
|
class CancelledError(Exception):
|
|
20
25
|
pass
|
|
@@ -40,6 +45,16 @@ class _LockWait:
|
|
|
40
45
|
self.lock = lock
|
|
41
46
|
|
|
42
47
|
|
|
48
|
+
class _SemaphoreWait:
|
|
49
|
+
def __init__(self, semaphore):
|
|
50
|
+
self.semaphore = semaphore
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class _ConditionWait:
|
|
54
|
+
def __init__(self, condition):
|
|
55
|
+
self.condition = condition
|
|
56
|
+
|
|
57
|
+
|
|
43
58
|
class _ResourceWait:
|
|
44
59
|
def __init__(self, token):
|
|
45
60
|
self.token = token
|
|
@@ -59,7 +74,7 @@ class _TimeoutWait:
|
|
|
59
74
|
|
|
60
75
|
|
|
61
76
|
class Task:
|
|
62
|
-
def __init__(self, coroutine, loop):
|
|
77
|
+
def __init__(self, coroutine, loop, name=None):
|
|
63
78
|
if not _is_coroutine(coroutine):
|
|
64
79
|
raise TypeError("a coroutine was expected")
|
|
65
80
|
self._coroutine = coroutine
|
|
@@ -72,6 +87,9 @@ class Task:
|
|
|
72
87
|
self._send_value = None
|
|
73
88
|
self._started = False
|
|
74
89
|
self._waiting_on = None
|
|
90
|
+
self._name = name
|
|
91
|
+
self._callbacks = []
|
|
92
|
+
self._cancel_requests = 0
|
|
75
93
|
|
|
76
94
|
def done(self):
|
|
77
95
|
return self._done
|
|
@@ -86,9 +104,45 @@ class Task:
|
|
|
86
104
|
raise self._exception
|
|
87
105
|
return self._result
|
|
88
106
|
|
|
89
|
-
def
|
|
107
|
+
def exception(self):
|
|
108
|
+
if not self._done:
|
|
109
|
+
raise RuntimeError("task is not complete")
|
|
110
|
+
if self._cancelled:
|
|
111
|
+
raise CancelledError()
|
|
112
|
+
return self._exception
|
|
113
|
+
|
|
114
|
+
def get_coro(self):
|
|
115
|
+
return self._coroutine
|
|
116
|
+
|
|
117
|
+
def get_name(self):
|
|
118
|
+
return self._name
|
|
119
|
+
|
|
120
|
+
def set_name(self, name):
|
|
121
|
+
self._name = str(name)
|
|
122
|
+
|
|
123
|
+
def cancelling(self):
|
|
124
|
+
return self._cancel_requests
|
|
125
|
+
|
|
126
|
+
def uncancel(self):
|
|
127
|
+
if self._cancel_requests:
|
|
128
|
+
self._cancel_requests -= 1
|
|
129
|
+
return self._cancel_requests
|
|
130
|
+
|
|
131
|
+
def add_done_callback(self, callback, context=None):
|
|
132
|
+
if self._done:
|
|
133
|
+
callback(self)
|
|
134
|
+
else:
|
|
135
|
+
self._callbacks.append(callback)
|
|
136
|
+
|
|
137
|
+
def remove_done_callback(self, callback):
|
|
138
|
+
previous = len(self._callbacks)
|
|
139
|
+
self._callbacks = [item for item in self._callbacks if item is not callback]
|
|
140
|
+
return previous - len(self._callbacks)
|
|
141
|
+
|
|
142
|
+
def cancel(self, msg=None):
|
|
90
143
|
if self._done:
|
|
91
144
|
return False
|
|
145
|
+
self._cancel_requests += 1
|
|
92
146
|
self._loop._cancel(self)
|
|
93
147
|
return True
|
|
94
148
|
|
|
@@ -181,6 +235,137 @@ class Lock:
|
|
|
181
235
|
self.release()
|
|
182
236
|
|
|
183
237
|
|
|
238
|
+
class Semaphore:
|
|
239
|
+
def __init__(self, value=1):
|
|
240
|
+
if value < 0:
|
|
241
|
+
raise ValueError("Semaphore initial value must be >= 0")
|
|
242
|
+
self._value = value
|
|
243
|
+
self._waiters = []
|
|
244
|
+
|
|
245
|
+
def locked(self):
|
|
246
|
+
return self._value == 0
|
|
247
|
+
|
|
248
|
+
async def acquire(self):
|
|
249
|
+
if self._value > 0:
|
|
250
|
+
self._value -= 1
|
|
251
|
+
return True
|
|
252
|
+
return await _SemaphoreWait(self)
|
|
253
|
+
|
|
254
|
+
def release(self):
|
|
255
|
+
if self._waiters:
|
|
256
|
+
task = self._waiters.pop(0)
|
|
257
|
+
task._loop._schedule(task, True)
|
|
258
|
+
else:
|
|
259
|
+
self._value += 1
|
|
260
|
+
|
|
261
|
+
async def __aenter__(self):
|
|
262
|
+
await self.acquire()
|
|
263
|
+
return self
|
|
264
|
+
|
|
265
|
+
async def __aexit__(self, kind, value, traceback):
|
|
266
|
+
self.release()
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class BoundedSemaphore(Semaphore):
|
|
270
|
+
def __init__(self, value=1):
|
|
271
|
+
super().__init__(value)
|
|
272
|
+
self._bound_value = value
|
|
273
|
+
|
|
274
|
+
def release(self):
|
|
275
|
+
if not self._waiters and self._value >= self._bound_value:
|
|
276
|
+
raise ValueError("BoundedSemaphore released too many times")
|
|
277
|
+
super().release()
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class Condition:
|
|
281
|
+
def __init__(self, lock=None):
|
|
282
|
+
self._lock = lock if lock is not None else Lock()
|
|
283
|
+
self._waiters = []
|
|
284
|
+
|
|
285
|
+
def locked(self):
|
|
286
|
+
return self._lock.locked()
|
|
287
|
+
|
|
288
|
+
async def acquire(self):
|
|
289
|
+
return await self._lock.acquire()
|
|
290
|
+
|
|
291
|
+
def release(self):
|
|
292
|
+
self._lock.release()
|
|
293
|
+
|
|
294
|
+
async def wait(self):
|
|
295
|
+
if not self.locked():
|
|
296
|
+
raise RuntimeError("cannot wait on un-acquired lock")
|
|
297
|
+
self.release()
|
|
298
|
+
try:
|
|
299
|
+
return await _ConditionWait(self)
|
|
300
|
+
finally:
|
|
301
|
+
await self.acquire()
|
|
302
|
+
|
|
303
|
+
async def wait_for(self, predicate):
|
|
304
|
+
result = predicate()
|
|
305
|
+
while not result:
|
|
306
|
+
await self.wait()
|
|
307
|
+
result = predicate()
|
|
308
|
+
return result
|
|
309
|
+
|
|
310
|
+
def notify(self, n=1):
|
|
311
|
+
if not self.locked():
|
|
312
|
+
raise RuntimeError("cannot notify on un-acquired lock")
|
|
313
|
+
for _ in range(min(n, len(self._waiters))):
|
|
314
|
+
task = self._waiters.pop(0)
|
|
315
|
+
task._loop._schedule(task, True)
|
|
316
|
+
|
|
317
|
+
def notify_all(self):
|
|
318
|
+
self.notify(len(self._waiters))
|
|
319
|
+
|
|
320
|
+
async def __aenter__(self):
|
|
321
|
+
await self.acquire()
|
|
322
|
+
return self
|
|
323
|
+
|
|
324
|
+
async def __aexit__(self, kind, value, traceback):
|
|
325
|
+
self.release()
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
class TaskGroup:
|
|
329
|
+
def __init__(self):
|
|
330
|
+
self._tasks = []
|
|
331
|
+
self._entered = False
|
|
332
|
+
|
|
333
|
+
async def __aenter__(self):
|
|
334
|
+
self._entered = True
|
|
335
|
+
return self
|
|
336
|
+
|
|
337
|
+
def create_task(self, coroutine, name=None, context=None):
|
|
338
|
+
if not self._entered:
|
|
339
|
+
raise RuntimeError("TaskGroup has not been entered")
|
|
340
|
+
task = create_task(coroutine, name=name)
|
|
341
|
+
self._tasks.append(task)
|
|
342
|
+
return task
|
|
343
|
+
|
|
344
|
+
async def __aexit__(self, kind, value, traceback):
|
|
345
|
+
self._entered = False
|
|
346
|
+
if kind is not None:
|
|
347
|
+
for task in self._tasks:
|
|
348
|
+
task.cancel()
|
|
349
|
+
await gather(*self._tasks, return_exceptions=True)
|
|
350
|
+
return False
|
|
351
|
+
if not self._tasks:
|
|
352
|
+
return False
|
|
353
|
+
done, pending = await wait(self._tasks, return_when=FIRST_EXCEPTION)
|
|
354
|
+
failed = []
|
|
355
|
+
for task in self._tasks:
|
|
356
|
+
if task not in done:
|
|
357
|
+
continue
|
|
358
|
+
if not task.cancelled() and task.exception() is not None:
|
|
359
|
+
failed.append(task.exception())
|
|
360
|
+
if failed:
|
|
361
|
+
for task in pending:
|
|
362
|
+
task.cancel()
|
|
363
|
+
await gather(*pending, return_exceptions=True)
|
|
364
|
+
raise failed[0]
|
|
365
|
+
await gather(*pending)
|
|
366
|
+
return False
|
|
367
|
+
|
|
368
|
+
|
|
184
369
|
class StreamReader:
|
|
185
370
|
def __init__(self, process, fd):
|
|
186
371
|
self._process = process
|
|
@@ -299,9 +484,12 @@ class _Loop:
|
|
|
299
484
|
self._ready = []
|
|
300
485
|
self._timers = []
|
|
301
486
|
self._resource_waiters = []
|
|
487
|
+
self._tasks = []
|
|
488
|
+
self._current = None
|
|
302
489
|
|
|
303
|
-
def create_task(self, coroutine):
|
|
304
|
-
task = Task(coroutine, self)
|
|
490
|
+
def create_task(self, coroutine, name=None):
|
|
491
|
+
task = Task(coroutine, self, name)
|
|
492
|
+
self._tasks.append(task)
|
|
305
493
|
self._ready.append(task)
|
|
306
494
|
return task
|
|
307
495
|
|
|
@@ -327,6 +515,7 @@ class _Loop:
|
|
|
327
515
|
task._cancelled = True
|
|
328
516
|
task._exception = CancelledError()
|
|
329
517
|
self._wake_waiters(task)
|
|
518
|
+
self._run_done_callbacks(task)
|
|
330
519
|
else:
|
|
331
520
|
self._schedule_failure(task, CancelledError())
|
|
332
521
|
|
|
@@ -391,16 +580,27 @@ class _Loop:
|
|
|
391
580
|
else:
|
|
392
581
|
self._schedule(waiter, task._result)
|
|
393
582
|
|
|
583
|
+
def _run_done_callbacks(self, task):
|
|
584
|
+
callbacks = task._callbacks
|
|
585
|
+
task._callbacks = []
|
|
586
|
+
for callback in callbacks:
|
|
587
|
+
try:
|
|
588
|
+
callback(task)
|
|
589
|
+
except Exception:
|
|
590
|
+
pass
|
|
591
|
+
|
|
394
592
|
def _finish(self, task, result):
|
|
395
593
|
task._done = True
|
|
396
594
|
task._result = result
|
|
397
595
|
self._wake_waiters(task)
|
|
596
|
+
self._run_done_callbacks(task)
|
|
398
597
|
|
|
399
598
|
def _fail(self, task, error):
|
|
400
599
|
task._done = True
|
|
401
600
|
task._exception = error
|
|
402
601
|
task._cancelled = isinstance(error, CancelledError)
|
|
403
602
|
self._wake_waiters(task)
|
|
603
|
+
self._run_done_callbacks(task)
|
|
404
604
|
|
|
405
605
|
def _block_on_task(self, task, awaited):
|
|
406
606
|
if awaited._done:
|
|
@@ -428,6 +628,10 @@ class _Loop:
|
|
|
428
628
|
awaited.queue._getters.append(task)
|
|
429
629
|
elif isinstance(awaited, _LockWait):
|
|
430
630
|
awaited.lock._waiters.append(task)
|
|
631
|
+
elif isinstance(awaited, _SemaphoreWait):
|
|
632
|
+
awaited.semaphore._waiters.append(task)
|
|
633
|
+
elif isinstance(awaited, _ConditionWait):
|
|
634
|
+
awaited.condition._waiters.append(task)
|
|
431
635
|
elif isinstance(awaited, _WaitFor):
|
|
432
636
|
if awaited.task._done:
|
|
433
637
|
self._block_on_task(task, awaited.task)
|
|
@@ -456,7 +660,9 @@ class _Loop:
|
|
|
456
660
|
if task._done:
|
|
457
661
|
continue
|
|
458
662
|
task._started = True
|
|
663
|
+
self._current = task
|
|
459
664
|
status, value = _step(task._coroutine, task._send_value)
|
|
665
|
+
self._current = None
|
|
460
666
|
task._send_value = None
|
|
461
667
|
if status == 1:
|
|
462
668
|
self._finish(task, value)
|
|
@@ -473,8 +679,30 @@ def get_running_loop():
|
|
|
473
679
|
return _running_loop
|
|
474
680
|
|
|
475
681
|
|
|
476
|
-
def create_task(coroutine):
|
|
477
|
-
return get_running_loop().create_task(coroutine)
|
|
682
|
+
def create_task(coroutine, name=None, context=None):
|
|
683
|
+
return get_running_loop().create_task(coroutine, name)
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
def ensure_future(awaitable):
|
|
687
|
+
return _ensure_task(awaitable)
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
def current_task(loop=None):
|
|
691
|
+
if loop is None:
|
|
692
|
+
loop = get_running_loop()
|
|
693
|
+
return loop._current
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
def all_tasks(loop=None):
|
|
697
|
+
if loop is None:
|
|
698
|
+
loop = get_running_loop()
|
|
699
|
+
return {task for task in loop._tasks if not task.done()}
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
def _ensure_task(awaitable):
|
|
703
|
+
if isinstance(awaitable, Task):
|
|
704
|
+
return awaitable
|
|
705
|
+
return create_task(awaitable)
|
|
478
706
|
|
|
479
707
|
|
|
480
708
|
async def sleep(delay, result=None):
|
|
@@ -483,12 +711,7 @@ async def sleep(delay, result=None):
|
|
|
483
711
|
|
|
484
712
|
|
|
485
713
|
async def gather(*coroutines, return_exceptions=False):
|
|
486
|
-
tasks = []
|
|
487
|
-
for coroutine in coroutines:
|
|
488
|
-
if isinstance(coroutine, Task):
|
|
489
|
-
tasks.append(coroutine)
|
|
490
|
-
else:
|
|
491
|
-
tasks.append(create_task(coroutine))
|
|
714
|
+
tasks = [_ensure_task(coroutine) for coroutine in coroutines]
|
|
492
715
|
results = []
|
|
493
716
|
for task in tasks:
|
|
494
717
|
try:
|
|
@@ -501,15 +724,100 @@ async def gather(*coroutines, return_exceptions=False):
|
|
|
501
724
|
|
|
502
725
|
|
|
503
726
|
async def wait_for(awaitable, timeout):
|
|
504
|
-
|
|
505
|
-
task = awaitable
|
|
506
|
-
else:
|
|
507
|
-
task = create_task(awaitable)
|
|
727
|
+
task = _ensure_task(awaitable)
|
|
508
728
|
if timeout is None:
|
|
509
729
|
return await task
|
|
510
730
|
return await _WaitFor(task, timeout)
|
|
511
731
|
|
|
512
732
|
|
|
733
|
+
def _wait_condition(tasks, return_when):
|
|
734
|
+
done = {task for task in tasks if task.done()}
|
|
735
|
+
if return_when == FIRST_COMPLETED:
|
|
736
|
+
return bool(done)
|
|
737
|
+
if return_when == FIRST_EXCEPTION:
|
|
738
|
+
for task in done:
|
|
739
|
+
if not task.cancelled() and task.exception() is not None:
|
|
740
|
+
return True
|
|
741
|
+
return len(done) == len(tasks)
|
|
742
|
+
return len(done) == len(tasks)
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
async def wait(awaitables, timeout=None, return_when=ALL_COMPLETED):
|
|
746
|
+
if return_when not in (ALL_COMPLETED, FIRST_COMPLETED, FIRST_EXCEPTION):
|
|
747
|
+
raise ValueError("invalid return_when value")
|
|
748
|
+
tasks = []
|
|
749
|
+
for awaitable in awaitables:
|
|
750
|
+
if awaitable not in tasks:
|
|
751
|
+
tasks.append(awaitable)
|
|
752
|
+
if not tasks:
|
|
753
|
+
raise ValueError("Set of Tasks/Futures is empty")
|
|
754
|
+
for task in tasks:
|
|
755
|
+
if not isinstance(task, Task):
|
|
756
|
+
raise TypeError("Passing coroutines is forbidden, use tasks explicitly")
|
|
757
|
+
changed = Event()
|
|
758
|
+
|
|
759
|
+
def task_completed(task):
|
|
760
|
+
changed.set()
|
|
761
|
+
|
|
762
|
+
observed = [task for task in tasks if not task.done()]
|
|
763
|
+
for task in observed:
|
|
764
|
+
task.add_done_callback(task_completed)
|
|
765
|
+
|
|
766
|
+
async def wait_until_ready():
|
|
767
|
+
while not _wait_condition(tasks, return_when):
|
|
768
|
+
changed.clear()
|
|
769
|
+
if _wait_condition(tasks, return_when):
|
|
770
|
+
break
|
|
771
|
+
await changed.wait()
|
|
772
|
+
|
|
773
|
+
try:
|
|
774
|
+
if timeout is None:
|
|
775
|
+
await wait_until_ready()
|
|
776
|
+
else:
|
|
777
|
+
try:
|
|
778
|
+
await wait_for(wait_until_ready(), timeout)
|
|
779
|
+
except TimeoutError:
|
|
780
|
+
pass
|
|
781
|
+
finally:
|
|
782
|
+
for task in observed:
|
|
783
|
+
task.remove_done_callback(task_completed)
|
|
784
|
+
done = {task for task in tasks if task.done()}
|
|
785
|
+
return done, set(tasks) - done
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
def as_completed(awaitables, timeout=None):
|
|
789
|
+
tasks = []
|
|
790
|
+
for awaitable in awaitables:
|
|
791
|
+
task = _ensure_task(awaitable)
|
|
792
|
+
if task not in tasks:
|
|
793
|
+
tasks.append(task)
|
|
794
|
+
completed = Queue()
|
|
795
|
+
deadline = None
|
|
796
|
+
if timeout is not None:
|
|
797
|
+
deadline = _monotonic_ns() + int(timeout * 1000000000)
|
|
798
|
+
|
|
799
|
+
def task_completed(task):
|
|
800
|
+
completed.put_nowait(task)
|
|
801
|
+
|
|
802
|
+
for task in tasks:
|
|
803
|
+
task.add_done_callback(task_completed)
|
|
804
|
+
|
|
805
|
+
async def next_result():
|
|
806
|
+
if deadline is None:
|
|
807
|
+
task = await completed.get()
|
|
808
|
+
else:
|
|
809
|
+
remaining = (deadline - _monotonic_ns()) / 1000000000
|
|
810
|
+
task = await wait_for(completed.get(), max(0, remaining))
|
|
811
|
+
return task.result()
|
|
812
|
+
|
|
813
|
+
for task in tasks:
|
|
814
|
+
yield next_result()
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
async def shield(awaitable):
|
|
818
|
+
return await _ensure_task(awaitable)
|
|
819
|
+
|
|
820
|
+
|
|
513
821
|
async def create_subprocess_exec(program, *args, stdin=None, stdout=None, stderr=None,
|
|
514
822
|
cwd=None, env=None, start_new_session=False, limit=65536):
|
|
515
823
|
if limit <= 0:
|
|
@@ -62,6 +62,36 @@ fn validate_array_layout(layout: &PyArrayLayout, storage_len: usize) -> PyResult
|
|
|
62
62
|
Ok(())
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
fn wait_reason_ready(
|
|
66
|
+
interp: &crate::interp::Interp,
|
|
67
|
+
reason: &crate::scheduler::WaitReason,
|
|
68
|
+
now: u64,
|
|
69
|
+
) -> bool {
|
|
70
|
+
use crate::scheduler::WaitReason;
|
|
71
|
+
|
|
72
|
+
match reason {
|
|
73
|
+
WaitReason::Timer(deadline) => *deadline <= now,
|
|
74
|
+
WaitReason::InputReadable(description) => interp.descriptors.input_readable(*description),
|
|
75
|
+
WaitReason::PipeReadable(pipe) => interp.descriptors.pipe_readable(*pipe),
|
|
76
|
+
WaitReason::PipeWritable(pipe) => interp.descriptors.pipe_writable(*pipe),
|
|
77
|
+
WaitReason::Child(pid) | WaitReason::ChildActivity(pid) => matches!(
|
|
78
|
+
interp.processes.get(*pid).map(|record| record.status),
|
|
79
|
+
Some(crate::process::ProcessStatus::Exited(_))
|
|
80
|
+
),
|
|
81
|
+
WaitReason::ChildDeadline(pid, deadline)
|
|
82
|
+
| WaitReason::ChildActivityDeadline(pid, deadline) => {
|
|
83
|
+
*deadline <= now
|
|
84
|
+
|| matches!(
|
|
85
|
+
interp.processes.get(*pid).map(|record| record.status),
|
|
86
|
+
Some(crate::process::ProcessStatus::Exited(_))
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
WaitReason::Any(reasons) => reasons
|
|
90
|
+
.iter()
|
|
91
|
+
.any(|reason| wait_reason_ready(interp, reason, now)),
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
65
95
|
impl PyRuntime for Vm<'_> {
|
|
66
96
|
fn reserve_memory(&mut self, bytes: usize) -> PyResult<()> {
|
|
67
97
|
self.reserve_result(bytes).map_err(PyError::resource_error)
|
|
@@ -1766,6 +1796,12 @@ impl PyRuntime for Vm<'_> {
|
|
|
1766
1796
|
));
|
|
1767
1797
|
}
|
|
1768
1798
|
let now = self.interp.clock.monotonic_ns();
|
|
1799
|
+
if reasons
|
|
1800
|
+
.iter()
|
|
1801
|
+
.any(|reason| wait_reason_ready(self.interp, reason, now))
|
|
1802
|
+
{
|
|
1803
|
+
return Ok(());
|
|
1804
|
+
}
|
|
1769
1805
|
self.execution
|
|
1770
1806
|
.async_timer_deadlines
|
|
1771
1807
|
.retain(|deadline| *deadline > now);
|
|
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
|
|
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
|
|
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
|
|
File without changes
|