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

feat(expressions): Extend Expression.url.upload() to support row-specific URLs #3518

Merged
Merged
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
4 changes: 3 additions & 1 deletion daft/daft/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -1056,9 +1056,11 @@ def url_download(
) -> PyExpr: ...
def url_upload(
expr: PyExpr,
folder_location: str,
folder_location: PyExpr,
max_connections: int,
raise_error_on_failure: bool,
multi_thread: bool,
is_single_folder: bool,
io_config: IOConfig | None,
) -> PyExpr: ...
def tokenize_encode(
Expand Down
32 changes: 27 additions & 5 deletions daft/expressions/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1381,21 +1381,24 @@

def upload(
self,
location: str,
location: str | Expression,
max_connections: int = 32,
on_error: Literal["raise", "null"] = "raise",
io_config: IOConfig | None = None,
) -> Expression:
"""Uploads a column of binary data to the provided location (also supports S3, local etc).
"""Uploads a column of binary data to the provided location(s) (also supports S3, local etc).

Files will be written into the location (folder) with a generated UUID filename, and the result
Files will be written into the location (folder(s)) with a generated UUID filename, and the result
will be returned as a column of string paths that is compatible with the ``.url.download()`` Expression.

Example:
>>> col("data").url.upload("s3://my-bucket/my-folder") # doctest: +SKIP

Args:
location: a folder location to upload data into
location: a folder location or column of folder locations to upload data into
max_connections: The maximum number of connections to use per thread to use for uploading data. Defaults to 32.
on_error: Behavior when a URL upload error is encountered - "raise" to raise the error immediately or "null" to log
the error but fallback to a Null value. Defaults to "raise".
desmondcheongzx marked this conversation as resolved.
Show resolved Hide resolved
io_config: IOConfig to use when uploading data

Returns:
Expand All @@ -1404,10 +1407,29 @@
if not (isinstance(max_connections, int) and max_connections > 0):
raise ValueError(f"Invalid value for `max_connections`: {max_connections}")

location_expr = Expression._to_expression(location)
raise_on_error = False
if on_error == "raise":
raise_on_error = True
elif on_error == "null":
raise_on_error = False
else:
raise NotImplementedError(f"Unimplemented on_error option: {on_error}.")

Check warning on line 1417 in daft/expressions/expressions.py

View check run for this annotation

Codecov / codecov/patch

daft/expressions/expressions.py#L1417

Added line #L1417 was not covered by tests
multi_thread = ExpressionUrlNamespace._should_use_multithreading_tokio_runtime()
# If the user specifies a single location via a string, we should upload to a single folder. Otherwise,
# if the user gave an expression, we assume that each row has a specific url to upload to.
is_single_folder = isinstance(location, str)
io_config = ExpressionUrlNamespace._override_io_config_max_connections(max_connections, io_config)
return Expression._from_pyexpr(
native.url_upload(self._expr, location, max_connections, multi_thread, io_config)
native.url_upload(
self._expr,
location_expr._expr,
max_connections,
raise_on_error,
multi_thread,
is_single_folder,
io_config,
)
)


Expand Down
8 changes: 6 additions & 2 deletions src/daft-functions/src/python/uri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ pub fn url_download(
#[pyfunction]
pub fn url_upload(
expr: PyExpr,
folder_location: &str,
folder_location: PyExpr,
max_connections: i64,
raise_error_on_failure: bool,
multi_thread: bool,
is_single_folder: bool,
io_config: Option<IOConfig>,
) -> PyResult<PyExpr> {
if max_connections <= 0 {
Expand All @@ -41,9 +43,11 @@ pub fn url_upload(
}
Ok(crate::uri::upload(
expr.into(),
folder_location,
folder_location.into(),
max_connections as usize,
raise_error_on_failure,
multi_thread,
is_single_folder,
io_config.map(|io_config| io_config.config),
)
.into())
Expand Down
9 changes: 6 additions & 3 deletions src/daft-functions/src/uri/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,22 @@ pub fn download(
#[must_use]
pub fn upload(
input: ExprRef,
location: &str,
location: ExprRef,
max_connections: usize,
raise_error_on_failure: bool,
multi_thread: bool,
is_single_folder: bool,
config: Option<IOConfig>,
) -> ExprRef {
ScalarFunction::new(
UploadFunction {
location: location.to_string(),
max_connections,
raise_error_on_failure,
multi_thread,
is_single_folder,
config: config.unwrap_or_default().into(),
},
vec![input],
vec![input, location],
)
.into()
}
Loading
Loading