Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support pickling & always manage store prefix #185

Merged
merged 21 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from 13 commits
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
7 changes: 5 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions docs/api/store/middleware.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# Middleware

Wrappers around other `ObjectStore` instances to provide monitoring or other modifications.

::: obstore.store.PrefixStore
4 changes: 0 additions & 4 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ Classes to construct a store are exported from the `obstore.store` submodule:
- [`LocalStore`][obstore.store.LocalStore]: Local filesystem storage providing the same object store interface.
- [`MemoryStore`][obstore.store.MemoryStore]: A fully in-memory implementation of ObjectStore.

Additionally, some middlewares exist:

- [`PrefixStore`][obstore.store.PrefixStore]: Store wrapper that applies a constant prefix to all paths handled by the store.

Each store concept has a variety of constructors, and a host of configuration options.

**Example:**
Expand Down
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ nav:
- api/store/local.md
- api/store/memory.md
- api/store/config.md
- api/store/middleware.md
# - api/store/middleware.md
- api/copy.md
- api/delete.md
- api/get.md
Expand Down
5 changes: 1 addition & 4 deletions obstore/python/obstore/store/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ from ._client import ClientConfig as ClientConfig
from ._gcs import GCSConfig as GCSConfig
from ._gcs import GCSStore as GCSStore
from ._http import HTTPStore as HTTPStore
from ._prefix import PrefixStore as PrefixStore
from ._retry import BackoffConfig as BackoffConfig
from ._retry import RetryConfig as RetryConfig

Expand Down Expand Up @@ -73,7 +72,5 @@ class MemoryStore:
def __init__(self) -> None: ...
def __repr__(self) -> str: ...

ObjectStore = (
AzureStore | GCSStore | HTTPStore | S3Store | LocalStore | MemoryStore | PrefixStore
)
ObjectStore = AzureStore | GCSStore | HTTPStore | S3Store | LocalStore | MemoryStore
"""All supported ObjectStore implementations."""
12 changes: 6 additions & 6 deletions obstore/python/obstore/store/_aws.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,7 @@ class S3Store:
self,
bucket: str | None = None,
*,
prefix: str | None = None,
kylebarron marked this conversation as resolved.
Show resolved Hide resolved
config: S3Config | None = None,
client_options: ClientConfig | None = None,
retry_config: RetryConfig | None = None,
Expand All @@ -532,6 +533,7 @@ class S3Store:
bucket: The AWS bucket to use.

