Skip to content

Commit 3a4a80a

Browse files
committed
Introduce is_real_gcs
1 parent efc4efc commit 3a4a80a

10 files changed

Lines changed: 38 additions & 40 deletions

gcsfs/extended_gcsfs.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,8 @@ async def _lookup_bucket_type(self, bucket):
196196
return self._storage_layout_cache[bucket]
197197
bucket_type = await self._get_bucket_type(bucket)
198198
# Dont cache UNKNOWN type by default.
199-
# Allow caching it in tests (via config) to avoid slow repeated failing lookups on emulator.
199+
# Caching can be enabled (via cache_unknown_buckets=True) to avoid repeated slow
200+
# lookups on emulators or when users lack permissions for the Storage Control API.
200201
if bucket_type == BucketType.UNKNOWN and not self._cache_unknown_buckets:
201202
return bucket_type
202203
self._storage_layout_cache[bucket] = bucket_type

gcsfs/tests/conftest.py

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
TEST_VERSIONED_BUCKET,
2626
TEST_ZONAL_BUCKET,
2727
)
28+
from gcsfs.tests.utils import is_real_gcs
2829

2930
files = {
3031
"test/accounts.1.json": (
@@ -79,7 +80,7 @@
7980
def _avoid_adc_timeout(monkeypatch):
8081
"""Avoid slow ADC lookups and Metadata Server requests in tests."""
8182
# Do not apply if tests are explicitly running against real GCS
82-
if os.environ.get("STORAGE_EMULATOR_HOST") == "https://storage.googleapis.com":
83+
if is_real_gcs():
8384
yield
8485
return
8586

@@ -103,7 +104,7 @@ def stop_docker(container):
103104
@pytest.fixture(scope="session")
104105
def docker_gcs():
105106
if "STORAGE_EMULATOR_HOST" in os.environ:
106-
if os.environ["STORAGE_EMULATOR_HOST"] != "https://storage.googleapis.com":
107+
if not is_real_gcs():
107108
params["token"] = "anon"
108109
# assume using real API or otherwise have a server already set up
109110
yield os.getenv("STORAGE_EMULATOR_HOST")
@@ -140,9 +141,8 @@ def gcs_factory(docker_gcs):
140141
def factory(**kwargs):
141142
GCSFileSystem.clear_instance_cache()
142143
fs = fsspec.filesystem("gcs", **params, **kwargs)
143-
# Enable caching of UNKNOWN bucket types if we are on emulator
144-
is_emulator = params.get("endpoint_url") != "https://storage.googleapis.com"
145-
if is_emulator and isinstance(fs, ExtendedGcsFileSystem):
144+
# Enable caching of UNKNOWN bucket types if not using real GCS
145+
if not is_real_gcs() and isinstance(fs, ExtendedGcsFileSystem):
146146
fs._cache_unknown_buckets = True
147147
return fs
148148

@@ -308,9 +308,6 @@ def final_cleanup(gcs_factory, buckets_to_delete):
308308
def gcs_versioned(gcs_factory, buckets_to_delete):
309309
gcs = gcs_factory()
310310
gcs.version_aware = True
311-
is_real_gcs = (
312-
os.environ.get("STORAGE_EMULATOR_HOST") == "https://storage.googleapis.com"
313-
)
314311
try: # ensure we're empty.
315312
# The versioned bucket might be created by `is_versioning_enabled`
316313
# in test_core_versioned.py. We must register it for cleanup only if
@@ -324,7 +321,7 @@ def gcs_versioned(gcs_factory, buckets_to_delete):
324321
buckets_to_delete.add(TEST_VERSIONED_BUCKET)
325322
except ImportError:
326323
pass # test_core_versioned is not being run
327-
if is_real_gcs:
324+
if is_real_gcs():
328325
cleanup_versioned_bucket(gcs, TEST_VERSIONED_BUCKET)
329326
else:
330327
# For emulators, we delete and recreate the bucket for a clean state
@@ -339,7 +336,7 @@ def gcs_versioned(gcs_factory, buckets_to_delete):
339336
finally:
340337
# Ensure the bucket is empty after the test.
341338
try:
342-
if is_real_gcs:
339+
if is_real_gcs():
343340
cleanup_versioned_bucket(gcs, TEST_VERSIONED_BUCKET)
344341
except Exception as e:
345342
logging.warning(
@@ -385,13 +382,9 @@ def cleanup_versioned_bucket(gcs, bucket_name, prefix=None):
385382

386383

387384
def _create_extended_gcsfs(gcs_factory, buckets_to_delete, populate_bucket, **kwargs):
388-
is_real_gcs = (
389-
os.environ.get("STORAGE_EMULATOR_HOST") == "https://storage.googleapis.com"
390-
)
391-
392385
extended_gcsfs = gcs_factory(**kwargs)
393386
# Only create/delete/populate the bucket if we are NOT using the real GCS endpoint.
394-
if not is_real_gcs:
387+
if not is_real_gcs():
395388
if not extended_gcsfs.exists(TEST_ZONAL_BUCKET):
396389
extended_gcsfs.mkdir(TEST_ZONAL_BUCKET)
397390
buckets_to_delete.add(TEST_ZONAL_BUCKET)
@@ -451,14 +444,14 @@ def gcs_hns(gcs_factory, buckets_to_delete):
451444
def zonal_write_mocks():
452445
"""A fixture for mocking Zonal bucket write functionality."""
453446

454-
if os.environ.get("STORAGE_EMULATOR_HOST") == "https://storage.googleapis.com":
447+
if is_real_gcs():
455448
yield None
456449
return
457450

458-
patch_target_get_bucket_type = (
451+
patch_target_lookup_bucket_type = (
459452
"gcsfs.extended_gcsfs.ExtendedGcsFileSystem._lookup_bucket_type"
460453
)
461-
patch_target_sync_get_bucket_type = (
454+
patch_target_sync_lookup_bucket_type = (
462455
"gcsfs.extended_gcsfs.ExtendedGcsFileSystem._sync_lookup_bucket_type"
463456
)
464457
patch_target_init_aaow = "gcsfs.zb_hns_utils.init_aaow"
@@ -490,12 +483,12 @@ async def finalize_side_effect():
490483

491484
with (
492485
mock.patch(
493-
patch_target_get_bucket_type,
486+
patch_target_lookup_bucket_type,
494487
new_callable=mock.AsyncMock,
495488
return_value=BucketType.ZONAL_HIERARCHICAL,
496489
),
497490
mock.patch(
498-
patch_target_sync_get_bucket_type,
491+
patch_target_sync_lookup_bucket_type,
499492
return_value=BucketType.ZONAL_HIERARCHICAL,
500493
),
501494
mock.patch(patch_target_gcsfs_info, mock_gcsfs_info),

gcsfs/tests/integration/test_async_gcsfs.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
from gcsfs.extended_gcsfs import ExtendedGcsFileSystem
2222
from gcsfs.tests.settings import TEST_HNS_BUCKET
23+
from gcsfs.tests.utils import is_real_gcs
2324

2425
REQUIRED_ENV_VAR = "GCSFS_EXPERIMENTAL_ZB_HNS_SUPPORT"
2526

@@ -34,7 +35,7 @@
3435
reason=f"Skipping tests: {REQUIRED_ENV_VAR} env variable is not set",
3536
),
3637
pytest.mark.skipif(
37-
os.environ.get("STORAGE_EMULATOR_HOST") != "https://storage.googleapis.com",
38+
not is_real_gcs(),
3839
reason="Skipping tests on emulator, requires real GCS.",
3940
),
4041
]

gcsfs/tests/integration/test_extended_hns.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
from gcsfs.extended_gcsfs import BucketType, ExtendedGcsFileSystem
2121
from gcsfs.tests.settings import TEST_HNS_BUCKET, TEST_PROJECT
22+
from gcsfs.tests.utils import is_real_gcs
2223

2324
should_run_hns = os.getenv("GCSFS_EXPERIMENTAL_ZB_HNS_SUPPORT", "false").lower() in (
2425
"true",
@@ -27,8 +28,7 @@
2728

2829
# Skip these tests if not running against a real GCS backend or if experimentation flag is not set.
2930
pytestmark = pytest.mark.skipif(
30-
os.environ.get("STORAGE_EMULATOR_HOST") != "https://storage.googleapis.com"
31-
or not should_run_hns,
31+
not is_real_gcs() or not should_run_hns,
3232
reason="This test class is for real GCS HNS buckets only and requires experimental flag.",
3333
)
3434

gcsfs/tests/test_core_versioned.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from gcsfs import GCSFileSystem
99
from gcsfs.tests.settings import TEST_VERSIONED_BUCKET
10+
from gcsfs.tests.utils import is_real_gcs
1011

1112
a = TEST_VERSIONED_BUCKET + "/tmp/test/a"
1213
b = TEST_VERSIONED_BUCKET + "/tmp/test/b"
@@ -22,7 +23,7 @@ def is_versioning_enabled():
2223
"""
2324
# Don't skip when using an emulator, as we create the versioned bucket ourselves.
2425
global _VERSIONED_BUCKET_CREATED_BY_TESTS
25-
if os.environ.get("STORAGE_EMULATOR_HOST") != "https://storage.googleapis.com":
26+
if not is_real_gcs():
2627
return True, ""
2728
try:
2829
gcs = GCSFileSystem(project=os.getenv("GCSFS_TEST_PROJECT", "project"))

gcsfs/tests/test_extended_gcsfs.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
)
3333
from gcsfs.tests.conftest import csv_files, files, text_files
3434
from gcsfs.tests.settings import TEST_BUCKET, TEST_ZONAL_BUCKET
35-
from gcsfs.tests.utils import tempdir, tmpfile
35+
from gcsfs.tests.utils import is_real_gcs, tempdir, tmpfile
3636
from gcsfs.zb_hns_utils import MRDPool
3737

3838
file = "test/accounts.1.json"
@@ -68,10 +68,7 @@ def gcs_bucket_mocks():
6868
@contextlib.contextmanager
6969
def _gcs_bucket_mocks_factory(file_data, bucket_type_val):
7070
"""Creates mocks for a given file content and bucket type."""
71-
is_real_gcs = (
72-
os.environ.get("STORAGE_EMULATOR_HOST") == "https://storage.googleapis.com"
73-
)
74-
if is_real_gcs:
71+
if is_real_gcs():
7572
yield None
7673
return
7774
patch_target_lookup_bucket_type = (
@@ -1125,18 +1122,17 @@ async def test_cp_file_not_implemented_error(
11251122
short_uuid = str(uuid.uuid4())[:8]
11261123
source_path = f"{source_bucket}/source_{short_uuid}"
11271124
dest_path = f"{dest_bucket}/dest_{short_uuid}"
1128-
is_real_gcs = os.getenv("STORAGE_EMULATOR_HOST") == "https://storage.googleapis.com"
11291125

11301126
# Source file needs to exist for last case when super method is called for standard buckets
1131-
if is_real_gcs:
1127+
if is_real_gcs():
11321128
await async_gcs._pipe_file(source_path, b"test data", finalize_on_close=True)
11331129

11341130
async def mock_is_zonal(bucket):
11351131
return bucket == TEST_ZONAL_BUCKET
11361132

11371133
is_zonal_patch_cm = (
11381134
mock.patch.object(async_gcs, "_is_zonal_bucket", side_effect=mock_is_zonal)
1139-
if not is_real_gcs
1135+
if not is_real_gcs()
11401136
else contextlib.nullcontext()
11411137
)
11421138

@@ -1151,7 +1147,7 @@ async def mock_is_zonal(bucket):
11511147
):
11521148
await async_gcs._cp_file(source_path, dest_path)
11531149
else: # Standard -> Standard
1154-
if is_real_gcs:
1150+
if is_real_gcs():
11551151
await async_gcs._cp_file(source_path, dest_path)
11561152
assert await async_gcs._cat(dest_path) == b"test data"
11571153
else:

gcsfs/tests/test_extended_gcsfs_unit.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from gcsfs.tests.conftest import csv_files, files
2222
from gcsfs.tests.settings import TEST_BUCKET, TEST_ZONAL_BUCKET
2323
from gcsfs.tests.test_extended_gcsfs import gcs_bucket_mocks # noqa: F401
24-
from gcsfs.tests.utils import tmpfile
24+
from gcsfs.tests.utils import is_real_gcs, tmpfile
2525

2626
file = "test/accounts.1.json"
2727
file_path = f"{TEST_ZONAL_BUCKET}/{file}"
@@ -46,7 +46,7 @@
4646
reason=f"Skipping tests: {REQUIRED_ENV_VAR} env variable is not set",
4747
),
4848
pytest.mark.skipif(
49-
os.environ.get("STORAGE_EMULATOR_HOST") == "https://storage.googleapis.com",
49+
is_real_gcs(),
5050
reason="Contains Unit tests using mocks, does not require testing on real GCS.",
5151
),
5252
]

gcsfs/tests/test_extended_hns_gcsfs.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from gcsfs.extended_gcsfs import BucketType, ExtendedGcsFileSystem
1919
from gcsfs.retry import DEFAULT_RETRY_CONFIG, HttpError
2020
from gcsfs.tests.settings import TEST_HNS_BUCKET
21+
from gcsfs.tests.utils import is_real_gcs
2122

2223
REQUIRED_ENV_VAR = "GCSFS_EXPERIMENTAL_ZB_HNS_SUPPORT"
2324

@@ -754,7 +755,7 @@ def test_hns_mkdir_nested_fails_if_create_parents_false(
754755
mocks["super_mkdir"].assert_not_called()
755756

756757
@pytest.mark.skipif(
757-
os.environ.get("STORAGE_EMULATOR_HOST") == "https://storage.googleapis.com",
758+
is_real_gcs(),
758759
reason="This test is only to check that create_folder is not called for non-HNS buckets.",
759760
)
760761
def test_mkdir_non_hns_bucket_falls_back(self, gcs_hns, gcs_hns_mocks):
@@ -1591,7 +1592,7 @@ def test_hns_rmdir_success(self, gcs_hns, gcs_hns_mocks):
15911592
mocks["super_rmdir"].assert_not_called()
15921593

15931594
@pytest.mark.skipif(
1594-
os.environ.get("STORAGE_EMULATOR_HOST") == "https://storage.googleapis.com",
1595+
is_real_gcs(),
15951596
reason="This test is only to check that delete_folder is not called in case of non-HNS buckets."
15961597
"In real GCS on non-HNS bucket there would be no empty directories to delete.",
15971598
)

gcsfs/tests/test_zonal_file.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
)
1010

1111
from gcsfs.tests.settings import TEST_ZONAL_BUCKET
12-
from gcsfs.tests.utils import tempdir, tmpfile
12+
from gcsfs.tests.utils import is_real_gcs, tempdir, tmpfile
1313
from gcsfs.zonal_file import ZonalFile
1414

1515
test_data = b"hello world"
@@ -354,7 +354,7 @@ def test_zonal_file_append_to_empty(extended_gcsfs, zonal_write_mocks, file_path
354354

355355

356356
@pytest.mark.skipif(
357-
os.environ.get("STORAGE_EMULATOR_HOST") != "https://storage.googleapis.com",
357+
not is_real_gcs(),
358358
reason="This test class is for real GCS only.",
359359
)
360360
class TestZonalFileRealGCS:

gcsfs/tests/utils.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,8 @@ def tmpfile(extension="", dir=None):
4040
else:
4141
with ignoring(OSError):
4242
os.remove(filename)
43+
44+
45+
def is_real_gcs():
46+
"""Checks if tests are explicitly running against real GCS."""
47+
return os.environ.get("STORAGE_EMULATOR_HOST") == "https://storage.googleapis.com"

0 commit comments

Comments
 (0)