Extend cloud support#
shapelib’s cloud layer is registry-based: providers, writers and remote handlers register themselves with decorators, so adding a new cloud (e.g. Google Cloud Storage or Azure Blob) means writing one class — no core changes.
Current state:
Provider |
Protocol |
Status |
|---|---|---|
AWS S3 |
|
supported |
HTTPS (presigned URLs) |
|
supported |
Google Cloud Storage |
|
not yet |
Azure Blob Storage |
|
not yet |
Step 1 — write the provider#
Create src/shapelib/core/cloud/providers/gcs_provider.py implementing
supports / upload / download / file_exists, decorated
with @register_cloud_provider:
from pathlib import Path
from shapelib.core.cloud.cloud_path import CloudPath
from shapelib.factories.cloud_registry import register_cloud_provider
@register_cloud_provider # auto-registers on import
class GcsProvider:
"""Google Cloud Storage provider."""
def supports(self, cloud_path: CloudPath) -> bool:
return cloud_path.scheme == "gs"
def upload(self, source: Path, cloud_path: CloudPath) -> None:
from google.cloud import storage
client = storage.Client()
bucket = client.bucket(cloud_path.host)
bucket.blob(cloud_path.path.lstrip("/")).upload_from_filename(str(source))
def download(self, cloud_path: CloudPath, destination: Path) -> Path:
from google.cloud import storage
client = storage.Client()
bucket = client.bucket(cloud_path.host)
destination.parent.mkdir(parents=True, exist_ok=True)
bucket.blob(cloud_path.path.lstrip("/")).download_to_filename(str(destination))
return destination
def file_exists(self, cloud_path: CloudPath) -> bool:
from google.cloud import storage
client = storage.Client()
bucket = client.bucket(cloud_path.host)
return bucket.blob(cloud_path.path.lstrip("/")).exists()
Step 2 — trigger the registration#
The decorator runs on import, so import the module at the end of
src/shapelib/factories/geo_cloud_factory.py:
import shapelib.core.cloud.providers.gcs_provider # noqa: F401, E402
Step 3 (optional) — streaming support#
For formats that stream (.fgb, .geojson, .gpkg), teach
CloudPath.vsicurl_path (src/shapelib/core/cloud/cloud_path.py)
how to build the GDAL virtual path for the new scheme — e.g.
/vsigs/<bucket>/<key> for GCS.
Step 4 — dependency and tests#
Add the SDK to the project dependencies (google-cloud-storage,
azure-storage-blob, …) and cover the provider in
tests/test_cloud_support.py, skipping when credentials are absent:
@pytest.mark.skipif(not has_gcs_credentials(), reason="GCS credentials not available")
class TestGCSIntegration:
def test_read_flatgeobuf_gcs(self):
result = shapelib.read_file("gs://my-bucket/data.fgb")
Where things live#
File |
Purpose |
|---|---|
|
The decorators: |
|
Cloud provider implementations |
|
Format-specific cloud writers |
|
URL parsing and protocol detection |