Repository: PrefectHQ/prefect

Issue:
Prefect Python client does not support flow deletion
### Bug summary

The Prefect REST API supports flow removal via `DELETE` HTTP method on the `/api/flows/{flow_id}` endpoint. However, the Python Prefect client lacks this functionality, making it impossible to remove the flow without patching the client or calling the Prefect REST API directly.

### Version info

```Text
Version:             3.1.6.dev7+0.g8fd92fe5e6.dirty
API version:         0.8.4
Python version:      3.11.11
Git commit:          8fd92fe5
Built:               Sun, Dec 8, 2024 8:00 PM
OS/Arch:             darwin/arm64
Profile:             default
Server type:         server
Pydantic version:    2.10.3
```


### Additional context

The missing functionality affects both Prefect 2 and 3.

Discussion:
zzstoatzz: thank you @paulusaptus ! I am going to try and understand quickly why the test suite did not run here 🤨 
zzstoatzz: oh! its because the base changed
![image](https://github.com/user-attachments/assets/84ce7609-2a82-45d2-8bc5-67954eb1fc02)

I'll push an inconsequential commit to kick it
paulusaptus: @zzstoatzz - sorry for that, I messed the target branch while creating the PR
zzstoatzz: no worries @paulusaptus ! if you could just `pre-commit install` and `pre-commit run --all-files` - I think we should be good to go here

Patch:
diff --git a/src/prefect/client/orchestration.py b/src/prefect/client/orchestration.py
index dfbc3c119b..4a5a06790a 100644
--- a/src/prefect/client/orchestration.py
+++ b/src/prefect/client/orchestration.py
@@ -499,6 +499,23 @@ class PrefectClient:
         response = await self._client.get(f"/flows/{flow_id}")
         return Flow.parse_obj(response.json())
 
+    async def delete_flow(self, flow_id: UUID) -> None:
+        """
+        Delete a flow by UUID.
+        Args:
+            flow_id: ID of the flow to be deleted
+        Raises:
+            prefect.exceptions.ObjectNotFound: If request returns 404
+            httpx.RequestError: If requests fails
+        """
+        try:
+            await self._client.delete(f"/flows/{flow_id}")
+        except httpx.HTTPStatusError as e:
+            if e.response.status_code == status.HTTP_404_NOT_FOUND:
+                raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
+            else:
+                raise
+
     async def read_flows(
         self,
         *,


Test patch:
diff --git a/tests/client/test_prefect_client.py b/tests/client/test_prefect_client.py
index fce86c1a80..b1da96987c 100644
--- a/tests/client/test_prefect_client.py
+++ b/tests/client/test_prefect_client.py
@@ -568,6 +568,19 @@ async def test_create_then_read_flow(prefect_client):
     assert lookup.name == foo.name
 
 
+async def test_create_then_delete_flow(prefect_client):
+    @flow
+    def foo():
+        pass
+
+    flow_id = await prefect_client.create_flow(foo)
+    assert isinstance(flow_id, UUID)
+
+    await prefect_client.delete_flow(flow_id)
+    with pytest.raises(prefect.exceptions.PrefectHTTPStatusError, match="404"):
+        await prefect_client.read_flow(flow_id)
+
+
 async def test_create_then_read_deployment(
     prefect_client, infrastructure_document_id, storage_document_id
 ):