Repository: PrefectHQ/prefect

Issue:
Change "PYTHONPATH" construct for local agent
## Current behavior
Local agent has the "--import-path" feature, but its now availible only for unix enviroment, because PYTHONPATH is concatenated with ":"
current_env["PYTHONPATH"] = ":".join(python_path)

## Proposed behavior
Use  "os.pathsep" instead ":", to get the correct value aswell for Windows users



Comments:

Patch:
diff --git a/changes/pr3548.yaml b/changes/pr3548.yaml
new file mode 100644
index 0000000000..f9b4d08522
--- /dev/null
+++ b/changes/pr3548.yaml
@@ -0,0 +1,5 @@
+enhancement:
+  - "`RenameFlowRunTask`: use default `flow_run_id` value from context - [#3548](https://github.com/PrefectHQ/prefect/pull/3548)"
+
+contributor:
+  - "[Panagiotis Simakis](https://github.com/sp1thas)"
diff --git a/changes/pr3549.yaml b/changes/pr3549.yaml
new file mode 100644
index 0000000000..f8c16afd92
--- /dev/null
+++ b/changes/pr3549.yaml
@@ -0,0 +1,2 @@
+enhancement:
+  - "Raise a better error message when trying to register a flow with parameters with JSON-incompatible defaults - [#3549](https://github.com/PrefectHQ/prefect/pull/3549)"
diff --git a/changes/pr3551.yaml b/changes/pr3551.yaml
new file mode 100644
index 0000000000..729d7b304b
--- /dev/null
+++ b/changes/pr3551.yaml
@@ -0,0 +1,2 @@
+fix:
+  - "Fix LocalAgent PYTHONPATH construction on Windows - [#3551](https://github.com/PrefectHQ/prefect/pull/3551)"
diff --git a/src/prefect/agent/local/agent.py b/src/prefect/agent/local/agent.py
index 61cb6ba95a..30ff1588df 100644
--- a/src/prefect/agent/local/agent.py
+++ b/src/prefect/agent/local/agent.py
@@ -225,7 +225,7 @@ class LocalAgent(Agent):
             python_path.append(os.environ["PYTHONPATH"])
         if self.import_paths:
             python_path.extend(self.import_paths)
-        env["PYTHONPATH"] = ":".join(python_path)
+        env["PYTHONPATH"] = os.pathsep.join(python_path)
 
         # 4. Values set on the agent via `--env`
         env.update(self.env_vars)
diff --git a/src/prefect/tasks/prefect/flow_run_rename.py b/src/prefect/tasks/prefect/flow_run_rename.py
index d4127a231c..69bd46312d 100644
--- a/src/prefect/tasks/prefect/flow_run_rename.py
+++ b/src/prefect/tasks/prefect/flow_run_rename.py
@@ -1,6 +1,7 @@
 import warnings
 from typing import Any
 
+import prefect
 from prefect import Task
 from prefect.client import Client
 from prefect.utilities.tasks import defaults_from_attrs
@@ -30,24 +31,30 @@ class RenameFlowRun(Task):
     def run(self, flow_run_id: str, flow_run_name: str) -> bool:
         """
         Args:
-            - flow_run_id (str, optional): The ID of the flow run to rename
+            - flow_run_id (str, optional): The ID of the flow run to rename. If `None`,
+                the `flow_run_id` from `prefect.context` will be used as default value
             - flow_run_name (str, optional): The new flow run name
 
         Returns:
             - bool: Boolean representing whether the flow run was renamed successfully or not.
 
         Raises:
-            - ValueError: If flow_run_id or name is not provided
+            - ValueError: If `flow_run_id` is not provided and `flow_run_id` does not exist
+                in `prefect.context`
+            - ValueError: If `flow_run_name` is not provided
 
         Example:
             ```python
             from prefect.tasks.prefect.flow_rename import FlowRenameTask
 
-            rename_flow = FlowRenameTask(flow_run_id="id123", flow_name="A new flow run name")
+            rename_flow = FlowRenameTask(flow_name="A new flow run name")
             ```
         """
-        if flow_run_id is None:
-            raise ValueError("Must provide a flow run ID.")
+        flow_run_id = flow_run_id or prefect.context.get("flow_run_id")
+        if not flow_run_id:
+            raise ValueError(
+                "`flow_run_id` must be explicitly provided or available in the context"
+            )
         if flow_run_name is None:
             raise ValueError("Must provide a flow name.")
 
diff --git a/src/prefect/utilities/serialization.py b/src/prefect/utilities/serialization.py
index 7225c02b8f..47eb01e970 100644
--- a/src/prefect/utilities/serialization.py
+++ b/src/prefect/utilities/serialization.py
@@ -179,14 +179,23 @@ class JSONCompatible(fields.Field):
         self.validators.insert(0, self._validate_json)
 
     def _serialize(self, value, attr, obj, **kwargs):  # type: ignore
