Skip to content

Commit c879225

Browse files
authored
COPDS-3042: Set file permissions explicitly with cacholote (#152)
* Set file permissions explicitly with cacholote * cleanup
1 parent 296f2dc commit c879225

File tree

4 files changed

+309
-0
lines changed

4 files changed

+309
-0
lines changed

cacholote/config.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class Settings(pydantic_settings.BaseSettings):
6363
"application/netcdf", "application/x-grib", "application/vnd+zarr"
6464
] = "application/netcdf"
6565
io_delete_original: bool = False
66+
io_chmod: Optional[int | str] = None
6667
raise_all_encoding_errors: bool = False
6768
expiration: Optional[datetime.datetime] = None
6869
tag: Optional[str] = None
@@ -82,6 +83,17 @@ def validate_create_engine_kwargs(
8283
create_engine_kwargs["poolclass"] = getattr(sa.pool, poolclass)
8384
return create_engine_kwargs
8485

86+
@pydantic.field_validator("io_chmod")
87+
def validate_io_chmod(
88+
cls: pydantic_settings.BaseSettings, io_chmod: Optional[int]
89+
) -> Optional[int]:
90+
if isinstance(io_chmod, str):
91+
try:
92+
return int(io_chmod, 8)
93+
except ValueError:
94+
pass
95+
return io_chmod
96+
8597
@pydantic.field_validator("expiration")
8698
def validate_expiration(
8799
cls: pydantic_settings.BaseSettings, expiration: datetime.datetime | None

cacholote/extra_encoders.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,9 @@ def _store_file_object(
374374
with fs_out.open(urlpath_out, "wb") as f_out:
375375
utils.copy_buffered_file(f_in, f_out)
376376

377+
if (mode := config.get().io_chmod) is not None:
378+
fs_out.chmod(urlpath_out, mode)
379+
377380
if io_delete_original and fs_in.exists(urlpath_in):
378381
with _logging_timer(
379382
"remove",

tests/test_50_io_encoder.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,3 +249,28 @@ def cached_in_place_open(path: str) -> io.FileIO:
249249
output = cached_in_place_open(str(tmpfile))
250250
assert isinstance(output, fsspec.implementations.local.LocalFileOpener)
251251
assert output.name == str(tmpfile)
252+
253+
254+
@pytest.mark.parametrize(
255+
"io_chmod,expected",
256+
[
257+
(None, 0o100644),
258+
(0o100600, 0o100600),
259+
("100600", 0o100600),
260+
("600", 0o100600),
261+
],
262+
)
263+
def test_chmod(
264+
tmp_path: pathlib.Path,
265+
io_chmod: int | None,
266+
expected: int,
267+
) -> None:
268+
fs, _ = utils.get_cache_files_fs_dirname()
269+
270+
# Cache file
271+
filename = tmp_path / "test.txt"
272+
fsspec.filesystem("file").pipe_file(filename, b"test")
273+
with config.set(raise_all_encoding_errors=True, io_chmod=io_chmod):
274+
cached_file = cached_open(filename)
275+
276+
assert fs.info(cached_file)["mode"] == expected

0 commit comments

Comments
 (0)