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

Copy (xclip backend) hangs when called in side shell command substitution #116

Closed
modax opened this issue Jan 24, 2018 · 5 comments
Closed

Comments

@modax
Copy link
Contributor

modax commented Jan 24, 2018

Hello,

OS: Debian unstable linux-amd64
Pyperclip 1.6.0
Backend in use: xclip
Shell: zsh, dash

pyperclip.copy() hangs when called inside shell's command substitution. When xclip executes, python process is gone while xclip stays up running. But it seems to be Python3-ony problem, Python2 is fine.

$ python3 --version
Python 3.6.4
$  echo "$(python3 -c 'import pyperclip; pyperclip.copy("foo")'; echo "foo")"
<hangs> ...
$ pgrep python
$ pgrep xclip
5659
Python 2.7.14+
$ echo "$(python2 -c 'import pyperclip; pyperclip.copy("foo")'; echo "foo")"
foo
$
@modax
Copy link
Contributor Author

modax commented Jan 26, 2018

Well, it seems that xclip is kind of broken in general when used in pipes. E.g. this one hangs as well:

echo "$(echo "foo" | xclip -selection clipboard)"

Could you make xsel preferred over xclip?

modax added a commit to modax/pyperclip that referenced this issue Jan 26, 2018
@modax
Copy link
Contributor Author

modax commented Feb 5, 2018

It seems that stdout must be closed, duped to stderr or piped to /dev/null for xclip work properly. So I worked around this problem with multiprocess:

import multiprocessing
import pyperclip
import os

def pyperclip_copy_wrapper(text):
    # Dup stdout to devnull
    fdnull = os.open(os.devnull, os.O_WRONLY)
    os.dup2(fdnull, 1)
    try:
        pyperclip.copy(text)
    finally:
        os.close(1)
        os.close(fdnull)

def copy_to_clipboard(text):
    p = multiprocessing.Process(name='copy-to-clipboard', target=pyperclip_copy_wrapper, args=(text,))
    p.start()
    p.join()
    return p.exitcode

This works with both xclip and xsel on both Python 2.7 and Python 3.6. Hopefully it does not break anything else.

Btw, xclip version is 0.12+svn84-4+b1

@modax
Copy link
Contributor Author

modax commented Feb 5, 2018

Or alternatively, just dup around in the same process. This does not break e.g. gtk clipboard and is probably safe enough:

import os
import pyperclip

def copy_to_clipboard(text):
    stdoutfd_dup = os.dup(1)
    fdnull = os.open(os.devnull, os.O_WRONLY)
    try:
        os.dup2(fdnull, 1)
        pyperclip.copy(text)
    finally:
        # Restore stdout FD and close temporary ones
        os.dup2(stdoutfd_dup, 1)
        os.close(stdoutfd_dup)
        os.close(fdnull)

@chocolateboy
Copy link

Well, it seems that xclip is kind of broken in general when used in pipes.

This appears to be the documented behavior of its default -silent (i.e. daemon) mode:

fork into the background to wait for requests, no informational output, errors only (default)

The default action is to silently wait in the background for X selection requests (pastes) until another X application places data in the clipboard, at which point xclip exits silently.

echo "foo" | xclip -selection clipboard | cat
<hangs>
echo "foo" | xclip -selection clipboard -silent | cat
<hangs>

It doesn't hang for me (xclip 0.13 on Linux (Arch)) if it's instructed to work as a filter (i.e. kept in the foreground) via the -f/-filter option e.g.:

echo "foo" | xclip -selection clipboard -f | cat
foo

@FluorineDog
Copy link

Installing xsel solves my headache. Maybe make it an explicit dependency better?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants