From 4fd6acf7db4ca91003c71c40c802c969b9dd47bb Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Feb 2026 05:16:01 +0000 Subject: [PATCH 1/6] Add flowfile.shared_location() API for kernel shared directory access Adds a new `shared_location(filename)` function to the kernel's Python API that returns an absolute path on the shared volume where files can be written and read from any FlowFile service. This enables users to easily write files (CSV, Parquet, etc.) to a shared directory that persists across kernel executions and is accessible from core, worker, and kernel containers. Changes: - kernel/manager.py: Pass FLOWFILE_KERNEL_SHARED_DIR env var to kernel containers - flowfile_client.py: Add shared_location() function with auto-mkdir - test_flowfile_client.py: Add 6 tests for shared_location() - FlowfileApiHelp.vue: Add File Utilities section and usage pattern example https://claude.ai/code/session_018xyft77B2g2s3Whm746Qmh --- flowfile_core/flowfile_core/kernel/manager.py | 4 ++ .../elements/pythonScript/FlowfileApiHelp.vue | 28 ++++++++++ .../kernel_runtime/flowfile_client.py | 32 +++++++++++ kernel_runtime/tests/test_flowfile_client.py | 54 +++++++++++++++++++ 4 files changed, 118 insertions(+) diff --git a/flowfile_core/flowfile_core/kernel/manager.py b/flowfile_core/flowfile_core/kernel/manager.py index b3ec0cec..4afc66b3 100644 --- a/flowfile_core/flowfile_core/kernel/manager.py +++ b/flowfile_core/flowfile_core/kernel/manager.py @@ -406,6 +406,10 @@ def _build_kernel_env(self, kernel_id: str, kernel: KernelInfo) -> dict[str, str # kernel, so no translation is required and the variable is omitted. if not self._kernel_volume: env["FLOWFILE_HOST_SHARED_DIR"] = self._shared_volume + # FLOWFILE_KERNEL_SHARED_DIR tells the kernel the absolute path of + # the shared directory *as seen from inside the kernel container*. + # Used by flowfile.shared_location() to resolve user file paths. + env["FLOWFILE_KERNEL_SHARED_DIR"] = self.to_kernel_path(self._shared_volume) # Persistence settings from kernel config env["KERNEL_ID"] = kernel_id env["PERSISTENCE_ENABLED"] = "true" if kernel.persistence_enabled else "false" diff --git a/flowfile_frontend/src/renderer/app/components/nodes/node-types/elements/pythonScript/FlowfileApiHelp.vue b/flowfile_frontend/src/renderer/app/components/nodes/node-types/elements/pythonScript/FlowfileApiHelp.vue index cbb3b417..f03a3409 100644 --- a/flowfile_frontend/src/renderer/app/components/nodes/node-types/elements/pythonScript/FlowfileApiHelp.vue +++ b/flowfile_frontend/src/renderer/app/components/nodes/node-types/elements/pythonScript/FlowfileApiHelp.vue @@ -129,6 +129,23 @@ +
+

File Utilities

+

+ Convenience helpers for working with files on the shared volume. Files written here + are accessible from all FlowFile services and persist across kernel executions. +

+
+ flowfile.shared_location("test_file.csv") +

Returns the absolute path for a file in the shared directory. + Parent directories are created automatically.

+
+
+ flowfile.shared_location("subdir/report.parquet") +

Supports nested paths — subdirectories are created as needed.

+
+
+

Common Patterns

@@ -184,6 +201,17 @@ flowfile.publish_global("rf_model", model, flowfile.log_info("Model published to catalog") +
+
Write to Shared Directory
+
import polars as pl
+
+df = flowfile.read_input().collect()
+
+# Write to the shared directory (accessible from all services)
+df.write_csv(flowfile.shared_location("exports/output.csv"))
+df.write_parquet(flowfile.shared_location("exports/output.parquet"))
+
+
Multiple Inputs
import polars as pl
diff --git a/kernel_runtime/kernel_runtime/flowfile_client.py b/kernel_runtime/kernel_runtime/flowfile_client.py
index 0e8a2fc8..726d84c2 100644
--- a/kernel_runtime/kernel_runtime/flowfile_client.py
+++ b/kernel_runtime/kernel_runtime/flowfile_client.py
@@ -502,6 +502,38 @@ def delete_global_artifact(
             resp.raise_for_status()
 
 
+# ===== File Utilities =====
+
+
+def shared_location(filename: str) -> str:
+    """Return the absolute path for a file in the shared directory.
+
+    The shared directory is accessible from all FlowFile services (core,
+    worker, kernel) and persists across kernel executions.  Use this to
+    write files that should be readable by other services or that should
+    survive container restarts.
+
+    Parent directories are created automatically.
+
+    Args:
+        filename: Relative filename or path, e.g. ``"test_file.csv"`` or
+                  ``"other_dir/test_file.csv"``.
+
+    Returns:
+        Absolute path as a string, ready to pass to file-writing functions.
+
+    Examples::
+
+        df.write_csv(flowfile.shared_location("test_file.csv"))
+        df.write_csv(flowfile.shared_location("reports/monthly.csv"))
+    """
+    base = os.environ.get("FLOWFILE_KERNEL_SHARED_DIR", "/shared")
+    full_path = os.path.join(base, "user_files", filename)
+    parent = os.path.dirname(full_path)
+    os.makedirs(parent, exist_ok=True)
+    return full_path
+
+
 # ===== Logging APIs =====
 
 
diff --git a/kernel_runtime/tests/test_flowfile_client.py b/kernel_runtime/tests/test_flowfile_client.py
index efdefc5f..7f0cf981 100644
--- a/kernel_runtime/tests/test_flowfile_client.py
+++ b/kernel_runtime/tests/test_flowfile_client.py
@@ -1,5 +1,6 @@
 """Tests for kernel_runtime.flowfile_client."""
 
+import os
 from pathlib import Path
 
 import polars as pl
@@ -366,3 +367,56 @@ def test_is_pil_image_without_import(self):
         """Without PIL installed, should return False."""
         result = flowfile_client._is_pil_image("not an image")
         assert result is False
+
+
+class TestSharedLocation:
+    """Tests for flowfile.shared_location()."""
+
+    def test_returns_path_under_user_files(self, tmp_dir: Path, monkeypatch: pytest.MonkeyPatch):
+        shared_dir = str(tmp_dir / "shared")
+        monkeypatch.setenv("FLOWFILE_KERNEL_SHARED_DIR", shared_dir)
+
+        result = flowfile_client.shared_location("test_file.csv")
+        assert result == os.path.join(shared_dir, "user_files", "test_file.csv")
+
+    def test_creates_parent_directories(self, tmp_dir: Path, monkeypatch: pytest.MonkeyPatch):
+        shared_dir = str(tmp_dir / "shared")
+        monkeypatch.setenv("FLOWFILE_KERNEL_SHARED_DIR", shared_dir)
+
+        result = flowfile_client.shared_location("other_dir/test_file.csv")
+        expected = os.path.join(shared_dir, "user_files", "other_dir", "test_file.csv")
+        assert result == expected
+        assert os.path.isdir(os.path.dirname(result))
+
+    def test_nested_subdirectories(self, tmp_dir: Path, monkeypatch: pytest.MonkeyPatch):
+        shared_dir = str(tmp_dir / "shared")
+        monkeypatch.setenv("FLOWFILE_KERNEL_SHARED_DIR", shared_dir)
+
+        result = flowfile_client.shared_location("a/b/c/deep_file.parquet")
+        expected = os.path.join(shared_dir, "user_files", "a", "b", "c", "deep_file.parquet")
+        assert result == expected
+        assert os.path.isdir(os.path.dirname(result))
+
+    def test_defaults_to_shared_when_env_not_set(self, monkeypatch: pytest.MonkeyPatch):
+        monkeypatch.delenv("FLOWFILE_KERNEL_SHARED_DIR", raising=False)
+
+        result = flowfile_client.shared_location("test.csv")
+        assert result == os.path.join("/shared", "user_files", "test.csv")
+
+    def test_file_is_writable(self, tmp_dir: Path, monkeypatch: pytest.MonkeyPatch):
+        shared_dir = str(tmp_dir / "shared")
+        monkeypatch.setenv("FLOWFILE_KERNEL_SHARED_DIR", shared_dir)
+
+        path = flowfile_client.shared_location("writable_test.csv")
+        with open(path, "w") as f:
+            f.write("col1,col2\n1,2\n")
+        assert os.path.isfile(path)
+
+    def test_does_not_require_execution_context(self, tmp_dir: Path, monkeypatch: pytest.MonkeyPatch):
+        """shared_location works without _set_context() being called."""
+        shared_dir = str(tmp_dir / "shared")
+        monkeypatch.setenv("FLOWFILE_KERNEL_SHARED_DIR", shared_dir)
+        flowfile_client._clear_context()
+
+        result = flowfile_client.shared_location("no_context.csv")
+        assert "no_context.csv" in result

From 32f01ed3d0d5fab0e7cc26c5218dc99719729bb3 Mon Sep 17 00:00:00 2001
From: Claude 
Date: Mon, 9 Feb 2026 07:15:46 +0000
Subject: [PATCH 2/6] Add parquet/CSV write roundtrip tests for
 shared_location()

Three new integration-style tests that actually write Polars DataFrames
to shared_location paths and read them back, verifying the full
write-read cycle with no mocking.

https://claude.ai/code/session_018xyft77B2g2s3Whm746Qmh
---
 kernel_runtime/tests/test_flowfile_client.py | 37 ++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/kernel_runtime/tests/test_flowfile_client.py b/kernel_runtime/tests/test_flowfile_client.py
index 7f0cf981..bf3d4b51 100644
--- a/kernel_runtime/tests/test_flowfile_client.py
+++ b/kernel_runtime/tests/test_flowfile_client.py
@@ -420,3 +420,40 @@ def test_does_not_require_execution_context(self, tmp_dir: Path, monkeypatch: py
 
         result = flowfile_client.shared_location("no_context.csv")
         assert "no_context.csv" in result
+
+    def test_write_parquet_roundtrip(self, tmp_dir: Path, monkeypatch: pytest.MonkeyPatch):
+        """Write a Polars DataFrame to shared_location and read it back."""
+        monkeypatch.setenv("FLOWFILE_KERNEL_SHARED_DIR", str(tmp_dir / "shared"))
+
+        df = pl.DataFrame({"id": [1, 2, 3], "value": [10.5, 20.0, 30.1]})
+        path = flowfile_client.shared_location("output.parquet")
+        df.write_parquet(path)
+
+        result = pl.read_parquet(path)
+        assert result.shape == (3, 2)
+        assert result["id"].to_list() == [1, 2, 3]
+        assert result["value"].to_list() == [10.5, 20.0, 30.1]
+
+    def test_write_parquet_nested_path(self, tmp_dir: Path, monkeypatch: pytest.MonkeyPatch):
+        """Write parquet into a nested subdirectory via shared_location."""
+        monkeypatch.setenv("FLOWFILE_KERNEL_SHARED_DIR", str(tmp_dir / "shared"))
+
+        df = pl.DataFrame({"name": ["alice", "bob"], "score": [95, 87]})
+        path = flowfile_client.shared_location("exports/daily/scores.parquet")
+        df.write_parquet(path)
+
+        result = pl.read_parquet(path)
+        assert result["name"].to_list() == ["alice", "bob"]
+
+    def test_write_csv_and_parquet_same_dir(self, tmp_dir: Path, monkeypatch: pytest.MonkeyPatch):
+        """Write both CSV and Parquet to the same shared subdirectory."""
+        monkeypatch.setenv("FLOWFILE_KERNEL_SHARED_DIR", str(tmp_dir / "shared"))
+
+        df = pl.DataFrame({"x": [1, 2], "y": [3, 4]})
+        csv_path = flowfile_client.shared_location("reports/data.csv")
+        parquet_path = flowfile_client.shared_location("reports/data.parquet")
+        df.write_csv(csv_path)
+        df.write_parquet(parquet_path)
+
+        assert pl.read_csv(csv_path).shape == (2, 2)
+        assert pl.read_parquet(parquet_path)["x"].to_list() == [1, 2]

From f105c4a8e43beb3f0da8d8a76f3bb0a34434507d Mon Sep 17 00:00:00 2001
From: Claude 
Date: Mon, 9 Feb 2026 07:20:14 +0000
Subject: [PATCH 3/6] Rename shared_location() to get_shared_location()

Rename the function to make it clearer that it's a getter, not a
property or constant.

https://claude.ai/code/session_018xyft77B2g2s3Whm746Qmh
---
 flowfile_core/flowfile_core/kernel/manager.py |  2 +-
 .../elements/pythonScript/FlowfileApiHelp.vue |  8 ++++----
 .../kernel_runtime/flowfile_client.py         |  6 +++---
 kernel_runtime/tests/test_flowfile_client.py  | 20 +++++++++----------
 4 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/flowfile_core/flowfile_core/kernel/manager.py b/flowfile_core/flowfile_core/kernel/manager.py
index 4afc66b3..c5cea938 100644
--- a/flowfile_core/flowfile_core/kernel/manager.py
+++ b/flowfile_core/flowfile_core/kernel/manager.py
@@ -408,7 +408,7 @@ def _build_kernel_env(self, kernel_id: str, kernel: KernelInfo) -> dict[str, str
             env["FLOWFILE_HOST_SHARED_DIR"] = self._shared_volume
         # FLOWFILE_KERNEL_SHARED_DIR tells the kernel the absolute path of
         # the shared directory *as seen from inside the kernel container*.
-        # Used by flowfile.shared_location() to resolve user file paths.
+        # Used by flowfile.get_shared_location() to resolve user file paths.
         env["FLOWFILE_KERNEL_SHARED_DIR"] = self.to_kernel_path(self._shared_volume)
         # Persistence settings from kernel config
         env["KERNEL_ID"] = kernel_id
diff --git a/flowfile_frontend/src/renderer/app/components/nodes/node-types/elements/pythonScript/FlowfileApiHelp.vue b/flowfile_frontend/src/renderer/app/components/nodes/node-types/elements/pythonScript/FlowfileApiHelp.vue
index f03a3409..53025a75 100644
--- a/flowfile_frontend/src/renderer/app/components/nodes/node-types/elements/pythonScript/FlowfileApiHelp.vue
+++ b/flowfile_frontend/src/renderer/app/components/nodes/node-types/elements/pythonScript/FlowfileApiHelp.vue
@@ -136,12 +136,12 @@
             are accessible from all FlowFile services and persist across kernel executions.
           

- flowfile.shared_location("test_file.csv") + flowfile.get_shared_location("test_file.csv")

Returns the absolute path for a file in the shared directory. Parent directories are created automatically.

- flowfile.shared_location("subdir/report.parquet") + flowfile.get_shared_location("subdir/report.parquet")

Supports nested paths — subdirectories are created as needed.

@@ -208,8 +208,8 @@ flowfile.log_info("Model published to catalog") df = flowfile.read_input().collect() # Write to the shared directory (accessible from all services) -df.write_csv(flowfile.shared_location("exports/output.csv")) -df.write_parquet(flowfile.shared_location("exports/output.parquet")) +df.write_csv(flowfile.get_shared_location("exports/output.csv")) +df.write_parquet(flowfile.get_shared_location("exports/output.parquet"))
diff --git a/kernel_runtime/kernel_runtime/flowfile_client.py b/kernel_runtime/kernel_runtime/flowfile_client.py index 726d84c2..82b5001b 100644 --- a/kernel_runtime/kernel_runtime/flowfile_client.py +++ b/kernel_runtime/kernel_runtime/flowfile_client.py @@ -505,7 +505,7 @@ def delete_global_artifact( # ===== File Utilities ===== -def shared_location(filename: str) -> str: +def get_shared_location(filename: str) -> str: """Return the absolute path for a file in the shared directory. The shared directory is accessible from all FlowFile services (core, @@ -524,8 +524,8 @@ def shared_location(filename: str) -> str: Examples:: - df.write_csv(flowfile.shared_location("test_file.csv")) - df.write_csv(flowfile.shared_location("reports/monthly.csv")) + df.write_csv(flowfile.get_shared_location("test_file.csv")) + df.write_csv(flowfile.get_shared_location("reports/monthly.csv")) """ base = os.environ.get("FLOWFILE_KERNEL_SHARED_DIR", "/shared") full_path = os.path.join(base, "user_files", filename) diff --git a/kernel_runtime/tests/test_flowfile_client.py b/kernel_runtime/tests/test_flowfile_client.py index bf3d4b51..24c76985 100644 --- a/kernel_runtime/tests/test_flowfile_client.py +++ b/kernel_runtime/tests/test_flowfile_client.py @@ -376,14 +376,14 @@ def test_returns_path_under_user_files(self, tmp_dir: Path, monkeypatch: pytest. shared_dir = str(tmp_dir / "shared") monkeypatch.setenv("FLOWFILE_KERNEL_SHARED_DIR", shared_dir) - result = flowfile_client.shared_location("test_file.csv") + result = flowfile_client.get_shared_location("test_file.csv") assert result == os.path.join(shared_dir, "user_files", "test_file.csv") def test_creates_parent_directories(self, tmp_dir: Path, monkeypatch: pytest.MonkeyPatch): shared_dir = str(tmp_dir / "shared") monkeypatch.setenv("FLOWFILE_KERNEL_SHARED_DIR", shared_dir) - result = flowfile_client.shared_location("other_dir/test_file.csv") + result = flowfile_client.get_shared_location("other_dir/test_file.csv") expected = os.path.join(shared_dir, "user_files", "other_dir", "test_file.csv") assert result == expected assert os.path.isdir(os.path.dirname(result)) @@ -392,7 +392,7 @@ def test_nested_subdirectories(self, tmp_dir: Path, monkeypatch: pytest.MonkeyPa shared_dir = str(tmp_dir / "shared") monkeypatch.setenv("FLOWFILE_KERNEL_SHARED_DIR", shared_dir) - result = flowfile_client.shared_location("a/b/c/deep_file.parquet") + result = flowfile_client.get_shared_location("a/b/c/deep_file.parquet") expected = os.path.join(shared_dir, "user_files", "a", "b", "c", "deep_file.parquet") assert result == expected assert os.path.isdir(os.path.dirname(result)) @@ -400,14 +400,14 @@ def test_nested_subdirectories(self, tmp_dir: Path, monkeypatch: pytest.MonkeyPa def test_defaults_to_shared_when_env_not_set(self, monkeypatch: pytest.MonkeyPatch): monkeypatch.delenv("FLOWFILE_KERNEL_SHARED_DIR", raising=False) - result = flowfile_client.shared_location("test.csv") + result = flowfile_client.get_shared_location("test.csv") assert result == os.path.join("/shared", "user_files", "test.csv") def test_file_is_writable(self, tmp_dir: Path, monkeypatch: pytest.MonkeyPatch): shared_dir = str(tmp_dir / "shared") monkeypatch.setenv("FLOWFILE_KERNEL_SHARED_DIR", shared_dir) - path = flowfile_client.shared_location("writable_test.csv") + path = flowfile_client.get_shared_location("writable_test.csv") with open(path, "w") as f: f.write("col1,col2\n1,2\n") assert os.path.isfile(path) @@ -418,7 +418,7 @@ def test_does_not_require_execution_context(self, tmp_dir: Path, monkeypatch: py monkeypatch.setenv("FLOWFILE_KERNEL_SHARED_DIR", shared_dir) flowfile_client._clear_context() - result = flowfile_client.shared_location("no_context.csv") + result = flowfile_client.get_shared_location("no_context.csv") assert "no_context.csv" in result def test_write_parquet_roundtrip(self, tmp_dir: Path, monkeypatch: pytest.MonkeyPatch): @@ -426,7 +426,7 @@ def test_write_parquet_roundtrip(self, tmp_dir: Path, monkeypatch: pytest.Monkey monkeypatch.setenv("FLOWFILE_KERNEL_SHARED_DIR", str(tmp_dir / "shared")) df = pl.DataFrame({"id": [1, 2, 3], "value": [10.5, 20.0, 30.1]}) - path = flowfile_client.shared_location("output.parquet") + path = flowfile_client.get_shared_location("output.parquet") df.write_parquet(path) result = pl.read_parquet(path) @@ -439,7 +439,7 @@ def test_write_parquet_nested_path(self, tmp_dir: Path, monkeypatch: pytest.Monk monkeypatch.setenv("FLOWFILE_KERNEL_SHARED_DIR", str(tmp_dir / "shared")) df = pl.DataFrame({"name": ["alice", "bob"], "score": [95, 87]}) - path = flowfile_client.shared_location("exports/daily/scores.parquet") + path = flowfile_client.get_shared_location("exports/daily/scores.parquet") df.write_parquet(path) result = pl.read_parquet(path) @@ -450,8 +450,8 @@ def test_write_csv_and_parquet_same_dir(self, tmp_dir: Path, monkeypatch: pytest monkeypatch.setenv("FLOWFILE_KERNEL_SHARED_DIR", str(tmp_dir / "shared")) df = pl.DataFrame({"x": [1, 2], "y": [3, 4]}) - csv_path = flowfile_client.shared_location("reports/data.csv") - parquet_path = flowfile_client.shared_location("reports/data.parquet") + csv_path = flowfile_client.get_shared_location("reports/data.csv") + parquet_path = flowfile_client.get_shared_location("reports/data.parquet") df.write_csv(csv_path) df.write_parquet(parquet_path) From ea810dd38ea8b32ace654fc3b95bb304e5d5f361 Mon Sep 17 00:00:00 2001 From: edwardvanechoud Date: Mon, 9 Feb 2026 08:24:48 +0100 Subject: [PATCH 4/6] adding hint --- .../node-types/elements/pythonScript/flowfileCompletions.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/flowfile_frontend/src/renderer/app/components/nodes/node-types/elements/pythonScript/flowfileCompletions.ts b/flowfile_frontend/src/renderer/app/components/nodes/node-types/elements/pythonScript/flowfileCompletions.ts index 4f8e3f71..e47abf2b 100644 --- a/flowfile_frontend/src/renderer/app/components/nodes/node-types/elements/pythonScript/flowfileCompletions.ts +++ b/flowfile_frontend/src/renderer/app/components/nodes/node-types/elements/pythonScript/flowfileCompletions.ts @@ -90,6 +90,7 @@ export const flowfileCompletionVals = [ detail: "flowfile.list_global_artifacts(namespace_id?, tags?)", apply: "list_global_artifacts()", }, + {label: "get_shared_location",}, { label: "delete_global_artifact", type: "function", From 97ee86503a985b0cbfa917e6a199c1ed9f484cb1 Mon Sep 17 00:00:00 2001 From: edwardvanechoud Date: Mon, 9 Feb 2026 08:26:03 +0100 Subject: [PATCH 5/6] adding hint --- .../elements/pythonScript/flowfileCompletions.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/flowfile_frontend/src/renderer/app/components/nodes/node-types/elements/pythonScript/flowfileCompletions.ts b/flowfile_frontend/src/renderer/app/components/nodes/node-types/elements/pythonScript/flowfileCompletions.ts index e47abf2b..91a1cf14 100644 --- a/flowfile_frontend/src/renderer/app/components/nodes/node-types/elements/pythonScript/flowfileCompletions.ts +++ b/flowfile_frontend/src/renderer/app/components/nodes/node-types/elements/pythonScript/flowfileCompletions.ts @@ -90,7 +90,12 @@ export const flowfileCompletionVals = [ detail: "flowfile.list_global_artifacts(namespace_id?, tags?)", apply: "list_global_artifacts()", }, - {label: "get_shared_location",}, + {label: "get_shared_location", + type: "function", + info: "Get the shared location to make objects available to other processes", + detail: "flowfile.get_shared_location()->str", + apply: "get_shared_location()", + }, { label: "delete_global_artifact", type: "function", From 33ec015f67868d6583b8991f862db7b0fc733c7f Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 10 Feb 2026 07:32:22 +0000 Subject: [PATCH 6/6] Fix test_defaults_to_shared_when_env_not_set for CI Patch os.makedirs in the fallback test to avoid PermissionError when /shared is not writable on CI runners. Also asserts makedirs was called with the correct path. https://claude.ai/code/session_018xyft77B2g2s3Whm746Qmh --- kernel_runtime/tests/test_flowfile_client.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kernel_runtime/tests/test_flowfile_client.py b/kernel_runtime/tests/test_flowfile_client.py index 24c76985..74d1d2ed 100644 --- a/kernel_runtime/tests/test_flowfile_client.py +++ b/kernel_runtime/tests/test_flowfile_client.py @@ -397,11 +397,15 @@ def test_nested_subdirectories(self, tmp_dir: Path, monkeypatch: pytest.MonkeyPa assert result == expected assert os.path.isdir(os.path.dirname(result)) - def test_defaults_to_shared_when_env_not_set(self, monkeypatch: pytest.MonkeyPatch): + def test_defaults_to_shared_when_env_not_set(self, tmp_dir: Path, monkeypatch: pytest.MonkeyPatch): monkeypatch.delenv("FLOWFILE_KERNEL_SHARED_DIR", raising=False) + # Patch os.makedirs to avoid PermissionError on /shared in CI + created = [] + monkeypatch.setattr(os, "makedirs", lambda p, exist_ok=False: created.append(p)) result = flowfile_client.get_shared_location("test.csv") assert result == os.path.join("/shared", "user_files", "test.csv") + assert created == [os.path.join("/shared", "user_files")] def test_file_is_writable(self, tmp_dir: Path, monkeypatch: pytest.MonkeyPatch): shared_dir = str(tmp_dir / "shared")