Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions planetary_computer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
)
from planetary_computer.settings import set_subscription_key
from planetary_computer._adlfs import get_adlfs_filesystem, get_container_client
from planetary_computer._obstore import get_obstore_store

from planetary_computer.version import __version__

__all__ = [
"get_adlfs_filesystem",
"get_container_client",
"get_obstore_store",
"set_subscription_key",
"sign_asset",
"sign_assets",
Expand Down
58 changes: 58 additions & 0 deletions planetary_computer/_obstore.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from planetary_computer.sas import get_token

if TYPE_CHECKING:
import sys

from obstore.store import (
AzureStore,
AzureConfig,
ClientConfig,
RetryConfig,
AzureSASToken,
)

if sys.version_info >= (3, 11):
from typing import Unpack
else:
from typing_extensions import Unpack


def get_obstore_store( # type: ignore[misc] # overlap with kwargs
account_name: str,
container_name: str,
*,
prefix: str | None = None,
config: AzureConfig | None = None,
client_options: ClientConfig | None = None,
retry_config: RetryConfig | None = None,
**kwargs: Unpack[AzureConfig], # type: ignore # noqa: PGH003 (container_name key overlaps with positional arg)
) -> AzureStore:
try:
import obstore
except ImportError as e:
raise ImportError(
"'planetary_computer.get_obstore_store' requires "
"the optional dependency 'obstore'."
) from e

def credential_provider() -> AzureSASToken:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To make this work with MPCPro, can you:

  1. Rename the inner function credential_provider to planetary_computer_credential_provider
  2. Add an optional callable argument to get_obstore_store that returns an AzureSASToken. When it's None, use the planetary_computer_credential_provider function.
  3. Write a function in _obstore.py that returns callable credential providers for MPCPro instances named planetary_computer_pro_credential_provider.

Such that an end-user can do this:

from planetary_computer import get_obstore_store, planetary_computer_pro_credential_provider
from planetary_computer.utils import parse_blob_url

stac_item : pystac.Item = ... # results of querying a geocatalog 
account, container = parse_blob_url(stac_item.assets["asset"].href)
store = get_obstore_store(account, container, credential_provider=planetary_computer_pro_credential_provider("https://geocatalog.azure.com")

Maybe the call to parse_blob_url is not necessary and get_obstore_store can just accept a string argument and parse it inside? So that callers can just pass the asset href to get_obstore_store and not have to think about parsing account names and containers.

Copy link
Author

@kylebarron kylebarron May 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about this a bit more:

While we can have a credential_provider parameter that matches obstore's own API, it seems a little verbose, and I think it's exposing concepts that are unnecessary here because we know users will always be using a credential provider. It's just a question of which one: Open PC or PC Pro, and that's all we need to have in our signature here. And I think we can figure out a simpler user API here in the context of users who will definitely be using PC.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the call to parse_blob_url is not necessary and get_obstore_store can just accept a string argument and parse it inside? So that callers can just pass the asset href to get_obstore_store and not have to think about parsing account names and containers.

This is why in obstore we have both AzureStore.__init__ and AzureStore.from_url so that we can customize the signature for the two different entry points.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • rename to get_obstore
  • Rename to credential provider to default to optional argument with default of open pc provider
  • One day we can swap in the mpc pro credential provider there.

Copy link
Author

@kylebarron kylebarron Jun 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ghidalgo3 We can't have a default argument as an instance of the credential provider because the credential provider needs to know the account_name and container_name. So for now it defaults to None and that defaults to the open PC credential provider.

token = get_token(account_name, container_name)
return {
"sas_token": token.token,
"expires_at": token.expiry,
}

return obstore.store.AzureStore(
account_name=account_name,
container_name=container_name,
prefix=prefix,
config=config,
client_options=client_options,
retry_config=retry_config,
credential_provider=credential_provider,
**kwargs, # type: ignore # (container_name key overlaps with positional arg)
)
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dev = [
"pytest",
"responses",
]
obstore = ["obstore"]

[project.scripts]
planetarycomputer = "planetary_computer.scripts.cli:app"
Expand All @@ -42,4 +43,4 @@ planetarycomputer = "planetary_computer.scripts.cli:app"
include-package-data = false

[tool.setuptools.dynamic]
version = {attr = "planetary_computer.version.__version__"}
version = {attr = "planetary_computer.version.__version__"}
Loading