virtualshell 1.0.2__tar.gz → 1.1.0__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 (54) hide show
  1. {virtualshell-1.0.2 → virtualshell-1.1.0}/.gitignore +2 -2
  2. {virtualshell-1.0.2 → virtualshell-1.1.0}/CMakeLists.txt +3 -7
  3. {virtualshell-1.0.2 → virtualshell-1.1.0}/PKG-INFO +108 -172
  4. virtualshell-1.1.0/README.md +186 -0
  5. virtualshell-1.1.0/bench/bench.csv +4 -0
  6. virtualshell-1.1.0/bench/bench.json +435 -0
  7. virtualshell-1.1.0/bench/vs_bench.py +558 -0
  8. virtualshell-1.1.0/cpp/include/cmd_state.hpp +39 -0
  9. virtualshell-1.1.0/cpp/include/config.hpp +25 -0
  10. virtualshell-1.1.0/cpp/include/dev_debug.hpp +228 -0
  11. virtualshell-1.1.0/cpp/include/execution_result.hpp +38 -0
  12. virtualshell-1.1.0/cpp/include/helpers.hpp +65 -0
  13. virtualshell-1.1.0/cpp/include/io_pump.hpp +101 -0
  14. virtualshell-1.1.0/cpp/include/powershell_process.hpp +174 -0
  15. virtualshell-1.1.0/cpp/include/process.hpp +32 -0
  16. virtualshell-1.1.0/cpp/include/py_bridge.hpp +327 -0
  17. virtualshell-1.1.0/cpp/include/timeout_watcher.hpp +112 -0
  18. {virtualshell-1.0.2 → virtualshell-1.1.0}/cpp/include/virtual_shell.hpp +130 -266
  19. virtualshell-1.1.0/cpp/src/binder.cpp +253 -0
  20. virtualshell-1.1.0/cpp/src/io_pump.cpp +233 -0
  21. virtualshell-1.1.0/cpp/src/powershell_process.cpp +645 -0
  22. virtualshell-1.1.0/cpp/src/virtual_shell.cpp +1339 -0
  23. {virtualshell-1.0.2 → virtualshell-1.1.0}/pyproject.toml +1 -1
  24. {virtualshell-1.0.2 → virtualshell-1.1.0}/src/virtualshell/__init__.py +4 -4
  25. virtualshell-1.1.0/src/virtualshell/_version.py +1 -0
  26. virtualshell-1.1.0/src/virtualshell/get-session.ps1 +153 -0
  27. virtualshell-1.1.0/src/virtualshell/save-session.ps1 +107 -0
  28. virtualshell-1.1.0/src/virtualshell/shell.py +522 -0
  29. virtualshell-1.1.0/wiki/Getting started/Getting started.md +27 -0
  30. virtualshell-1.1.0/wiki/Getting started/Installation.md +11 -0
  31. virtualshell-1.1.0/wiki/Getting started/Quickstart.md +30 -0
  32. virtualshell-1.1.0/wiki/Help/FAQ.md +11 -0
  33. virtualshell-1.1.0/wiki/Help/Troubleshooting.md +20 -0
  34. virtualshell-1.1.0/wiki/Home.md +10 -0
  35. virtualshell-1.1.0/wiki/Project/Benchmarks.md +65 -0
  36. virtualshell-1.1.0/wiki/Project/Changelog.md +19 -0
  37. virtualshell-1.1.0/wiki/Project/Design & Architecture.md +12 -0
  38. virtualshell-1.1.0/wiki/Usage/API Overview.md +23 -0
  39. virtualshell-1.1.0/wiki/Usage/Asynchronous Execution.md +25 -0
  40. virtualshell-1.1.0/wiki/Usage/Configuration.md +14 -0
  41. virtualshell-1.1.0/wiki/Usage/Error Handling.md +23 -0
  42. virtualshell-1.1.0/wiki/Usage/Performance Tips.md +7 -0
  43. virtualshell-1.1.0/wiki/Usage/Running Scripts.md +26 -0
  44. virtualshell-1.1.0/wiki/Usage/Security Notes.md +5 -0
  45. virtualshell-1.1.0/wiki/Usage/Synchronous Execution.md +27 -0
  46. virtualshell-1.0.2/README.md +0 -250
  47. virtualshell-1.0.2/cpp/src/binder.cpp +0 -529
  48. virtualshell-1.0.2/cpp/src/virtual_shell.cpp +0 -1806
  49. virtualshell-1.0.2/demo.py +0 -328
  50. virtualshell-1.0.2/src/virtualshell/_version.py +0 -1
  51. virtualshell-1.0.2/src/virtualshell/shell.py +0 -515
  52. {virtualshell-1.0.2 → virtualshell-1.1.0}/.github/workflows/workflow.yml +0 -0
  53. {virtualshell-1.0.2 → virtualshell-1.1.0}/LICENSE +0 -0
  54. {virtualshell-1.0.2 → virtualshell-1.1.0}/src/virtualshell/errors.py +0 -0
