Skip to content

Commit

Permalink
feat(python): Add read_clipboard and DataFrame.write_clipboard (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
CanglongCl authored Apr 1, 2024
1 parent b1eaff3 commit 82f717b
Show file tree
Hide file tree
Showing 10 changed files with 207 additions and 0 deletions.
103 changes: 103 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ version_check = "0.9.4"
xxhash-rust = { version = "0.8.6", features = ["xxh3"] }
zstd = "0.13"
uuid = { version = "1.7.0", features = ["v4"] }
arboard = { version = "3.3.2", default-features = false }

polars = { version = "0.38.3", path = "crates/polars", default-features = false }
polars-compute = { version = "0.38.3", path = "crates/polars-compute", default-features = false }
Expand Down
3 changes: 3 additions & 0 deletions py-polars/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ polars-plan = { workspace = true }
polars-utils = { workspace = true }

ahash = { workspace = true }
arboard = { workspace = true, optional = true }
ciborium = { workspace = true }
either = { workspace = true }
itoa = { workspace = true }
Expand Down Expand Up @@ -126,6 +127,7 @@ search_sorted = ["polars/search_sorted"]
decompress = ["polars/decompress-fast"]
regex = ["polars/regex"]
csv = ["polars/csv"]
clipboard = ["arboard"]
object = ["polars/object"]
extract_jsonpath = ["polars/extract_jsonpath"]
pivot = ["polars/pivot"]
Expand Down Expand Up @@ -204,6 +206,7 @@ io = [
"avro",
"csv",
"cloud",
"clipboard",
]

optimizations = [
Expand Down
8 changes: 8 additions & 0 deletions py-polars/docs/source/reference/io.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ Avro
read_avro
DataFrame.write_avro

Clipboard
~~~~~~~~~
.. autosummary::
:toctree: api/

read_clipboard
DataFrame.write_clipboard

CSV
~~~
.. autosummary::
Expand Down
2 changes: 2 additions & 0 deletions py-polars/polars/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@
)
from polars.io import (
read_avro,
read_clipboard,
read_csv,
read_csv_batched,
read_database,
Expand Down Expand Up @@ -316,6 +317,7 @@
"scan_ndjson",
"scan_parquet",
"scan_pyarrow_dataset",
"read_clipboard",
# polars.stringcache
"StringCache",
"disable_string_cache",
Expand Down
22 changes: 22 additions & 0 deletions py-polars/polars/dataframe/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
with contextlib.suppress(ImportError): # Module not available when building docs
from polars.polars import PyDataFrame
from polars.polars import dtype_str_repr as _dtype_str_repr
from polars.polars import write_clipboard_string as _write_clipboard_string

if TYPE_CHECKING:
import sys
Expand Down Expand Up @@ -2595,6 +2596,27 @@ def write_csv(

return None

def write_clipboard(self, *, separator: str = "\t", **kwargs: Any) -> None:
"""
Copy `DataFrame` in csv format to the system clipboard with `write_csv`.
Useful for pasting into Excel or other similar spreadsheet software.
Parameters
----------
separator
Separate CSV fields with this symbol.
kwargs
Additional arguments to pass to `write_csv`.
See Also
--------
polars.read_clipboard: Read a DataFrame from the clipboard.
write_csv: Write to comma-separated values (CSV) file.
"""
result: str = self.write_csv(file=None, separator=separator, **kwargs)
_write_clipboard_string(result)

def write_avro(
self,
file: str | Path | IO[bytes],
Expand Down
2 changes: 2 additions & 0 deletions py-polars/polars/io/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Functions for reading data."""

from polars.io.avro import read_avro
from polars.io.clipboard import read_clipboard
from polars.io.csv import read_csv, read_csv_batched, scan_csv
from polars.io.database import read_database, read_database_uri
from polars.io.delta import read_delta, scan_delta
Expand Down Expand Up @@ -35,4 +36,5 @@
"scan_ndjson",
"scan_parquet",
"scan_pyarrow_dataset",
"read_clipboard",
]
36 changes: 36 additions & 0 deletions py-polars/polars/io/clipboard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from __future__ import annotations

import contextlib
from io import StringIO
from typing import TYPE_CHECKING, Any

from polars.io.csv.functions import read_csv

with contextlib.suppress(ImportError):
from polars.polars import read_clipboard_string as _read_clipboard_string

if TYPE_CHECKING:
from polars import DataFrame


def read_clipboard(separator: str = "\t", **kwargs: Any) -> DataFrame:
"""
Read text from clipboard and pass to `read_csv`.
Useful for reading data copied from Excel or other similar spreadsheet software.
Parameters
----------
separator
Single byte character to use as separator parsing csv from clipboard.
kwargs
Additional arguments passed to `read_csv`.
See Also
--------
read_csv : Read a csv file into a DataFrame.
DataFrame.write_clipboard : Write a DataFrame to the clipboard.
"""
csv_string: str = _read_clipboard_string()
io_string = StringIO(csv_string)
return read_csv(source=io_string, separator=separator, **kwargs)
24 changes: 24 additions & 0 deletions py-polars/src/functions/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,27 @@ fn fields_to_pydict(fields: &Vec<Field>, dict: &PyDict, py: Python) -> PyResult<
}
Ok(())
}

#[cfg(feature = "clipboard")]
#[pyfunction]
pub fn read_clipboard_string() -> PyResult<String> {
use arboard;
let mut clipboard =
arboard::Clipboard::new().map_err(|e| PyPolarsErr::Other(format!("{e}")))?;
let result = clipboard
.get_text()
.map_err(|e| PyPolarsErr::Other(format!("{e}")))?;
Ok(result)
}

#[cfg(feature = "clipboard")]
#[pyfunction]
pub fn write_clipboard_string(s: &str) -> PyResult<()> {
use arboard;
let mut clipboard =
arboard::Clipboard::new().map_err(|e| PyPolarsErr::Other(format!("{e}")))?;
clipboard
.set_text(s)
.map_err(|e| PyPolarsErr::Other(format!("{e}")))?;
Ok(())
}
6 changes: 6 additions & 0 deletions py-polars/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,12 @@ fn polars(py: Python, m: &PyModule) -> PyResult<()> {
#[cfg(feature = "parquet")]
m.add_wrapped(wrap_pyfunction!(functions::read_parquet_schema))
.unwrap();
#[cfg(feature = "clipboard")]
m.add_wrapped(wrap_pyfunction!(functions::read_clipboard_string))
.unwrap();
#[cfg(feature = "clipboard")]
m.add_wrapped(wrap_pyfunction!(functions::write_clipboard_string))
.unwrap();

// Functions - meta
m.add_wrapped(wrap_pyfunction!(functions::get_index_type))
Expand Down

0 comments on commit 82f717b

Please sign in to comment.