-        self._validate_json(value)
+        try:
+            json.dumps(value)
+        except TypeError:
+            raise ValidationError(
+                "When running with Prefect Cloud/Server, values for "
+                f"`{type(obj).__name__}.{attr}` must be JSON compatible. "
+                f"Unable to serialize `{value!r}`."
+            ) from None
         return super()._serialize(value, attr, obj, **kwargs)
 
     def _validate_json(self, value: Any) -> None:
         try:
             json.dumps(value)
-        except TypeError as type_error:
-            raise ValidationError("Value is not JSON-compatible") from type_error
+        except TypeError:
+            raise ValidationError(
+                f"Values must be JSON compatible, got `{value!r}`"
+            ) from None
 
 
 class Nested(fields.Nested):


Test patch:
diff --git a/tests/agent/test_local_agent.py b/tests/agent/test_local_agent.py
index 4c2d4d657d..5abedcfe12 100644
--- a/tests/agent/test_local_agent.py
+++ b/tests/agent/test_local_agent.py
@@ -87,18 +87,22 @@ def test_local_agent_uses_ip_if_dockerdesktop_hostname(monkeypatch):
     assert "IP" in agent.labels
 
 
-def test_populate_env_vars():
+def test_populate_env_vars(monkeypatch):
     agent = LocalAgent()
 
+    # The python path may be a single item and we want to ensure the correct separator
+    # is added so we will ensure PYTHONPATH has an item in it to start
+    if not os.environ.get("PYTHONPATH", ""):
+        monkeypatch.setenv("PYTHONPATH", "foobar")
+
     env_vars = agent.populate_env_vars(
         GraphQLResult({"id": "id", "name": "name", "flow": {"id": "foo"}})
     )
-    python_path = env_vars.pop("PYTHONPATH", "")
-    assert os.getcwd() in python_path
 
     expected = os.environ.copy()
     expected.update(
         {
+            "PYTHONPATH": os.getcwd() + os.pathsep + expected.get("PYTHONPATH", ""),
             "PREFECT__CLOUD__API": "https://api.prefect.io",
             "PREFECT__CLOUD__AUTH_TOKEN": "TEST_TOKEN",
             "PREFECT__CLOUD__AGENT__LABELS": str(DEFAULT_AGENT_LABELS),
diff --git a/tests/tasks/prefect/test_flow_run_rename.py b/tests/tasks/prefect/test_flow_run_rename.py
index 6801c0c7a2..d6dba9f2da 100644
--- a/tests/tasks/prefect/test_flow_run_rename.py
+++ b/tests/tasks/prefect/test_flow_run_rename.py
@@ -1,6 +1,7 @@
 import pytest
 from unittest.mock import MagicMock
 
+import prefect
 from prefect.tasks.prefect.flow_run_rename import RenameFlowRun
 
 
@@ -34,9 +35,29 @@ def test_flow_run_rename_task(monkeypatch):
     assert client.set_flow_run_name.call_args[0][1] == "a_new_name!"
 
 
+def test_flow_run_id_defaults_from_context(monkeypatch):
+    client = MagicMock()
+    client.set_flow_run_name = MagicMock(return_value=True)
+    monkeypatch.setattr(
+        "prefect.tasks.prefect.flow_run_rename.Client", MagicMock(return_value=client)
+    )
+
+    task = RenameFlowRun(flow_run_name="a_new_name!")
+
+    # Verify client called with arguments
+    with prefect.context(flow_run_id="id123"):
+        task.run()
+    assert client.set_flow_run_name.called
+    assert client.set_flow_run_name.call_args[0][0] == "id123"
+    assert client.set_flow_run_name.call_args[0][1] == "a_new_name!"
+
+
 def test_missing_flow_run_id():
     task = RenameFlowRun()
-    with pytest.raises(ValueError, match="Must provide a flow run ID."):
+    with pytest.raises(
+        ValueError,
+        match="`flow_run_id` must be explicitly provided or available in the context",
+    ):
         task.run(flow_run_name="a_new_name!")
 
 
diff --git a/tests/utilities/test_serialization.py b/tests/utilities/test_serialization.py
index e4313f9404..6a72ca3787 100644
--- a/tests/utilities/test_serialization.py
+++ b/tests/utilities/test_serialization.py
@@ -71,11 +71,15 @@ class TestJSONCompatibleField:
         assert serialized["j"] == value
 
     def test_validate_on_dump(self):
-        with pytest.raises(marshmallow.ValidationError):
+        with pytest.raises(
+            marshmallow.ValidationError, match="must be JSON compatible"
+        ):
             self.Schema().dump({"j": lambda: 1})
 
     def test_validate_on_load(self):
-        with pytest.raises(marshmallow.ValidationError):
+        with pytest.raises(
+            marshmallow.ValidationError, match="must be JSON compatible"
+        ):
             self.Schema().load({"j": lambda: 1})