diff --git a/pyogrio/__init__.py b/pyogrio/__init__.py index 17db8339..9e871d10 100644 --- a/pyogrio/__init__.py +++ b/pyogrio/__init__.py @@ -23,6 +23,7 @@ read_bounds, read_info, set_gdal_config_options, + vsi_curl_clear_cache, vsi_listtree, vsi_rmtree, vsi_unlink, @@ -49,6 +50,7 @@ "read_dataframe", "read_info", "set_gdal_config_options", + "vsi_curl_clear_cache", "vsi_listtree", "vsi_rmtree", "vsi_unlink", diff --git a/pyogrio/_ogr.pxd b/pyogrio/_ogr.pxd index 24dfa817..9ca6a52b 100644 --- a/pyogrio/_ogr.pxd +++ b/pyogrio/_ogr.pxd @@ -63,6 +63,8 @@ cdef extern from "cpl_vsi.h" nogil: int VSIFCloseL(VSILFILE *fp) int VSIFFlushL(VSILFILE *fp) int VSIUnlink(const char *path) + void VSICurlPartialClearCache(const char *prefix) + void VSICurlClearCache() VSILFILE* VSIFileFromMemBuffer(const char *path, void *data, diff --git a/pyogrio/_vsi.pyx b/pyogrio/_vsi.pyx index 5cc7fb38..34188b60 100644 --- a/pyogrio/_vsi.pyx +++ b/pyogrio/_vsi.pyx @@ -286,3 +286,31 @@ def ogr_vsi_unlink(str path): errcode = VSIUnlink(path_c) if errcode != 0: raise OSError(f"Error removing '{path}': {errcode=}") + + +def ogr_vsi_curl_clear_all_cache(): + """Clean local cache associated with /vsicurl/. + + """ + + VSICurlClearCache() + + +def ogr_vsi_curl_clear_cache(str prefix): + """Clean local cache associated with /vsicurl/. + + Parameters + ---------- + prefix : str + Filename or prefix to clear associated cache. + + """ + cdef const char *prefix_c + + try: + prefix_b = prefix.encode("UTF-8") + except UnicodeDecodeError: + prefix_b = prefix + prefix_c = prefix_b + + VSICurlPartialClearCache(prefix_c) diff --git a/pyogrio/core.py b/pyogrio/core.py index 4e0fe3c1..7e7706e3 100644 --- a/pyogrio/core.py +++ b/pyogrio/core.py @@ -7,6 +7,7 @@ _mask_to_wkb, _preprocess_options_key_value, get_vsi_path_or_buffer, + vsi_path, ) with GDALEnv(): @@ -26,6 +27,8 @@ set_gdal_config_options as _set_gdal_config_options, ) from pyogrio._vsi import ( + ogr_vsi_curl_clear_all_cache, + ogr_vsi_curl_clear_cache, ogr_vsi_listtree, ogr_vsi_rmtree, ogr_vsi_unlink, @@ -385,3 +388,19 @@ def vsi_unlink(path: str | Path): path = path.as_posix() ogr_vsi_unlink(path) + + +def vsi_curl_clear_cache(prefix: str = ""): + """Clean local cache associated with /vsicurl/. + + Parameters + ---------- + prefix : str + Filename or prefix to clear associated cache. If not specified clear all cache. + + """ + if prefix == "": + ogr_vsi_curl_clear_all_cache() + else: + vsi_prefix = vsi_path(prefix) + ogr_vsi_curl_clear_cache(vsi_prefix) diff --git a/pyogrio/tests/test_core.py b/pyogrio/tests/test_core.py index 634cbc1c..fbce5afd 100644 --- a/pyogrio/tests/test_core.py +++ b/pyogrio/tests/test_core.py @@ -14,6 +14,7 @@ read_bounds, read_info, set_gdal_config_options, + vsi_curl_clear_cache, vsi_listtree, vsi_rmtree, vsi_unlink, @@ -700,3 +701,13 @@ def test_vsimem_unlink_error(naturalearth_lowres_vsimem): with pytest.raises(FileNotFoundError, match="Path does not exist"): vsi_unlink("/vsimem/non-existent.gpkg") + + +def test_vsi_curl_clear_cache_empty_default(): + # Validate call doesn't raise any error when no prefix is used + vsi_curl_clear_cache() + + +def test_vsi_curl_clear_cache(): + # Validate call doesn't raise any error when prefix/path is used + vsi_curl_clear_cache("http://example.com/prefix")