diff --git a/rtree/index.py b/rtree/index.py index f063eff4..a81629aa 100644 --- a/rtree/index.py +++ b/rtree/index.py @@ -230,12 +230,13 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: # check we can read the file f = str(basename) + "." + self.properties.idx_extension p = os.path.abspath(f) + d = os.path.dirname(p) + file_exists = os.path.exists(f) - # assume if the file exists, we're not going to overwrite it + # assume we're not going to overwrite an existing file # unless the user explicitly set the property to do so - if os.path.exists(p): - self.properties.overwrite = bool(kwargs.get("overwrite", False)) - + self.properties.overwrite = bool(kwargs.get("overwrite", False)) + if file_exists: # assume we're fetching the first index_id. If the user # set it, we'll fetch that one. if not self.properties.overwrite: @@ -244,8 +245,11 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: except RTreeError: self.properties.index_id = 1 - d = os.path.dirname(p) - if not os.access(d, os.W_OK): + writing_required = (not file_exists) or ( + file_exists and self.properties.overwrite + ) + writeable = os.access(f, os.W_OK) if file_exists else os.access(d, os.W_OK) + if writing_required and not writeable: message = "Unable to open file '%s' for index storage" % f raise OSError(message) elif storage: diff --git a/tests/test_index.py b/tests/test_index.py index 6590b3e9..5099e69c 100644 --- a/tests/test_index.py +++ b/tests/test_index.py @@ -1,7 +1,10 @@ from __future__ import annotations import ctypes +import os +import pathlib import pickle +import stat import tempfile import unittest from typing import Dict, Iterator, Tuple @@ -607,6 +610,35 @@ def test_overwrite(self) -> None: idx = index.Index(tname, overwrite=True) assert isinstance(idx, index.Index) + def _make_read_only_index(self) -> str: + """Create a read-only index & return the path without extension""" + dirname = pathlib.Path(tempfile.mkdtemp()) + file_path_no_extension = dirname / "index" + idx_filename = file_path_no_extension.with_suffix(".idx") + + idx = index.Index(str(file_path_no_extension)) + idx.add(1, (2, 2)) + del idx + + # Now that the index is written, make it read-only + os.chmod(idx_filename, stat.S_IREAD) + assert not os.access(idx_filename, os.W_OK) + + return str(file_path_no_extension) + + def test_overwrite_uneditable_throws(self) -> None: + """Existing index with overwrite should throw if we can't edit the file""" + file_path_no_extension = self._make_read_only_index() + + with pytest.raises(OSError): + index.Index(file_path_no_extension, overwrite=True) + + def test_read_only_ok_if_overwrite_false(self) -> None: + file_path_no_extension = self._make_read_only_index() + + idx = index.Index(file_path_no_extension, overwrite=False) + assert isinstance(idx, index.Index) + class IndexNearest(IndexTestCase): def test_nearest_basic(self) -> None: