Skip to content

Commit

Permalink
Combine config and kwargs (#180)
Browse files Browse the repository at this point in the history
* Combine config and kwargs

* Use `ObstoreError`
  • Loading branch information
kylebarron authored Jan 30, 2025
1 parent aab07e7 commit 5082d96
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 61 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Simple, fast integration with object storage services like Amazon S3, Google Clo
- **Streaming downloads** with configurable chunking.
- **Streaming uploads** from async or sync iterators.
- **Streaming list**, with no need to paginate.
- Automatically uses [**multipart uploads**](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html) for large file objects.
- Automatic [**multipart uploads**](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html) for large file objects.
- Support for **conditional put** ("put if not exists"), as well as custom tags and attributes.
- Optionally return list results in [Apache Arrow](https://arrow.apache.org/) format, which is faster and more memory-efficient than materializing Python `dict`s.
- File-like object API and [fsspec](https://github.com/fsspec/filesystem_spec) integration.
Expand Down
60 changes: 37 additions & 23 deletions pyo3-object_store/src/aws.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use pyo3::types::PyType;

use crate::client::PyClientOptions;
use crate::config::PyConfigValue;
use crate::error::{PyObjectStoreError, PyObjectStoreResult};
use crate::error::{ObstoreError, PyObjectStoreError, PyObjectStoreResult};
use crate::retry::PyRetryConfig;

/// A Python-facing wrapper around an [`AmazonS3`].
Expand Down Expand Up @@ -43,11 +43,8 @@ impl PyS3Store {
kwargs: Option<PyAmazonS3Config>,
) -> PyObjectStoreResult<Self> {
let mut builder = AmazonS3Builder::new().with_bucket_name(bucket);
if let Some(config) = config {
builder = config.apply_config(builder);
}
if let Some(kwargs) = kwargs {
builder = kwargs.apply_config(builder);
if let Some(config_kwargs) = combine_config_kwargs(config, kwargs)? {
builder = config_kwargs.apply_config(builder);
}
if let Some(client_options) = client_options {
builder = builder.with_client_options(client_options.into())
Expand All @@ -73,11 +70,8 @@ impl PyS3Store {
if let Some(bucket) = bucket {
builder = builder.with_bucket_name(bucket);
}
if let Some(config) = config {
builder = config.apply_config(builder);
}
if let Some(kwargs) = kwargs {
builder = kwargs.apply_config(builder);
if let Some(config_kwargs) = combine_config_kwargs(config, kwargs)? {
builder = config_kwargs.apply_config(builder);
}
if let Some(client_options) = client_options {
builder = builder.with_client_options(client_options.into())
Expand Down Expand Up @@ -135,11 +129,8 @@ impl PyS3Store {
if let Some(token) = token {
builder = builder.with_token(token);
}
if let Some(config) = config {
builder = config.apply_config(builder);
}
if let Some(kwargs) = kwargs {
builder = kwargs.apply_config(builder);
if let Some(config_kwargs) = combine_config_kwargs(config, kwargs)? {
builder = config_kwargs.apply_config(builder);
}
if let Some(client_options) = client_options {
builder = builder.with_client_options(client_options.into())
Expand All @@ -162,11 +153,8 @@ impl PyS3Store {
kwargs: Option<PyAmazonS3Config>,
) -> PyObjectStoreResult<Self> {
let mut builder = AmazonS3Builder::from_env().with_url(url);
if let Some(config) = config {
builder = config.apply_config(builder);
}
if let Some(kwargs) = kwargs {
builder = kwargs.apply_config(builder);
if let Some(config_kwargs) = combine_config_kwargs(config, kwargs)? {
builder = config_kwargs.apply_config(builder);
}
if let Some(client_options) = client_options {
builder = builder.with_client_options(client_options.into())
Expand All @@ -183,7 +171,7 @@ impl PyS3Store {
}
}

#[derive(Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PyAmazonS3ConfigKey(AmazonS3ConfigKey);

impl<'py> FromPyObject<'py> for PyAmazonS3ConfigKey {
Expand All @@ -194,7 +182,7 @@ impl<'py> FromPyObject<'py> for PyAmazonS3ConfigKey {
}
}

#[derive(Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PyAmazonS3Config(HashMap<PyAmazonS3ConfigKey, PyConfigValue>);

impl<'py> FromPyObject<'py> for PyAmazonS3Config {
Expand All @@ -210,4 +198,30 @@ impl PyAmazonS3Config {
}
builder
}

fn merge(mut self, other: PyAmazonS3Config) -> PyObjectStoreResult<PyAmazonS3Config> {
for (k, v) in other.0.into_iter() {
let old_value = self.0.insert(k.clone(), v);
if old_value.is_some() {
return Err(ObstoreError::new_err(format!(
"Duplicate key {} between config and kwargs",
k.0.as_ref()
))
.into());
}
}

Ok(self)
}
}

fn combine_config_kwargs(
config: Option<PyAmazonS3Config>,
kwargs: Option<PyAmazonS3Config>,
) -> PyObjectStoreResult<Option<PyAmazonS3Config>> {
match (config, kwargs) {
(None, None) => Ok(None),
(Some(x), None) | (None, Some(x)) => Ok(Some(x)),
(Some(config), Some(kwargs)) => Ok(Some(config.merge(kwargs)?)),
}
}
53 changes: 35 additions & 18 deletions pyo3-object_store/src/azure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use pyo3::types::PyType;

use crate::client::PyClientOptions;
use crate::config::PyConfigValue;
use crate::error::{PyObjectStoreError, PyObjectStoreResult};
use crate::error::{ObstoreError, PyObjectStoreError, PyObjectStoreResult};
use crate::retry::PyRetryConfig;

/// A Python-facing wrapper around a [`MicrosoftAzure`].
Expand Down Expand Up @@ -42,11 +42,8 @@ impl PyAzureStore {
kwargs: Option<PyAzureConfig>,
) -> PyObjectStoreResult<Self> {
let mut builder = MicrosoftAzureBuilder::new().with_container_name(container);
if let Some(config) = config {
builder = config.apply_config(builder);
}
if let Some(kwargs) = kwargs {
builder = kwargs.apply_config(builder);
if let Some(config_kwargs) = combine_config_kwargs(config, kwargs)? {
builder = config_kwargs.apply_config(builder);
}
if let Some(client_options) = client_options {
builder = builder.with_client_options(client_options.into())
Expand All @@ -69,11 +66,8 @@ impl PyAzureStore {
kwargs: Option<PyAzureConfig>,
) -> PyObjectStoreResult<Self> {
let mut builder = MicrosoftAzureBuilder::from_env().with_container_name(container);
if let Some(config) = config {
builder = config.apply_config(builder);
}
if let Some(kwargs) = kwargs {
builder = kwargs.apply_config(builder);
if let Some(config_kwargs) = combine_config_kwargs(config, kwargs)? {
builder = config_kwargs.apply_config(builder);
}
if let Some(client_options) = client_options {
builder = builder.with_client_options(client_options.into())
Expand All @@ -95,11 +89,8 @@ impl PyAzureStore {
kwargs: Option<PyAzureConfig>,
) -> PyObjectStoreResult<Self> {
let mut builder = MicrosoftAzureBuilder::from_env().with_url(url);
if let Some(config) = config {
builder = config.apply_config(builder);
}
if let Some(kwargs) = kwargs {
builder = kwargs.apply_config(builder);
if let Some(config_kwargs) = combine_config_kwargs(config, kwargs)? {
builder = config_kwargs.apply_config(builder);
}
if let Some(client_options) = client_options {
builder = builder.with_client_options(client_options.into())
Expand All @@ -116,7 +107,7 @@ impl PyAzureStore {
}
}

#[derive(Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PyAzureConfigKey(AzureConfigKey);

impl<'py> FromPyObject<'py> for PyAzureConfigKey {
Expand All @@ -127,7 +118,7 @@ impl<'py> FromPyObject<'py> for PyAzureConfigKey {
}
}

#[derive(Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PyAzureConfig(HashMap<PyAzureConfigKey, PyConfigValue>);

impl<'py> FromPyObject<'py> for PyAzureConfig {
Expand All @@ -143,4 +134,30 @@ impl PyAzureConfig {
}
builder
}

fn merge(mut self, other: PyAzureConfig) -> PyObjectStoreResult<PyAzureConfig> {
for (k, v) in other.0.into_iter() {
let old_value = self.0.insert(k.clone(), v);
if old_value.is_some() {
return Err(ObstoreError::new_err(format!(
"Duplicate key {} between config and kwargs",
k.0.as_ref()
))
.into());
}
}

Ok(self)
}
}

fn combine_config_kwargs(
config: Option<PyAzureConfig>,
kwargs: Option<PyAzureConfig>,
) -> PyObjectStoreResult<Option<PyAzureConfig>> {
match (config, kwargs) {
(None, None) => Ok(None),
(Some(x), None) | (None, Some(x)) => Ok(Some(x)),
(Some(config), Some(kwargs)) => Ok(Some(config.merge(kwargs)?)),
}
}
2 changes: 1 addition & 1 deletion pyo3-object_store/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use pyo3::prelude::*;
/// - `True` and `False` (becomes `"true"` and `"false"`)
/// - `timedelta`
/// - `str`
#[derive(Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PyConfigValue(pub String);

impl<'py> FromPyObject<'py> for PyConfigValue {
Expand Down
53 changes: 35 additions & 18 deletions pyo3-object_store/src/gcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use pyo3::types::PyType;

use crate::client::PyClientOptions;
use crate::config::PyConfigValue;
use crate::error::{PyObjectStoreError, PyObjectStoreResult};
use crate::error::{ObstoreError, PyObjectStoreError, PyObjectStoreResult};
use crate::retry::PyRetryConfig;

/// A Python-facing wrapper around a [`GoogleCloudStorage`].
Expand Down Expand Up @@ -42,11 +42,8 @@ impl PyGCSStore {
kwargs: Option<PyGoogleConfig>,
) -> PyObjectStoreResult<Self> {
let mut builder = GoogleCloudStorageBuilder::new().with_bucket_name(bucket);
if let Some(config) = config {
builder = config.apply_config(builder);
}
if let Some(kwargs) = kwargs {
builder = kwargs.apply_config(builder);
if let Some(config_kwargs) = combine_config_kwargs(config, kwargs)? {
builder = config_kwargs.apply_config(builder);
}
if let Some(client_options) = client_options {
builder = builder.with_client_options(client_options.into())
Expand All @@ -69,11 +66,8 @@ impl PyGCSStore {
kwargs: Option<PyGoogleConfig>,
) -> PyObjectStoreResult<Self> {
let mut builder = GoogleCloudStorageBuilder::from_env().with_bucket_name(bucket);
if let Some(config) = config {
builder = config.apply_config(builder);
}
if let Some(kwargs) = kwargs {
builder = kwargs.apply_config(builder);
if let Some(config_kwargs) = combine_config_kwargs(config, kwargs)? {
builder = config_kwargs.apply_config(builder);
}
if let Some(client_options) = client_options {
builder = builder.with_client_options(client_options.into())
Expand All @@ -95,11 +89,8 @@ impl PyGCSStore {
kwargs: Option<PyGoogleConfig>,
) -> PyObjectStoreResult<Self> {
let mut builder = GoogleCloudStorageBuilder::from_env().with_url(url);
if let Some(config) = config {
builder = config.apply_config(builder);
}
if let Some(kwargs) = kwargs {
builder = kwargs.apply_config(builder);
if let Some(config_kwargs) = combine_config_kwargs(config, kwargs)? {
builder = config_kwargs.apply_config(builder);
}
if let Some(client_options) = client_options {
builder = builder.with_client_options(client_options.into())
Expand All @@ -116,7 +107,7 @@ impl PyGCSStore {
}
}

#[derive(Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PyGoogleConfigKey(GoogleConfigKey);

impl<'py> FromPyObject<'py> for PyGoogleConfigKey {
Expand All @@ -127,7 +118,7 @@ impl<'py> FromPyObject<'py> for PyGoogleConfigKey {
}
}

#[derive(Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PyGoogleConfig(HashMap<PyGoogleConfigKey, PyConfigValue>);

impl<'py> FromPyObject<'py> for PyGoogleConfig {
Expand All @@ -143,4 +134,30 @@ impl PyGoogleConfig {
}
builder
}

fn merge(mut self, other: PyGoogleConfig) -> PyObjectStoreResult<PyGoogleConfig> {
for (k, v) in other.0.into_iter() {
let old_value = self.0.insert(k.clone(), v);
if old_value.is_some() {
return Err(ObstoreError::new_err(format!(
"Duplicate key {} between config and kwargs",
k.0.as_ref()
))
.into());
}
}

Ok(self)
}
}

fn combine_config_kwargs(
config: Option<PyGoogleConfig>,
kwargs: Option<PyGoogleConfig>,
) -> PyObjectStoreResult<Option<PyGoogleConfig>> {
match (config, kwargs) {
(None, None) => Ok(None),
(Some(x), None) | (None, Some(x)) => Ok(Some(x)),
(Some(config), Some(kwargs)) => Ok(Some(config.merge(kwargs)?)),
}
}

0 comments on commit 5082d96

Please sign in to comment.