forked from 1b-agent/opencadstudio-upstream
fix(plugins): Python plugins see the host document again
Plugins read the host's document through a shared reference across the dynamic-library boundary, and the entity index hashed with foldhash's per-process seed, so every handle lookup from the plugin missed. The codec now uses a fixed hasher (opencadcodec f385f75; kernel 9841749, graph e9c23ad). The Python IPC suite also caught up with intended changes: ATTDEF renders its tag, DXF now keeps the lock-position flag, and the modify audit turns associative arrays off since it checks loose copies. The rate-limiter test waits for the host's check instead of a fixed sleep that raced on macOS, and ENV_LOCK no longer cascades a poisoned lock into every later test. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
c48fac7ae9
commit
ee2898095e
11 files changed
+43
-42
No files matched your search
Generated
+4
-4
@@ -4373,7 +4373,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "opencadcodec"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=d36331d#d36331d9e4f01576cdd29e8420caeb6503675c2c"
|
||||
source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=f385f75#f385f75decc311b7caeefb22d4e7435d97e12edb"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.13.1",
|
||||
@@ -4396,7 +4396,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "opencadgraph"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=74aac56#74aac563a09388489834a1cbafc1b3893a739802"
|
||||
source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=e9c23ad#e9c23adb0facdd00d043ca9aeb6a5dacf0208779"
|
||||
dependencies = [
|
||||
"opencadkernel",
|
||||
"serde_json",
|
||||
@@ -4405,7 +4405,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "opencadkernel"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=60f086b#60f086bd6200924f9f96f31de4722d1216bb29b9"
|
||||
source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=9841749#9841749422c8fb0fa1f68205ac725d27f13e13bd"
|
||||
dependencies = [
|
||||
"cavalier_contours",
|
||||
"opencadcodec",
|
||||
@@ -4416,7 +4416,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "opencadkernel-constraints"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=60f086b#60f086bd6200924f9f96f31de4722d1216bb29b9"
|
||||
source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=9841749#9841749422c8fb0fa1f68205ac725d27f13e13bd"
|
||||
dependencies = [
|
||||
"nalgebra",
|
||||
]
|
||||
|
||||
+4
-4
@@ -37,10 +37,10 @@ rfd = "0.17"
|
||||
clap = { version = "4", features = ["derive", "string"] }
|
||||
env_logger = "0.11"
|
||||
log = "0.4"
|
||||
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "d36331d", features = ["serde"] }
|
||||
kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "60f086b", features = ["acis", "offset"] }
|
||||
kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "60f086b" }
|
||||
graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "74aac56" }
|
||||
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "f385f75", features = ["serde"] }
|
||||
kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "9841749", features = ["acis", "offset"] }
|
||||
kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "9841749" }
|
||||
graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "e9c23ad" }
|
||||
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
|
||||
# Shared SAVE serialization dispatch + web-worker wire format: one
|
||||
# implementation for the inline (fallback/native) path and the worker path.
|
||||
|
||||
@@ -15,7 +15,7 @@ serde_json = { version = "1", optional = true }
|
||||
# Pulled in only by the `host` feature, which adds the `opencadcodec`-typed
|
||||
# `HostApi` runtime surface. The default crate stays dependency-free so engine
|
||||
# crates and external tooling can depend on the manifest/ribbon contract cheaply.
|
||||
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "d36331d", optional = true, features = ["serde"] }
|
||||
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "f385f75", optional = true, features = ["serde"] }
|
||||
|
||||
# Runtime IPC and serialization (host feature only).
|
||||
interprocess = { version = "2", optional = true }
|
||||
@@ -39,7 +39,7 @@ serde_json = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
cargo-lock = "11"
|
||||
# opencadcodec is scanned at build time to generate the embedded type registry.
|
||||
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "d36331d", features = ["serde"] }
|
||||
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "f385f75", features = ["serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1"
|
||||
@@ -1210,7 +1210,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn v4_client_notification_queue_size_env_is_honored() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let prev = std::env::var("OCS_PLUGIN_NOTIFY_QUEUE_SIZE").ok();
|
||||
std::env::set_var("OCS_PLUGIN_NOTIFY_QUEUE_SIZE", "7");
|
||||
assert_eq!(notify_queue_size(), 7);
|
||||
@@ -1222,7 +1222,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn v4_client_notification_queue_drops_newest_on_overflow() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let prev = std::env::var("OCS_PLUGIN_NOTIFY_QUEUE_SIZE").ok();
|
||||
std::env::set_var("OCS_PLUGIN_NOTIFY_QUEUE_SIZE", "1");
|
||||
let (host_stream, mut runner_stream) = connect_pair();
|
||||
|
||||
@@ -108,14 +108,14 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn effective_max_defaults_to_api_version() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
std::env::remove_var(MAX_API_VERSION_ENV);
|
||||
assert_eq!(effective_max_api_version(), API_VERSION);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn effective_max_can_disable_v4() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
std::env::set_var(MAX_API_VERSION_ENV, "3");
|
||||
assert_eq!(effective_max_api_version(), 3);
|
||||
assert!(!host_accepts_plugin_version(4));
|
||||
@@ -126,7 +126,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn effective_max_below_minimum_is_v3_only_mode() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
std::env::set_var(MAX_API_VERSION_ENV, "2");
|
||||
assert_eq!(effective_max_api_version(), 3);
|
||||
assert!(!host_accepts_plugin_version(4));
|
||||
@@ -137,7 +137,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn effective_max_clamps_to_supported_range() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
std::env::set_var(MAX_API_VERSION_ENV, "99");
|
||||
assert_eq!(effective_max_api_version(), API_VERSION);
|
||||
std::env::set_var(MAX_API_VERSION_ENV, "1");
|
||||
@@ -147,7 +147,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn host_accepts_supported_range() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
std::env::remove_var(MAX_API_VERSION_ENV);
|
||||
assert!(host_accepts_plugin_version(API_VERSION_MIN_SUPPORTED));
|
||||
assert!(host_accepts_plugin_version(API_VERSION));
|
||||
|
||||
@@ -1497,7 +1497,7 @@ mod timeout_tests {
|
||||
|
||||
#[test]
|
||||
fn runner_handshake_correct_token_is_accepted() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
std::env::remove_var("OCS_PLUGIN_MAX_API_VERSION");
|
||||
let result = verify_runner_handshake(
|
||||
RunnerHandshake::Token("correct-token".to_string()),
|
||||
@@ -1512,7 +1512,7 @@ mod timeout_tests {
|
||||
|
||||
#[test]
|
||||
fn runner_handshake_tokenv4_selects_v4_path() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
std::env::remove_var("OCS_PLUGIN_MAX_API_VERSION");
|
||||
let result = verify_runner_handshake(
|
||||
RunnerHandshake::TokenV4 {
|
||||
@@ -1530,7 +1530,7 @@ mod timeout_tests {
|
||||
|
||||
#[test]
|
||||
fn runner_handshake_tokenv4_mismatched_version_is_rejected() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
std::env::remove_var("OCS_PLUGIN_MAX_API_VERSION");
|
||||
let result = verify_runner_handshake(
|
||||
RunnerHandshake::TokenV4 {
|
||||
@@ -1547,7 +1547,7 @@ mod timeout_tests {
|
||||
|
||||
#[test]
|
||||
fn runner_handshake_tokenv4_rejected_when_v4_disabled() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
std::env::set_var("OCS_PLUGIN_MAX_API_VERSION", "3");
|
||||
let result = verify_runner_handshake(
|
||||
RunnerHandshake::TokenV4 {
|
||||
|
||||
@@ -482,7 +482,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn execute_code_timeout_floor_is_at_least_60s_and_env_override() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let prev_call_timeout = std::env::var("OCS_PLUGIN_CALL_TIMEOUT_SECS").ok();
|
||||
let prev_test_floor = std::env::var("OCS_PLUGIN_TEST_FLOOR_SECS").ok();
|
||||
let prev_execute_timeout = std::env::var("OCS_PLUGIN_EXECUTE_TIMEOUT_SECS").ok();
|
||||
@@ -519,7 +519,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn v4_connection_request_response() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
set_test_env();
|
||||
let (host_stream, mut runner_stream) = connect_pair();
|
||||
let handler: Arc<dyn Fn(Option<u64>, PluginNotification) + Send + Sync> =
|
||||
@@ -565,7 +565,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn v4_connection_handles_nested_plugin_request() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
set_test_env();
|
||||
let (host_stream, mut runner_stream) = connect_pair();
|
||||
let handler: Arc<dyn Fn(Option<u64>, PluginNotification) + Send + Sync> =
|
||||
@@ -635,7 +635,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn v4_connection_routes_entity_transaction_over_nested_ipc() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
set_test_env();
|
||||
let (host_stream, mut runner_stream) = connect_pair();
|
||||
let handler: Arc<dyn Fn(Option<u64>, PluginNotification) + Send + Sync> =
|
||||
@@ -705,7 +705,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn v4_connection_notification_invokes_handler() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
set_test_env();
|
||||
use crate::host::LogLevel;
|
||||
|
||||
@@ -744,7 +744,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn v4_connection_handler_panic_is_isolated() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
set_test_env();
|
||||
use crate::host::LogLevel;
|
||||
|
||||
@@ -792,7 +792,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn v4_connection_notification_rate_limiter_drops_excess() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
set_test_env();
|
||||
std::env::set_var("OCS_PLUGIN_NOTIFY_RATE_LIMIT", "2");
|
||||
use crate::host::LogLevel;
|
||||
@@ -805,6 +805,7 @@ mod tests {
|
||||
*received2.lock().unwrap() += 1;
|
||||
});
|
||||
let conn = V4Connection::new(host_stream, handler).unwrap();
|
||||
let (checked_tx, checked_rx) = mpsc::channel::<()>();
|
||||
|
||||
let runner = thread::spawn(move || {
|
||||
for _ in 0..10 {
|
||||
@@ -820,9 +821,9 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
// Keep the stream open long enough for the host to verify the
|
||||
// reader thread is still alive after the flood.
|
||||
std::thread::sleep(Duration::from_millis(300));
|
||||
// Keep the stream open until the host has checked the reader
|
||||
// thread survived the flood; a fixed sleep raced on slow runners.
|
||||
let _ = checked_rx.recv_timeout(Duration::from_secs(30));
|
||||
});
|
||||
|
||||
std::thread::sleep(Duration::from_millis(200));
|
||||
@@ -835,6 +836,7 @@ mod tests {
|
||||
conn.is_alive(),
|
||||
"connection should survive rate-limited flood"
|
||||
);
|
||||
checked_tx.send(()).unwrap();
|
||||
runner.join().unwrap();
|
||||
drop(conn);
|
||||
std::env::remove_var("OCS_PLUGIN_NOTIFY_RATE_LIMIT");
|
||||
@@ -843,7 +845,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn execute_code_returns_delayed_result_with_tab_index() {
|
||||
let _guard = ENV_LOCK.lock().unwrap();
|
||||
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
set_test_env();
|
||||
|
||||
let (host_stream, mut runner_stream) = connect_pair();
|
||||
|
||||
@@ -10,6 +10,6 @@ publish = false
|
||||
# exactly one implementation. No UI/app dependencies — only the codec + serde.
|
||||
|
||||
[dependencies]
|
||||
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "d36331d", features = ["serde"] }
|
||||
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "f385f75", features = ["serde"] }
|
||||
bincode = "1.3"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
@@ -8,7 +8,7 @@ publish = false
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "d36331d", features = ["serde"] }
|
||||
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "f385f75", features = ["serde"] }
|
||||
bincode = "1.3"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
console_error_panic_hook = "0.1"
|
||||
|
||||
Generated
+1
-1
@@ -1264,7 +1264,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "opencadcodec"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=d36331d#d36331d9e4f01576cdd29e8420caeb6503675c2c"
|
||||
source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=f385f75#f385f75decc311b7caeefb22d4e7435d97e12edb"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
|
||||
@@ -5199,7 +5199,7 @@ mod tests {
|
||||
assert!(strokes.iter().any(|stroke| stroke
|
||||
.run
|
||||
.as_ref()
|
||||
.is_some_and(|run| run.text.contains("PN-002"))));
|
||||
.is_some_and(|run| run.text.contains("PART_NO"))));
|
||||
|
||||
for (label, patch, message) in [
|
||||
("Reject tag", "'tag':'BAD TAG'", "whitespace"),
|
||||
@@ -8644,12 +8644,8 @@ mod tests {
|
||||
expect_created: "PART_NO|Part number|PN-001 ins1.0,2.0 al3.0,4.0 h2.5 r0.25 wf1.25 ob0.10 Center/Top f1010 fl12 tg2 lock1",
|
||||
expect_edited: "PART_NO|Serial|PN-002 ins4.0,5.0 al3.0,4.0 h3.0 r0.50 wf0.80 ob0.20 Right/Middle f0101 fl20 tg2 lock1",
|
||||
expect_reedited: "PART_NO|Serial|PN-003 ins4.0,5.0 al3.0,4.0 h3.0 r0.50 wf0.80 ob0.20 Right/Middle f0101 fl20 tg2 lock1",
|
||||
// BLOCKER (opencadcodec): the DXF ATTDEF writer emits no group 280 at
|
||||
// all — neither the version byte nor the lock-position flag the
|
||||
// reader looks for after it — so `lock_position` reopens false. Every
|
||||
// other field round-trips since opencadcodec dd1d7bf.
|
||||
expect_edited_dxf: "PART_NO|Serial|PN-002 ins4.0,5.0 al3.0,4.0 h3.0 r0.50 wf0.80 ob0.20 Right/Middle f0101 fl20 tg2 lock0",
|
||||
expect_reedited_dxf: "PART_NO|Serial|PN-003 ins4.0,5.0 al3.0,4.0 h3.0 r0.50 wf0.80 ob0.20 Right/Middle f0101 fl20 tg2 lock0",
|
||||
expect_edited_dxf: "",
|
||||
expect_reedited_dxf: "",
|
||||
expect_edited_dwg: "",
|
||||
expect_reedited_dwg: "",
|
||||
});
|
||||
@@ -12126,6 +12122,9 @@ LT.delete('Temp')
|
||||
let Some(plugin_path) = std::env::var_os("OCS_TEST_PYTHON_PLUGIN") else {
|
||||
return;
|
||||
};
|
||||
// The array assertions below check loose copies; ARRAYRECT/ARRAYPOLAR
|
||||
// build associative arrays by default (thread-local, this thread only).
|
||||
crate::modules::draw::defaults::set_array_assoc(0.0);
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
app.tabs[0].is_start = false;
|
||||
let mut host = HostSession::new(&mut app, 0);
|
||||
|
||||
Reference in new issue
Block a user