11"""Test that S3 client works when current directory is not writeable."""
22
3+ import importlib
34import os
45import tempfile
6+
57import pytest
68
79
8- def test_s3_client_readonly_cwd (mocker ):
9- """Test that S3 client doesn't require writable CWD (issue #854)."""
10- # Mock boto3 and dependencies before importing S3
10+ def _mock_s3_dependencies (mocker ):
1111 mock_boto3 = mocker .MagicMock ()
1212 mock_transfer_config = mocker .MagicMock ()
1313 mocker .patch .dict ("sys.modules" , {"boto3" : mock_boto3 })
@@ -16,33 +16,82 @@ def test_s3_client_readonly_cwd(mocker):
1616 {"boto3.s3.transfer" : mocker .MagicMock (TransferConfig = mock_transfer_config )},
1717 )
1818
19- from metaflow .plugins .datatools .s3 import S3
2019
21- # Create a temporary read-only directory
22- with tempfile .TemporaryDirectory () as tmpdir :
23- readonly_dir = os .path .join (tmpdir , "readonly" )
24- os .makedirs (readonly_dir , mode = 0o555 )
20+ def _load_s3_class ():
21+ """Reload configuration and S3 so environment changes affect the default."""
22+ metaflow_config = importlib .import_module ("metaflow.metaflow_config" )
23+ s3_module = importlib .import_module ("metaflow.plugins.datatools.s3.s3" )
24+ importlib .reload (metaflow_config )
25+ importlib .reload (s3_module )
26+ # Keep the package-level export in sync for tests that import S3 later.
27+ s3_package = importlib .import_module ("metaflow.plugins.datatools.s3" )
28+ s3_package .S3 = s3_module .S3
29+ return s3_module .S3
2530
26- # Change to read-only directory
27- original_cwd = os .getcwd ()
28- try :
29- os .chdir (readonly_dir )
3031
31- # This should not raise an exception about permissions
32- # The S3 client should use system temp dir, not CWD
33- s3_client = S3 ()
32+ def test_s3_client_readonly_cwd (mocker , monkeypatch ):
33+ """Test that S3 client doesn't require writable CWD (issue #854)."""
34+ _mock_s3_dependencies (mocker )
35+ original_tempdir = os .environ .get ("METAFLOW_TEMPDIR" )
36+
37+ try :
38+ with monkeypatch .context () as env :
39+ env .delenv ("METAFLOW_TEMPDIR" , raising = False )
40+ S3 = _load_s3_class ()
41+
42+ # Create a temporary read-only directory
43+ with tempfile .TemporaryDirectory () as tmpdir :
44+ readonly_dir = os .path .join (tmpdir , "readonly" )
45+ os .makedirs (readonly_dir , mode = 0o555 )
46+
47+ # Change to read-only directory
48+ original_cwd = os .getcwd ()
49+ try :
50+ os .chdir (readonly_dir )
3451
35- # Verify that tmpdir was created in system temp, not CWD
36- assert s3_client ._tmpdir is not None
37- # The temp dir should NOT be in the current (read-only) directory
38- assert not s3_client ._tmpdir .startswith (readonly_dir )
39- # The temp dir should be in the system temp directory
40- system_temp = tempfile .gettempdir ()
41- assert s3_client ._tmpdir .startswith (system_temp )
42-
43- # Clean up
44- s3_client .close ()
45- finally :
46- os .chdir (original_cwd )
47- # Make directory writable again so it can be deleted
48- os .chmod (readonly_dir , 0o755 )
52+ # This should not raise an exception about permissions
53+ # The S3 client should use system temp dir, not CWD
54+ s3_client = S3 ()
55+
56+ # Verify that tmpdir was created in system temp, not CWD
57+ assert s3_client ._tmpdir is not None
58+ # The temp dir should NOT be in the current (read-only) directory
59+ assert not s3_client ._tmpdir .startswith (readonly_dir )
60+ # The temp dir should be in the system temp directory
61+ system_temp = tempfile .gettempdir ()
62+ assert s3_client ._tmpdir .startswith (system_temp )
63+
64+ # Clean up
65+ s3_client .close ()
66+ finally :
67+ os .chdir (original_cwd )
68+ # Make directory writable again so it can be deleted
69+ os .chmod (readonly_dir , 0o755 )
70+ assert os .environ .get ("METAFLOW_TEMPDIR" ) == original_tempdir
71+ finally :
72+ # Restore the S3 module's import-time default after the isolated test.
73+ _load_s3_class ()
74+
75+
76+ def test_s3_client_respects_metaflow_tempdir (mocker , monkeypatch , tmp_path ):
77+ """Test that METAFLOW_TEMPDIR overrides the system temporary directory."""
78+ _mock_s3_dependencies (mocker )
79+ configured_dir = tmp_path / "configured"
80+ configured_dir .mkdir ()
81+ original_tempdir = os .environ .get ("METAFLOW_TEMPDIR" )
82+
83+ try :
84+ with monkeypatch .context () as env :
85+ env .setenv ("METAFLOW_TEMPDIR" , str (configured_dir ))
86+ S3 = _load_s3_class ()
87+
88+ s3_client = S3 ()
89+ try :
90+ assert s3_client ._tmproot == str (configured_dir )
91+ assert os .path .dirname (s3_client ._tmpdir ) == str (configured_dir )
92+ finally :
93+ s3_client .close ()
94+ assert os .environ .get ("METAFLOW_TEMPDIR" ) == original_tempdir
95+ finally :
96+ # Restore the S3 module's import-time default after the isolated test.
97+ _load_s3_class ()
0 commit comments