Ionite
1 year ago
10 changed files with 369 additions and 18 deletions
@ -0,0 +1,49 @@
|
||||
""" |
||||
Startup site customization for Stability Matrix. |
||||
|
||||
Currently this installs an audit hook to notify the parent process when input() is called, |
||||
so we can prompt the user to enter something. |
||||
""" |
||||
|
||||
import sys |
||||
|
||||
# Application Program Command escape sequence |
||||
# This wraps messages sent to the parent process. |
||||
esc_apc = "\x9F" |
||||
esc_prefix = "[SM;" |
||||
esc_st = "\x9C" |
||||
|
||||
|
||||
def send_apc(msg: str): |
||||
"""Send an Application Program Command to the parent process.""" |
||||
sys.stdout.flush() |
||||
sys.stdout.write(esc_apc + esc_prefix + msg + esc_st) |
||||
sys.stdout.flush() |
||||
|
||||
|
||||
def send_apc_input(prompt: str): |
||||
"""Apc message for input() prompt.""" |
||||
send_apc('{"type":"input","data":"' + str(prompt) + '"}') |
||||
|
||||
|
||||
def audit(event: str, *args): |
||||
"""Main audit hook function.""" |
||||
# https://docs.python.org/3/library/functions.html#input |
||||
# input() raises audit event `builtins.input` with args (prompt: str) *before* reading from stdin. |
||||
# `builtins.input/result` raised after reading from stdin. |
||||
|
||||
if event == "builtins.input": |
||||
try: |
||||
prompts = args[0] if args else () |
||||
prompt = "".join(prompts) |
||||
send_apc_input(prompt) |
||||
except Exception: |
||||
pass |
||||
|
||||
|
||||
# Reconfigure stdout to UTF-8 |
||||
# noinspection PyUnresolvedReferences |
||||
sys.stdout.reconfigure(encoding="utf-8") |
||||
|
||||
# Install the audit hook |
||||
sys.addaudithook(audit) |
@ -0,0 +1,12 @@
|
||||
using System.Text.Json.Serialization; |
||||
|
||||
namespace StabilityMatrix.Core.Processes; |
||||
|
||||
public readonly struct ApcMessage |
||||
{ |
||||
[JsonPropertyName("type")] |
||||
public required ApcType Type { get; init; } |
||||
|
||||
[JsonPropertyName("data")] |
||||
public required string Data { get; init; } |
||||
} |
@ -0,0 +1,55 @@
|
||||
using System.Diagnostics; |
||||
using System.Diagnostics.CodeAnalysis; |
||||
using System.Text; |
||||
using System.Text.Json; |
||||
|
||||
namespace StabilityMatrix.Core.Processes; |
||||
|
||||
/// <summary> |
||||
/// Parse escaped messages from subprocess |
||||
/// The message standard: |
||||
/// - Message events are prefixed with char 'APC' (9F) |
||||
/// - Followed by '[SM;' |
||||
/// - Json dict string of 2 strings, 'type' and 'data' |
||||
/// - Ends with char 'ST' (9C) |
||||
/// </summary> |
||||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] |
||||
internal static class ApcParser |
||||
{ |
||||
public const char ApcEscape = (char) 0x9F; |
||||
public const string IdPrefix = "[SM;"; |
||||
public const char StEscape = (char) 0x9C; |
||||
|
||||
/// <summary> |
||||
/// Attempts to extract an APC message from the given text |
||||
/// </summary> |
||||
/// <returns>ApcMessage struct</returns> |
||||
public static bool TryParse(string text, out ApcMessage? message) |
||||
{ |
||||
message = null; |
||||
var startIndex = text.IndexOf(ApcEscape); |
||||
if (startIndex == -1) return false; |
||||
|
||||
// Check the IdPrefix follows the ApcEscape |
||||
var idIndex = text.IndexOf(IdPrefix, startIndex + 1, StringComparison.Ordinal); |
||||
if (idIndex == -1) return false; |
||||
|
||||
// Get the end index (ST escape) |
||||
var stIndex = text.IndexOf(StEscape, idIndex + IdPrefix.Length); |
||||
if (stIndex == -1) return false; |
||||
|
||||
// Extract the json string (between idIndex and stIndex) |
||||
var json = text.Substring(idIndex + IdPrefix.Length, stIndex - idIndex - IdPrefix.Length); |
||||
|
||||
try |
||||
{ |
||||
message = JsonSerializer.Deserialize<ApcMessage>(json); |
||||
return true; |
||||
} |
||||
catch (Exception e) |
||||
{ |
||||
Debug.WriteLine($"Failed to parse APC message: {e.Message}"); |
||||
return false; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,11 @@
|
||||
using System.Runtime.Serialization; |
||||
using System.Text.Json.Serialization; |
||||
|
||||
namespace StabilityMatrix.Core.Processes; |
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))] |
||||
public enum ApcType |
||||
{ |
||||
[EnumMember(Value = "input")] |
||||
Input = 1, |
||||
} |
Loading…
Reference in new issue