@@ -3,9 +3,9 @@ __pycache__/
3
3
  *.pyc
4
4
  build/
5
5
  *.egg-info/
6
- dev_*
7
6
  test/
8
7
  .pypirc
9
8
  dist/
10
9
  .venv*
11
- test_run*
10
+ test_run*
11
+ test_sync_timeout.py
@@ -21,14 +21,10 @@ set(CMAKE_CXX_STANDARD 17)
21
21
  set(CMAKE_CXX_STANDARD_REQUIRED ON)
22
22
  set(CMAKE_POSITION_INDEPENDENT_CODE ON)
23
23
 
24
-
25
- if (WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
26
- message(FATAL_ERROR "MinGW is not supported on Windows for this project. Use MSVC.")
27
- endif()
28
-
29
-
30
24
  set(SRC
31
25
  cpp/src/binder.cpp
26
+ cpp/src/io_pump.cpp
27
+ cpp/src/powershell_process.cpp
32
28
  cpp/src/virtual_shell.cpp
33
29
  )
34
30
 
@@ -39,7 +35,7 @@ else()
39
35
  set(_VS_OUTDIR "${SKBUILD_PLATLIB_DIR}/virtualshell")
40
36
  endif()
41
37
 
42
- pybind11_add_module(_core MODULE ${SRC})
38
+ pybind11_add_module(_core MODULE ${SRC} cpp/include/py_bridge.hpp)
43
39
  target_include_directories(_core PRIVATE cpp/include)
44
40
 
45
41
  if(UNIX)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: virtualshell
3
- Version: 1.0.2
3
+ Version: 1.1.0
4
4
  Summary: High-performance PowerShell bridge (C++ pybind11 backend)
5
5
  Keywords: powershell,automation,shell,cpp,pybind11
6
6
  Author: Kim-Andre Myrvold
@@ -219,251 +219,187 @@ Description-Content-Type: text/markdown
219
219
 
220
220
  # virtualshell
221
221
 
222
- High-performance Python façade over a **C++ PowerShell runner**.
223
- A single long-lived PowerShell process is managed in C++, handling pipes, threads, timeouts and output demux; Python exposes a small, predictable API.
222
+ High-performance PowerShell automation for Python. `virtualshell` keeps a single PowerShell host warm and exposes it through a thin Python wrapper backed by a C++ engine. The result: millisecond-scale latency, async execution, and session persistence without juggling subprocesses.
223
+
224
+ > Full documentation now lives in the [project wiki](https://github.com/Chamoswor/virtualshell/wiki). This README gives you the essentials and quick links.
224
225
 
225
226
  ---
226
227
 
227
- ## Features
228
+ ## Why virtualshell?
229
+
230
+ - **Persistent session** – reuse modules, `$env:*`, and functions between calls.
231
+ - **Low latency** – avoid the 200+ ms penalty of `subprocess.run("pwsh")`; most commands settle in ~2-4 ms.
232
+ - **Async + batching** – schedule commands concurrently or in batches with strong timeout control.
233
+ - **Structured results** – every invocation returns stdout/stderr, exit code, success flag, and timing.
234
+ - **Predictable failures** – typed Python exceptions for “pwsh missing”, timeouts, and execution errors.
228
235
 
229
- - **Persistent session** to `pwsh`/`powershell` (reuse modules, `$env:*`, functions, cwd)
230
- - **Sync & async** execution (Futures + optional callbacks)
231
- - **Script execution** (positional / named args, optional dot-sourcing)
232
- - **Batch** with per-command timeout & early-stop
233
- - **Clear failures** (typed exceptions), **context manager** lifecycle
236
+ Typical users embed PowerShell inside Python orchestration, long-running agents, or test suites that need reliability and speed.
234
237
 
235
238
  ---
236
239
 
237
- ## Install
240
+ ## Installation
238
241
 
239
242
  ```bash
240
243
  pip install virtualshell
241
- ````
242
-
243
- ## Platform & Python Support
244
-
245
- Prebuilt wheels are provided via PyPI for common platforms and Python versions.
246
- This means you can usually `pip install virtualshell` without needing a compiler.
247
-
248
- **Supported Python versions:**
249
- - 3.8, 3.9, 3.10, 3.11, 3.12, 3.13
250
-
251
- **Supported platforms:**
252
- - **Windows** (x86_64, MSVC build)
253
- - **Linux** (x86_64, aarch64, manylinux2014/2.28)
254
- - **macOS** (universal2: x86_64 + arm64)
255
-
256
- If your platform is not listed above, pip will fall back to building from source.
257
- See [Building from source](#building-from-source-advanced) for details.
258
-
244
+ ```
259
245
 
260
- > Requires PowerShell on `PATH` (`pwsh` preferred, `powershell` also supported).
246
+ Pre-built wheels are published for Windows, Linux (x86_64/aarch64), and macOS universal2. PowerShell (`pwsh` or `powershell.exe`) must be discoverable on `PATH` unless you pass an explicit path.
261
247
 
262
248
  ---
263
249
 
264
250
  ## Quick start
265
251
 
266
252
  ```python
267
- import virtualshell
268
-
269
- # Create a shell with a 5s default timeout
270
- sh = virtualshell.Shell(timeout_seconds=5).start()
271
-
272
- # 1) One-liners (sync)
273
- res = sh.run("Write-Output 'hello'")
274
- print(res.out.strip()) # -> hello
275
-
276
- # 2) Async single command
277
- fut = sh.run_async("Write-Output 'async!'")
278
- print(fut.result().out.strip())
279
-
280
- # 3) Scripts with positional args
281
- r = sh.run_script(r"C:\temp\demo.ps1", args=["alpha", "42"])
282
- print(r.out)
283
-
284
- # 4) Scripts with named args
285
- r = sh.run_script_kv(r"C:\temp\demo.ps1", named_args={"Name":"Alice","Count":"3"})
286
- print(r.out)
253
+ from virtualshell import Shell
287
254
 
288
- # 5) Context manager (auto-stop on exit)
289
- with virtualshell.Shell(timeout_seconds=3) as s:
290
- print(s.run("Write-Output 'inside with'").out.strip())
255
+ with Shell(timeout_seconds=5) as sh:
256
+ result = sh.run("Write-Output 'Hello from pwsh'")
257
+ print(result.out.strip())
291
258
 
292
- sh.stop()
259
+ sh.run("function Inc { $global:i++; $global:i }")
260
+ print(sh.run("Inc").out.strip()) # 1
261
+ print(sh.run("Inc").out.strip()) # 2
293
262
  ```
294
263
 
295
- Another example (stateful session):
264
+ ### Async execution
296
265
 
297
266
  ```python
298
267
  from virtualshell import Shell
299
- with Shell(timeout_seconds=3) as sh:
300
- sh.run("function Inc { $global:i++; $global:i }")
301
- nums = [sh.run("Inc").out.strip() for _ in range(5)]
302
- print(nums) # ['1','2','3','4','5']
303
- ```
268
+ import asyncio
304
269
 
305
- ---
306
-
307
- ## API (overview)
270
+ async def main():
271
+ shell = Shell().start()
272
+ fut = shell.run_async("Get-Date")
273
+ res = await asyncio.wrap_future(fut)
274
+ print(res.out.strip())
275
+ shell.stop()
308
276
 
309
- ```python
310
- import virtualshell
311
- from virtualshell import ExecutionResult # dataclass view
277
+ asyncio.run(main())
278
+ ```
312
279
 
313
- sh = virtualshell.Shell(
314
- powershell_path=None, # optional explicit path
315
- working_directory=None, # resolved to absolute path
316
- timeout_seconds=5.0, # default per-command timeout
317
- environment={"FOO": "BAR"}, # extra child env vars
318
- initial_commands=["$ErrorActionPreference='Stop'"], # post-start setup
319
- ).start()
280
+ ### Scripts and arguments
320
281
 
321
- # Sync
322
- res: ExecutionResult = sh.run("Get-Location | Select-Object -Expand Path")
282
+ ```python
283
+ from pathlib import Path
284
+ from virtualshell import Shell
323
285
 
324
- # Scripts
325
- res = sh.run_script(r"/path/to/job.ps1", args=["--fast","1"])
326
- res = sh.run_script_kv(r"/path/to/job.ps1", named_args={"Mode":"Fast","Count":"1"})
327
- res = sh.run_script(r"/path/init.ps1", dot_source=True)
286
+ shell = Shell().start()
328
287
 
329
- # Async
330
- f = sh.run_async("Write-Output 'ping'")
331
- f2 = sh.run_async_batch(["$PSVersionTable", "Get-Random"])
288
+ # Positional arguments
289
+ shell.script(Path("./scripts/test.ps1"), args=["alpha", "42"])
332
290
 
333
- # Convenience
334
- res = sh.pwsh("literal 'quoted' string") # safe single-quoted literal
291
+ # Named arguments (hashtable splatting)
292
+ shell.script(
293
+ Path("./scripts/test.ps1"),
294
+ args={"Name": "Alice", "Count": "3"},
295
+ )
335
296
 
336
- sh.stop()
297
+ shell.stop()
337
298
  ```
338
299
 
339
- ### Return type
340
-
341
- By default you get a Python dataclass:
300
+ Every API surface (sync/async/script) accepts `timeout` overrides and optional error raising via `raise_on_error` or callbacks.
342
301
 
343
- ```python
344
- @dataclass(frozen=True)
345
- class ExecutionResult:
346
- out: str
347
- err: str
348
- exit_code: int
349
- success: bool
350
- execution_time: float
351
- ```
302
+ ---
352
303
 
353
- Pass `as_dataclass=False` to receive the raw C++ result object.
304
+ ## Core API overview
354
305
 
355
- ### Timeouts
306
+ | Method | Purpose |
307
+ | --- | --- |
308
+ | `Shell.run(cmd, *, timeout=None, raise_on_error=False)` | Execute a single command synchronously. |
309
+ | `Shell.run_async(cmd, *, callback=None, timeout=None)` | Schedule a command; returns a `concurrent.futures.Future`. |
310
+ | `Shell.script(path, args=None, *, timeout=None, dot_source=False, raise_on_error=False)` | Execute `.ps1` files with positional or named arguments. |
311
+ | `Shell.script_async(...)` | Async counterpart of `script`. |
312
+ | `Shell.save_session()` | Persist the current session to an XML snapshot. |
313
+ | `Shell.pwsh(text)` | Safely echo a literal PowerShell string (auto quoting). |
356
314
 
357
- * Every method accepts a `timeout` (or `per_command_timeout`) in seconds.
358
- * On timeout: `success=False`, `exit_code=-1`, `err` contains `"timeout"`.
359
- * Async futures resolve with the timeout result; late output is dropped in C++.
315
+ More helpers live in the wiki, including session restore, batching, and diagnostic tips.
360
316
 
361
317
  ---
362
318
 
363
- ## Design notes
364
-
365
- * **Thin wrapper:** heavy I/O in C++; Python does orchestration only.
366
- * **No surprises:** stable API, documented side-effects.
367
- * **Clear failure modes:** `raise_on_error` and typed exceptions.
368
- * **Thread-friendly:** async returns Futures/callbacks; no Python GIL-level locking.
369
- * **Boundary hygiene:** explicit path/arg conversions, minimal marshalling.
370
-
371
- ### Security
319
+ ## Configuration
372
320
 
373
- * The wrapper **does not sanitize** raw commands. Only `pwsh()` applies literal single-quoting for data.
374
- * Don’t pass untrusted strings to `run*` without proper quoting/sanitization.
375
- * Avoid logging secrets; env injection happens via `Shell(..., environment=...)`.
376
-
377
- ### Performance
321
+ ```python
322
+ from virtualshell import Shell
378
323
 
379
- * Sync/async routes call into C++ directly; Python overhead is object creation + callback dispatch.
380
- * Prefer **batch/async** for many small commands to amortize round-trips.
324
+ shell = Shell(
325
+ powershell_path=r"C:\Program Files\PowerShell\7\pwsh.exe",
326
+ working_directory=r"C:\automation",
327
+ environment={"MY_FLAG": "1"},
328
+ initial_commands=[
329
+ "$ErrorActionPreference = 'Stop'",
330
+ "$ProgressPreference = 'SilentlyContinue'",
331
+ ],
332
+ timeout_seconds=10,
333
+ auto_restart_on_timeout=True,
334
+ )
381
335
 
382
- ### Lifetime
336
+ shell.start()
337
+ ```
383
338
 
384
- * `Shell.start()` ensures a running backend; `Shell.stop()` tears it down.
385
- * `with Shell(...)` guarantees stop-on-exit, even on exceptions.
339
+ Configuration is applied before the process starts. You can inspect or replace it later with `shell._core.get_config()` or rebuild the shell.
386
340
 
387
341
  ---
388
342
 
389
- ## Exceptions
343
+ ## Error handling
390
344
 
391
345
  ```python
392
- from virtualshell.errors import (
393
- VirtualShellError,
394
- PowerShellNotFoundError,
395
- ExecutionTimeoutError,
396
- ExecutionError,
397
- )
346
+ from virtualshell import Shell
347
+ from virtualshell.errors import ExecutionError, ExecutionTimeoutError
348
+
349
+ shell = Shell().start()
398
350
 
399
351
  try:
400
- res = sh.run("throw 'boom'", raise_on_error=True)
352
+ shell.run("throw 'boom'", raise_on_error=True)
401
353
  except ExecutionTimeoutError:
402
- ...
403
- except ExecutionError as e:
404
- print("PowerShell failed:", e)
354
+ print("Timed out")
355
+ except ExecutionError as exc:
356
+ print("PowerShell failure:", exc)
357
+ finally:
358
+ shell.stop()
405
359
  ```
406
360
 
407
- * `ExecutionTimeoutError` is raised on timeouts **if** `raise_on_error=True`.
408
- * Otherwise, APIs return `ExecutionResult(success=False)`.
361
+ Refer to `virtualshell.errors` for all exception types.
409
362
 
410
363
  ---
411
364
 
412
- ## Configuration tips
413
-
414
- If PowerShell isn’t on `PATH`, pass `powershell_path`:
365
+ ## Performance
415
366
 
416
- ```python
417
- Shell(powershell_path=r"C:\Program Files\PowerShell\7\pwsh.exe")
418
- ```
367
+ Up-to-date benchmark artefacts (`bench.json`, `bench.csv`) and analysis live in [docs/wiki/Project/Benchmarks.md](docs/wiki/Project/Benchmarks.md). Headline numbers from the latest run (Windows 11, Python 3.13):
419
368
 
420
- Session setup example:
369
+ - Sequential commands: ~3.5 ms average
370
+ - Batch commands: ~3.2 ms per command
371
+ - Async latency: ~1.9–2.4 ms with 50 outstanding tasks
372
+ - Session save: ~0.30 s median
421
373
 
422
- ```python
423
- Shell(initial_commands=[
424
- "$OutputEncoding = [Console]::OutputEncoding = [Text.UTF8Encoding]::new()",
425
- "$ErrorActionPreference = 'Stop'"
426
- ])
427
- ```
374
+ See the wiki for charts and methodology.
428
375
 
429
376
  ---
430
377
 
431
- # Building from source (advanced)
432
- Source builds require a C++ toolchain and CMake.
378
+ ## Building from source
433
379
 
434
- **Prereqs:** Python ≥3.8, C++17, CMake ≥3.20, `scikit-build-core`, `pybind11`.
380
+ Dependencies:
435
381
 
436
- ```bash
437
- # in repo root
438
- python -m pip install -U pip build
439
- python -m build # -> dist/*.whl, dist/*.tar.gz
440
- python -m pip install dist/virtualshell-*.whl
441
- ```
442
-
443
- Editable install:
382
+ - Python 3.8+
383
+ - CMake 3.20+
384
+ - A C++17 compiler (MSVC, Clang, or GCC)
385
+ - `scikit-build-core`, `pybind11`
444
386
 
445
387
  ```bash
446
- python -m pip install -e .
388
+ python -m pip install -U build
389
+ python -m build
390
+ python -m pip install dist/virtualshell-*.whl
447
391
  ```
448
392
 
449
- * Linux wheels target **manylinux_2_28** (x86_64/aarch64).
450
- * macOS builds target **universal2** (x86_64 + arm64).
451
-
452
393
  ---
453
394
 
454
- ## Roadmap
455
-
456
- * ✅ Windows x64 wheels (3.8–3.13)
457
- * ✅ Linux x64/aarch64 wheels (manylinux_2_28)
458
- * ✅ macOS x86_64/arm64 wheels
459
- * ⏳ Streaming APIs and richer progress events
460
-
461
- ---
395
+ ## Learn more
462
396
 
463
- ## License
397
+ - [Usage guides](docs/wiki/Usage)
398
+ - [Performance tips](docs/wiki/Usage/Performance%20Tips.md)
399
+ - [Benchmarks](docs/wiki/Project/Benchmarks.md)
464
400
 
465
- Apache 2.0 — see [LICENSE](LICENSE).
401
+ Bug reports and feature requests are welcome via issues or discussions.
466
402
 
467
403
  ---
468
404
 
469
- *Issues & feedback are welcome. Please include Python version, OS, your PowerShell path (`pwsh`/`powershell`), and a minimal repro.*
405
+ Licensed under the Apache 2.0 license. See [LICENSE](LICENSE).
@@ -0,0 +1,186 @@
1
+ # virtualshell
2
+
3
+ High-performance PowerShell automation for Python. `virtualshell` keeps a single PowerShell host warm and exposes it through a thin Python wrapper backed by a C++ engine. The result: millisecond-scale latency, async execution, and session persistence without juggling subprocesses.
4
+
5
+ > Full documentation now lives in the [project wiki](https://github.com/Chamoswor/virtualshell/wiki). This README gives you the essentials and quick links.
6
+
7
+ ---
8
+
9
+ ## Why virtualshell?
10
+
11
+ - **Persistent session** – reuse modules, `$env:*`, and functions between calls.
12
+ - **Low latency** – avoid the 200+ ms penalty of `subprocess.run("pwsh")`; most commands settle in ~2-4 ms.
13
+ - **Async + batching** – schedule commands concurrently or in batches with strong timeout control.
14
+ - **Structured results** – every invocation returns stdout/stderr, exit code, success flag, and timing.
15
+ - **Predictable failures** – typed Python exceptions for “pwsh missing”, timeouts, and execution errors.
16
+
17
+ Typical users embed PowerShell inside Python orchestration, long-running agents, or test suites that need reliability and speed.
18
+
19
+ ---
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ pip install virtualshell
25
+ ```
26
+
27
+ Pre-built wheels are published for Windows, Linux (x86_64/aarch64), and macOS universal2. PowerShell (`pwsh` or `powershell.exe`) must be discoverable on `PATH` unless you pass an explicit path.
28
+
29
+ ---
30
+
31
+ ## Quick start
32
+
33
+ ```python
34
+ from virtualshell import Shell
35
+
36
+ with Shell(timeout_seconds=5) as sh:
37
+ result = sh.run("Write-Output 'Hello from pwsh'")
38
+ print(result.out.strip())
39
+
40
+ sh.run("function Inc { $global:i++; $global:i }")
41
+ print(sh.run("Inc").out.strip()) # 1
42
+ print(sh.run("Inc").out.strip()) # 2
43
+ ```
44
+
45
+ ### Async execution
46
+
47
+ ```python
48
+ from virtualshell import Shell
49
+ import asyncio
50
+
51
+ async def main():
52
+ shell = Shell().start()
53
+ fut = shell.run_async("Get-Date")
54
+ res = await asyncio.wrap_future(fut)
55
+ print(res.out.strip())
56
+ shell.stop()
57
+
58
+ asyncio.run(main())
59
+ ```
60
+
61
+ ### Scripts and arguments
62
+
63
+ ```python
64
+ from pathlib import Path
65
+ from virtualshell import Shell
66
+
67
+ shell = Shell().start()
68
+
69
+ # Positional arguments
70
+ shell.script(Path("./scripts/test.ps1"), args=["alpha", "42"])
71
+
72
+ # Named arguments (hashtable splatting)
73
+ shell.script(
74
+ Path("./scripts/test.ps1"),
75
+ args={"Name": "Alice", "Count": "3"},
76
+ )
77
+
78
+ shell.stop()
79
+ ```
80
+
81
+ Every API surface (sync/async/script) accepts `timeout` overrides and optional error raising via `raise_on_error` or callbacks.
82
+
83
+ ---
84
+
85
+ ## Core API overview
86
+
87
+ | Method | Purpose |
88
+ | --- | --- |
89
+ | `Shell.run(cmd, *, timeout=None, raise_on_error=False)` | Execute a single command synchronously. |
90
+ | `Shell.run_async(cmd, *, callback=None, timeout=None)` | Schedule a command; returns a `concurrent.futures.Future`. |
91
+ | `Shell.script(path, args=None, *, timeout=None, dot_source=False, raise_on_error=False)` | Execute `.ps1` files with positional or named arguments. |
92
+ | `Shell.script_async(...)` | Async counterpart of `script`. |
93
+ | `Shell.save_session()` | Persist the current session to an XML snapshot. |
94
+ | `Shell.pwsh(text)` | Safely echo a literal PowerShell string (auto quoting). |
95
+
96
+ More helpers live in the wiki, including session restore, batching, and diagnostic tips.
97
+
98
+ ---
99
+
100
+ ## Configuration
101
+
102
+ ```python
103
+ from virtualshell import Shell
104
+
105
+ shell = Shell(
106
+ powershell_path=r"C:\Program Files\PowerShell\7\pwsh.exe",
107
+ working_directory=r"C:\automation",
108
+ environment={"MY_FLAG": "1"},
109
+ initial_commands=[
110
+ "$ErrorActionPreference = 'Stop'",
111
+ "$ProgressPreference = 'SilentlyContinue'",
112
+ ],
113
+ timeout_seconds=10,
114
+ auto_restart_on_timeout=True,
115
+ )
116
+
117
+ shell.start()
118
+ ```
119
+
120
+ Configuration is applied before the process starts. You can inspect or replace it later with `shell._core.get_config()` or rebuild the shell.
121
+
122
+ ---
123
+
124
+ ## Error handling
125
+
126
+ ```python
127
+ from virtualshell import Shell
128
+ from virtualshell.errors import ExecutionError, ExecutionTimeoutError
129
+
130
+ shell = Shell().start()
131
+
132
+ try:
133
+ shell.run("throw 'boom'", raise_on_error=True)
134
+ except ExecutionTimeoutError:
135
+ print("Timed out")
136
+ except ExecutionError as exc:
137
+ print("PowerShell failure:", exc)
138
+ finally:
139
+ shell.stop()
140
+ ```
141
+
142
+ Refer to `virtualshell.errors` for all exception types.
143
+
144
+ ---
145
+
146
+ ## Performance
147
+
148
+ Up-to-date benchmark artefacts (`bench.json`, `bench.csv`) and analysis live in [docs/wiki/Project/Benchmarks.md](docs/wiki/Project/Benchmarks.md). Headline numbers from the latest run (Windows 11, Python 3.13):
149
+
150
+ - Sequential commands: ~3.5 ms average
151
+ - Batch commands: ~3.2 ms per command
152
+ - Async latency: ~1.9–2.4 ms with 50 outstanding tasks
153
+ - Session save: ~0.30 s median
154
+
155
+ See the wiki for charts and methodology.
156
+
157
+ ---
158
+
159
+ ## Building from source
160
+
161
+ Dependencies:
162
+
163
+ - Python 3.8+
164
+ - CMake 3.20+
165
+ - A C++17 compiler (MSVC, Clang, or GCC)
166
+ - `scikit-build-core`, `pybind11`
167
+
168
+ ```bash
169
+ python -m pip install -U build
170
+ python -m build
171
+ python -m pip install dist/virtualshell-*.whl
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Learn more
177
+
178
+ - [Usage guides](docs/wiki/Usage)
179
+ - [Performance tips](docs/wiki/Usage/Performance%20Tips.md)
180
+ - [Benchmarks](docs/wiki/Project/Benchmarks.md)
181
+
182
+ Bug reports and feature requests are welcome via issues or discussions.
183
+
184
+ ---
185
+
186
+ Licensed under the Apache 2.0 license. See [LICENSE](LICENSE).
@@ -0,0 +1,4 @@
1
+ size,single_mean_ms,single_thr,batch_per_cmd_mean_ms,batch_thr,async_lat_mean_ms,async_thr,batch_eff,async_eff
2
+ 50,4.041,247.301,3.387,295.240,2.359,293.607,1.193,1.186
3
+ 100,3.754,266.165,3.685,271.400,2.247,304.264,1.019,1.142
4
+ 200,3.476,287.452,3.203,312.230,1.938,348.655,1.085,1.212