Skip to content

Commit

Permalink
Merge pull request #21 from mgaitan/best_binary
Browse files Browse the repository at this point in the history
allow pasting arbitrary binary content in xclip
  • Loading branch information
spyoungtech authored Oct 31, 2022
2 parents 75bbe62 + e18b874 commit 48f23b0
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 11 deletions.
21 changes: 16 additions & 5 deletions pyclip/xclip_clip.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
"""
Provides the clipboard functionality for Linux via ``xclip``
"""
import warnings

from .base import ClipboardBase, ClipboardSetupException, ClipboardException
from typing import Union
import shutil
import subprocess
import warnings
from typing import Union

from .base import ClipboardBase, ClipboardException, ClipboardSetupException


class XclipClipboard(ClipboardBase):
Expand Down Expand Up @@ -94,8 +94,19 @@ def paste(self, encoding: str = None, text: bool = None, errors: str = None):
encoding=encoding,
)
else:
# retrieve the available targets and selects the first mime type available or plain text.
available_targets = [
t for t in subprocess.check_output(args + ['-t', 'TARGETS'], text=True).splitlines() if t.islower()
]
if "text/plain" in available_targets:
target = ["-t", "text/plain"]
elif available_targets:
target = ["-t", available_targets[0]]
else:
target = []

completed_proc = subprocess.run(
args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
args + target, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)

if completed_proc.returncode != 0:
Expand Down
32 changes: 26 additions & 6 deletions tests/test_clipboard.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import sys, os
import os
import secrets
import subprocess
import sys
from unittest import mock

import pytest

try:
import pyclip as clip
except ImportError:
# XXX: probably shouldn't do this, but oh well ¯\_(ツ)_/¯
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
import pyclip as clip
import pytest

from unittest import mock

def test_copypaste():
clip.copy('foo')
Expand All @@ -24,13 +29,11 @@ def test_copypaste_unicode():

@pytest.mark.xfail(sys.platform == 'darwin', reason="MacOS doesn't yet support arbitrary data")
def test_copy_paste_arbitrary_data():
import secrets
randbytes = secrets.token_bytes(1024)
clip.copy(randbytes)
assert clip.paste() == randbytes

def test_copy_paste_null_bytes_in_body():
import secrets
randbytes = secrets.token_bytes(1024)

data_body = b'somedata\x00\x00'+randbytes
Expand All @@ -39,7 +42,6 @@ def test_copy_paste_null_bytes_in_body():


def test_copy_paste_null_terminated_bytes():
import secrets
randbytes = secrets.token_bytes(1024)

data_body = b'somedata\x00\x00'+randbytes + b'\x00\x00'
Expand Down Expand Up @@ -159,6 +161,24 @@ def test_xclip_missing_raises_error():
c = XclipClipboard()


@pytest.mark.skipif(sys.platform != 'linux', reason='This test is for Linux only')
@pytest.mark.parametrize('targets, expected', [
(["TARGETS", "TIMESTAMP", "image/png"], ["-t", "image/png"]),
([], []),
(["TARGETS", "TIMESTAMP", "text/plain"], ["-t", "text/plain"]),
(["text/html", "TARGETS", "TIMESTAMP", "text/plain"], ["-t", "text/plain"]),
])
def test_xclip_with_target(targets, expected):
with mock.patch.object(subprocess, 'check_output', return_value="\n".join(targets)):
with mock.patch.object(subprocess, 'run', wraps=subprocess.run) as wrapped_run:
data = secrets.token_bytes(10)
clip.copy(data)
assert clip.paste() == data

# the expected target was selected
args = wrapped_run.call_args[0][0]
assert args[1:] == ['-o', '-selection', 'clipboard'] + expected

def test_unknown_platform_raises_error():
from pyclip.util import detect_clipboard, ClipboardSetupException
with mock.patch('sys.platform', new='unknown'):
Expand Down

0 comments on commit 48f23b0

Please sign in to comment.