Keyword Args:
prefix: A prefix within the bucket to use for all operations.
config: AWS Configuration. Values in this config will override values inferred from the environment. Defaults to None.
client_options: HTTP Client options. Defaults to None.
retry_config: Retry configuration. Defaults to None.
Expand All @@ -544,8 +546,9 @@ class S3Store:
def from_session(
cls,
session: boto3.session.Session | botocore.session.Session,
bucket: str,
bucket: str | None = None,
*,
prefix: str | None = None,
kylebarron marked this conversation as resolved.
Show resolved Hide resolved
config: S3Config | None = None,
client_options: ClientConfig | None = None,
retry_config: RetryConfig | None = None,
Expand Down Expand Up @@ -574,6 +577,7 @@ class S3Store:
bucket: The AWS bucket to use.

Keyword Args:
prefix: A prefix within the bucket to use for all operations.
config: AWS Configuration. Values in this config will override values inferred from the session. Defaults to None.
client_options: HTTP Client options. Defaults to None.
retry_config: Retry configuration. Defaults to None.
Expand Down Expand Up @@ -601,11 +605,6 @@ class S3Store:
- `https://<bucket>.s3.<region>.amazonaws.com`
- `https://ACCOUNT_ID.r2.cloudflarestorage.com/bucket`

!!! note
Note that `from_url` will not use any additional parts of the path as a
bucket prefix. It will only extract the bucket, region, and endpoint. If you
wish to use a path prefix, consider wrapping this with `PrefixStore`.

Args:
url: well-known storage URL.

Expand All @@ -619,4 +618,5 @@ class S3Store:
S3Store
"""

def __getnewargs_ex__(self): ...
def __repr__(self) -> str: ...
7 changes: 1 addition & 6 deletions obstore/python/obstore/store/_azure.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -328,12 +328,6 @@ class AzureStore:
- `https://<account>.blob.fabric.microsoft.com`
- `https://<account>.blob.fabric.microsoft.com/<container>`

!!! note
Note that `from_url` will not use any additional parts of the path as a
bucket prefix. It will only extract the container name, account name, and
whether it's a fabric endpoint. If you wish to use a path prefix, consider
wrapping this with `PrefixStore`.

Args:
url: well-known storage URL.

Expand All @@ -346,4 +340,5 @@ class AzureStore:
AzureStore
"""

def __getnewargs_ex__(self): ...
def __repr__(self) -> str: ...
6 changes: 1 addition & 5 deletions obstore/python/obstore/store/_gcs.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,6 @@ class GCSStore:

- `gs://<bucket>/<path>`

!!! note
Note that `from_url` will not use any additional parts of the path as a
bucket prefix. It will only extract the bucket name. If you wish to use a
path prefix, consider wrapping this with `PrefixStore`.

Args:
url: well-known storage URL.

Expand All @@ -137,4 +132,5 @@ class GCSStore:
GCSStore
"""

def __getnewargs_ex__(self): ...
def __repr__(self) -> str: ...
42 changes: 0 additions & 42 deletions obstore/python/obstore/store/_prefix.pyi

This file was deleted.

36 changes: 23 additions & 13 deletions obstore/src/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ use pyo3::prelude::*;
use pyo3::pybacked::PyBackedStr;
use pyo3::types::PyString;
use pyo3_object_store::{
PyAzureStore, PyGCSStore, PyObjectStoreError, PyObjectStoreResult, PyS3Store,
MaybePrefixedStore, PyAzureStore, PyGCSStore, PyObjectStoreError, PyObjectStoreResult,
PyS3Store,
};
use url::Url;

Expand All @@ -24,19 +25,19 @@ use crate::runtime::get_runtime;

#[derive(Debug)]
pub(crate) enum SignCapableStore {
S3(Arc<AmazonS3>),
Gcs(Arc<GoogleCloudStorage>),
Azure(Arc<MicrosoftAzure>),
S3(Arc<MaybePrefixedStore<AmazonS3>>),
Gcs(Arc<MaybePrefixedStore<GoogleCloudStorage>>),
Azure(Arc<MaybePrefixedStore<MicrosoftAzure>>),
}

impl<'py> FromPyObject<'py> for SignCapableStore {
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
if let Ok(store) = ob.downcast::<PyS3Store>() {
Ok(Self::S3(store.borrow().as_ref().clone()))
Ok(Self::S3(store.get().as_ref().clone()))
} else if let Ok(store) = ob.downcast::<PyGCSStore>() {
Ok(Self::Gcs(store.borrow().as_ref().clone()))
Ok(Self::Gcs(store.get().as_ref().clone()))
} else if let Ok(store) = ob.downcast::<PyAzureStore>() {
Ok(Self::Azure(store.borrow().as_ref().clone()))
Ok(Self::Azure(store.get().as_ref().clone()))
} else {
let py = ob.py();
// Check for object-store instance from other library
Expand Down Expand Up @@ -78,9 +79,9 @@ impl Signer for SignCapableStore {
Self: 'async_trait,
{
match self {
Self::S3(inner) => inner.signed_url(method, path, expires_in),
Self::Gcs(inner) => inner.signed_url(method, path, expires_in),
Self::Azure(inner) => inner.signed_url(method, path, expires_in),
Self::S3(inner) => inner.as_ref().inner().signed_url(method, path, expires_in),
Self::Gcs(inner) => inner.as_ref().inner().signed_url(method, path, expires_in),
Self::Azure(inner) => inner.as_ref().inner().signed_url(method, path, expires_in),
}
}

Expand All @@ -96,9 +97,18 @@ impl Signer for SignCapableStore {
Self: 'async_trait,
{
match self {
Self::S3(inner) => inner.signed_urls(method, paths, expires_in),
Self::Gcs(inner) => inner.signed_urls(method, paths, expires_in),
Self::Azure(inner) => inner.signed_urls(method, paths, expires_in),
Self::S3(inner) => inner
.as_ref()
.inner()
.signed_urls(method, paths, expires_in),
Self::Gcs(inner) => inner
.as_ref()
.inner()
.signed_urls(method, paths, expires_in),
Self::Azure(inner) => inner
.as_ref()
.inner()
.signed_urls(method, paths, expires_in),
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions pyo3-object_store/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ rust-version = "1.75"
include = ["src", "type-hints", "README.md", "LICENSE"]

[dependencies]
async-trait = "0.1.85"
bytes = "1"
futures = "0.3"
# This is already an object_store dependency
humantime = "2.1"
Expand All @@ -25,6 +27,7 @@ object_store = { version = "0.11.2", features = [
] }
pyo3 = { version = "0.23", features = ["chrono", "indexmap"] }
pyo3-async-runtimes = { version = "0.23", features = ["tokio-runtime"] }
serde = "1"
thiserror = "1"
url = "2"

Expand Down
5 changes: 1 addition & 4 deletions pyo3-object_store/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ use pyo3::intern;
use pyo3::prelude::*;

use crate::error::*;
use crate::{
PyAzureStore, PyGCSStore, PyHttpStore, PyLocalStore, PyMemoryStore, PyPrefixStore, PyS3Store,
};
use crate::{PyAzureStore, PyGCSStore, PyHttpStore, PyLocalStore, PyMemoryStore, PyS3Store};

/// Export the default Python API as a submodule named `store` within the given parent module
///
Expand Down Expand Up @@ -52,7 +50,6 @@ pub fn register_store_module(
child_module.add_class::<PyLocalStore>()?;
child_module.add_class::<PyMemoryStore>()?;
child_module.add_class::<PyS3Store>()?;
child_module.add_class::<PyPrefixStore>()?;

parent_module.add_submodule(&child_module)?;

Expand Down
Loading