Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions python-client/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
windmill-api/
.venv/
__pycache__/
*.pyc
*.pyo
*.pyd
50 changes: 50 additions & 0 deletions python-client/tests/wmill_client_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,56 @@ def test_download_upload_s3_file(self):
)
print(file_key)

@unittest.skip("skipping")
def test_remove_s3_file(self):
test_content = b"This file will be deleted"
s3_obj = S3Object(s3="test_file_to_delete.txt")

# 1. Upload a file
file_key = wmill.write_s3_file(s3_obj, test_content)
print(f"Uploaded file: {file_key}")

# 2. Verify the file exists by reading it back
content = wmill.load_s3_file(s3_obj)
self.assertEqual(content, test_content)
print(f"File verified: content matches ({len(content)} bytes)")

# 3. Remove the file
wmill.remove_s3_file(s3_obj)
print("File removed successfully")

# 4. Verify the file no longer exists (should raise an exception)
with self.assertRaises(Exception):
wmill.load_s3_file(s3_obj)
print("Verified file was removed (read failed as expected)")

@unittest.skip("skipping")
def test_remove_s3_file_with_resource_path(self):
test_content = b"This file will be deleted with resource path"
s3_obj = S3Object(s3="test_file_with_resource.txt")

# 1. Upload with explicit resource path
file_key = wmill.write_s3_file(
s3_obj,
test_content,
s3_resource_path=self._resource_path
)
print(f"Uploaded file: {file_key}")

# 2. Verify the file exists by reading it back
content = wmill.load_s3_file(s3_obj, s3_resource_path=self._resource_path)
self.assertEqual(content, test_content)
print(f"File verified: content matches ({len(content)} bytes)")

# 3. Remove with resource path
wmill.remove_s3_file(s3_obj, s3_resource_path=self._resource_path)
print("File removed successfully with resource path")

# 4. Verify the file no longer exists (should raise an exception)
with self.assertRaises(Exception):
wmill.load_s3_file(s3_obj, s3_resource_path=self._resource_path)
print("Verified file was removed (read failed as expected)")


if __name__ == "__main__":
unittest.main()
58 changes: 58 additions & 0 deletions python-client/wmill/wmill/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,43 @@ def write_s3_file(
raise Exception("Could not write file to S3") from e
return S3Object(s3=response["file_key"], storage=s3object.get("storage") if s3object else None)

def remove_s3_file(
self,
s3object: S3Object | str,
s3_resource_path: str | None = None,
) -> None:
"""
Remove a file from the workspace S3 bucket

'''python
from wmill import S3Object

s3_obj = S3Object(s3="/path/to/my_file.txt")
client.remove_s3_file(s3_obj)
'''
"""
s3object = parse_s3_object(s3object)

query_params = {}
if s3object is not None and s3object["s3"] != "":
query_params["file_key"] = s3object["s3"]
if s3_resource_path is not None and s3_resource_path != "":
query_params["s3_resource_path"] = s3_resource_path
if (
s3object is not None
and "storage" in s3object
and s3object["storage"] is not None
):
query_params["storage"] = s3object["storage"]

try:
self.delete(
f"/w/{self.workspace}/job_helpers/delete_s3_file",
params=query_params,
)
except Exception as e:
raise Exception("Could not delete file from S3") from e

def sign_s3_objects(self, s3_objects: list[S3Object | str]) -> list[S3Object]:
"""Sign S3 objects for use by anonymous users in public apps.

Expand Down Expand Up @@ -1679,6 +1716,27 @@ def write_s3_file(
)


@init_global_client
def remove_s3_file(
s3object: S3Object | str,
s3_resource_path: str | None = None,
) -> None:
"""
Remove a file from the workspace S3 bucket

'''python
from wmill import S3Object

s3_obj = S3Object(s3="/path/to/my_file.txt")
wmill.remove_s3_file(s3_obj)
'''
"""
return _client.remove_s3_file(
s3object,
s3_resource_path if s3_resource_path != "" else None,
)


@init_global_client
def sign_s3_objects(s3_objects: list[S3Object | str]) -> list[S3Object]:
"""
Expand Down