Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
This repository was archived by the owner on Mar 31, 2026. It is now read-only.

Commit 7bb8065

Browse filesBrowse files
authored
feat: support includeFoldersAsPrefixes (#1223)
* feat: support includeFoldersAsPrefixes * sys test * update sys test with cleanup
1 parent 3928aa0 commit 7bb8065
Copy full SHA for 7bb8065

5 files changed

+66Lines changed: 66 additions & 0 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎google/cloud/storage/bucket.py‎

Copy file name to clipboardExpand all lines: google/cloud/storage/bucket.py
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,7 @@ def list_blobs(
13061306
timeout=_DEFAULT_TIMEOUT,
13071307
retry=DEFAULT_RETRY,
13081308
match_glob=None,
1309+
include_folders_as_prefixes=None,
13091310
soft_deleted=None,
13101311
):
13111312
"""Return an iterator used to find blobs in the bucket.
@@ -1388,6 +1389,11 @@ def list_blobs(
13881389
The string value must be UTF-8 encoded. See:
13891390
https://cloud.google.com/storage/docs/json_api/v1/objects/list#list-object-glob
13901391
1392+
:type include_folders_as_prefixes: bool
1393+
(Optional) If true, includes Folders and Managed Folders in the set of
1394+
``prefixes`` returned by the query. Only applicable if ``delimiter`` is set to /.
1395+
See: https://cloud.google.com/storage/docs/managed-folders
1396+
13911397
:type soft_deleted: bool
13921398
:param soft_deleted:
13931399
(Optional) If true, only soft-deleted objects will be listed as distinct results in order of increasing
@@ -1415,6 +1421,7 @@ def list_blobs(
14151421
timeout=timeout,
14161422
retry=retry,
14171423
match_glob=match_glob,
1424+
include_folders_as_prefixes=include_folders_as_prefixes,
14181425
soft_deleted=soft_deleted,
14191426
)
14201427

Collapse file

‎google/cloud/storage/client.py‎

Copy file name to clipboardExpand all lines: google/cloud/storage/client.py
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1184,6 +1184,7 @@ def list_blobs(
11841184
timeout=_DEFAULT_TIMEOUT,
11851185
retry=DEFAULT_RETRY,
11861186
match_glob=None,
1187+
include_folders_as_prefixes=None,
11871188
soft_deleted=None,
11881189
):
11891190
"""Return an iterator used to find blobs in the bucket.
@@ -1283,6 +1284,11 @@ def list_blobs(
12831284
The string value must be UTF-8 encoded. See:
12841285
https://cloud.google.com/storage/docs/json_api/v1/objects/list#list-object-glob
12851286
1287+
include_folders_as_prefixes (bool):
1288+
(Optional) If true, includes Folders and Managed Folders in the set of
1289+
``prefixes`` returned by the query. Only applicable if ``delimiter`` is set to /.
1290+
See: https://cloud.google.com/storage/docs/managed-folders
1291+
12861292
soft_deleted (bool):
12871293
(Optional) If true, only soft-deleted objects will be listed as distinct results in order of increasing
12881294
generation number. This parameter can only be used successfully if the bucket has a soft delete policy.
@@ -1325,6 +1331,9 @@ def list_blobs(
13251331
if fields is not None:
13261332
extra_params["fields"] = fields
13271333

1334+
if include_folders_as_prefixes is not None:
1335+
extra_params["includeFoldersAsPrefixes"] = include_folders_as_prefixes
1336+
13281337
if soft_deleted is not None:
13291338
extra_params["softDeleted"] = soft_deleted
13301339

Collapse file

‎tests/system/test_bucket.py‎

Copy file name to clipboardExpand all lines: tests/system/test_bucket.py
+41Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,47 @@ def test_bucket_list_blobs_w_match_glob(
653653
assert [blob.name for blob in blobs] == expected_names
654654

655655

656+
def test_bucket_list_blobs_include_managed_folders(
657+
storage_client,
658+
buckets_to_delete,
659+
blobs_to_delete,
660+
hierarchy_filenames,
661+
):
662+
bucket_name = _helpers.unique_name("ubla-mf")
663+
bucket = storage_client.bucket(bucket_name)
664+
bucket.iam_configuration.uniform_bucket_level_access_enabled = True
665+
_helpers.retry_429_503(bucket.create)()
666+
buckets_to_delete.append(bucket)
667+
668+
payload = b"helloworld"
669+
for filename in hierarchy_filenames:
670+
blob = bucket.blob(filename)
671+
blob.upload_from_string(payload)
672+
blobs_to_delete.append(blob)
673+
674+
# Make API call to create a managed folder.
675+
# TODO: change to use storage control client once available.
676+
path = f"/b/{bucket_name}/managedFolders"
677+
properties = {"name": "managedfolder1"}
678+
storage_client._post_resource(path, properties)
679+
680+
expected_prefixes = set(["parent/"])
681+
blob_iter = bucket.list_blobs(delimiter="/")
682+
list(blob_iter)
683+
assert blob_iter.prefixes == expected_prefixes
684+
685+
# Test that managed folders are only included when IncludeFoldersAsPrefixes is set.
686+
expected_prefixes = set(["parent/", "managedfolder1/"])
687+
blob_iter = bucket.list_blobs(delimiter="/", include_folders_as_prefixes=True)
688+
list(blob_iter)
689+
assert blob_iter.prefixes == expected_prefixes
690+
691+
# Cleanup: API call to delete a managed folder.
692+
# TODO: change to use storage control client once available.
693+
path = f"/b/{bucket_name}/managedFolders/managedfolder1"
694+
storage_client._delete_resource(path)
695+
696+
656697
def test_bucket_update_retention_period(
657698
storage_client,
658699
buckets_to_delete,
Collapse file

‎tests/unit/test_bucket.py‎

Copy file name to clipboardExpand all lines: tests/unit/test_bucket.py
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,6 +1177,7 @@ def test_list_blobs_w_defaults(self):
11771177
expected_versions = None
11781178
expected_projection = "noAcl"
11791179
expected_fields = None
1180+
expected_include_folders_as_prefixes = None
11801181
soft_deleted = None
11811182
client.list_blobs.assert_called_once_with(
11821183
bucket,
@@ -1193,6 +1194,7 @@ def test_list_blobs_w_defaults(self):
11931194
timeout=self._get_default_timeout(),
11941195
retry=DEFAULT_RETRY,
11951196
match_glob=expected_match_glob,
1197+
include_folders_as_prefixes=expected_include_folders_as_prefixes,
11961198
soft_deleted=soft_deleted,
11971199
)
11981200

@@ -1206,6 +1208,7 @@ def test_list_blobs_w_explicit(self):
12061208
start_offset = "c"
12071209
end_offset = "g"
12081210
include_trailing_delimiter = True
1211+
include_folders_as_prefixes = True
12091212
versions = True
12101213
soft_deleted = True
12111214
projection = "full"
@@ -1231,6 +1234,7 @@ def test_list_blobs_w_explicit(self):
12311234
timeout=timeout,
12321235
retry=retry,
12331236
match_glob=match_glob,
1237+
include_folders_as_prefixes=include_folders_as_prefixes,
12341238
soft_deleted=soft_deleted,
12351239
)
12361240

@@ -1247,6 +1251,7 @@ def test_list_blobs_w_explicit(self):
12471251
expected_versions = versions
12481252
expected_projection = projection
12491253
expected_fields = fields
1254+
expected_include_folders_as_prefixes = include_folders_as_prefixes
12501255
expected_soft_deleted = soft_deleted
12511256
other_client.list_blobs.assert_called_once_with(
12521257
bucket,
@@ -1263,6 +1268,7 @@ def test_list_blobs_w_explicit(self):
12631268
timeout=timeout,
12641269
retry=retry,
12651270
match_glob=expected_match_glob,
1271+
include_folders_as_prefixes=expected_include_folders_as_prefixes,
12661272
soft_deleted=expected_soft_deleted,
12671273
)
12681274

Collapse file

‎tests/unit/test_client.py‎

Copy file name to clipboardExpand all lines: tests/unit/test_client.py
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2015,6 +2015,7 @@ def test_list_blobs_w_explicit_w_user_project(self):
20152015
start_offset = "c"
20162016
end_offset = "g"
20172017
include_trailing_delimiter = True
2018+
include_folders_as_prefixes = True
20182019
soft_deleted = False
20192020
versions = True
20202021
projection = "full"
@@ -2048,6 +2049,7 @@ def test_list_blobs_w_explicit_w_user_project(self):
20482049
timeout=timeout,
20492050
retry=retry,
20502051
match_glob=match_glob,
2052+
include_folders_as_prefixes=include_folders_as_prefixes,
20512053
soft_deleted=soft_deleted,
20522054
)
20532055

@@ -2070,6 +2072,7 @@ def test_list_blobs_w_explicit_w_user_project(self):
20702072
"versions": versions,
20712073
"fields": fields,
20722074
"userProject": user_project,
2075+
"includeFoldersAsPrefixes": include_folders_as_prefixes,
20732076
"softDeleted": soft_deleted,
20742077
}
20752078
expected_page_start = _blobs_page_start

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.