google_cloud.py 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. import os
  2. from typing import List, Optional
  3. from google.api_core.exceptions import NotFound
  4. from google.cloud import storage
  5. from openhands.storage.files import FileStore
  6. class GoogleCloudFileStore(FileStore):
  7. def __init__(self, bucket_name: Optional[str] = None) -> None:
  8. """
  9. Create a new FileStore. If GOOGLE_APPLICATION_CREDENTIALS is defined in the
  10. environment it will be used for authentication. Otherwise access will be
  11. anonymous.
  12. """
  13. if bucket_name is None:
  14. bucket_name = os.environ['GOOGLE_CLOUD_BUCKET_NAME']
  15. self.storage_client = storage.Client()
  16. self.bucket = self.storage_client.bucket(bucket_name)
  17. def write(self, path: str, contents: str | bytes) -> None:
  18. blob = self.bucket.blob(path)
  19. with blob.open('w') as f:
  20. f.write(contents)
  21. def read(self, path: str) -> str:
  22. blob = self.bucket.blob(path)
  23. try:
  24. with blob.open('r') as f:
  25. return f.read()
  26. except NotFound as err:
  27. raise FileNotFoundError(err)
  28. def list(self, path: str) -> List[str]:
  29. if not path or path == '/':
  30. path = ''
  31. elif not path.endswith('/'):
  32. path += '/'
  33. # The delimiter logic screens out directories, so we can't use it. :(
  34. # For example, given a structure:
  35. # foo/bar/zap.txt
  36. # foo/bar/bang.txt
  37. # ping.txt
  38. # prefix=None, delimiter="/" yields ["ping.txt"] # :(
  39. # prefix="foo", delimiter="/" yields [] # :(
  40. blobs = set()
  41. prefix_len = len(path)
  42. for blob in self.bucket.list_blobs(prefix=path):
  43. name = blob.name
  44. if name == path:
  45. continue
  46. try:
  47. index = name.index('/', prefix_len + 1)
  48. if index != prefix_len:
  49. blobs.add(name[: index + 1])
  50. except ValueError:
  51. blobs.add(name)
  52. return list(blobs)
  53. def delete(self, path: str) -> None:
  54. blob = self.bucket.blob(path)
  55. blob.delete()