summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Leonhardt <todd.leonhardt@gmail.com>2017-06-30 16:47:47 -0400
committerTodd Leonhardt <todd.leonhardt@gmail.com>2017-06-30 16:47:47 -0400
commit42d4ba77ddbee818ab7e198d52ed5a2c555e6330 (patch)
tree93e3fbcac10785c694f8074741169c8c3aae9937
parent679401b5dd314843694e92ca5c7d68e4f0f63c4d (diff)
downloadcmd2-git-42d4ba77ddbee818ab7e198d52ed5a2c555e6330.tar.gz
Replaced clipboard copy and paste custom code with pyperclip
- Added a 3rd-party dependency on the pyperclip module TODO: Rigorous test on all OSes, particularly Linux systems without xclip
-rw-r--r--CHANGES.md4
-rw-r--r--CONTRIBUTING.md11
-rwxr-xr-xREADME.md3
-rwxr-xr-xcmd2.py151
-rw-r--r--docs/install.rst1
-rwxr-xr-xsetup.py4
-rw-r--r--tox.ini1
7 files changed, 33 insertions, 142 deletions
diff --git a/CHANGES.md b/CHANGES.md
index d84e6b74..b853f222 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -17,7 +17,9 @@ News
* Removed presence of a default file name and default file extension
* These also strongly felt out of place
* ``load`` and ``_relative_load`` now require a file path
- * ``edit`` and ``save`` now use a temporary file if a file path isn't provided
+ * ``edit`` and ``save`` now use a temporary file if a file path isn't provided
+ * Load command has better error checking and reporting
+ * Clipboard copy and paste functionality is now handled by the ``pyperclip`` module
0.7.3
-----
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a0a6b21e..ffac39bf 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -42,11 +42,12 @@ The tables below list all prerequisites along with the minimum required version
#### Prerequisites to run cmd2 applications
-| Prerequisite | Minimum Version |
-| ------------------------------------------- | --------------- |
-| [Python](https://www.python.org/downloads/) | `3.3 or 2.7` |
-| [six](https://pypi.python.org/pypi/six) | `1.8` |
-| [pyparsing](http://pyparsing.wikispaces.com)| `2.0.3` |
+| Prerequisite | Minimum Version |
+| --------------------------------------------------- | --------------- |
+| [Python](https://www.python.org/downloads/) | `3.3 or 2.7` |
+| [six](https://pypi.python.org/pypi/six) | `1.8` |
+| [pyparsing](http://pyparsing.wikispaces.com) | `2.0.3` |
+| [pyperclip](https://github.com/asweigart/pyperclip) | `1.5` |
#### Additional prerequisites to run cmd2 unit tests
diff --git a/README.md b/README.md
index 862622f3..1642b4b5 100755
--- a/README.md
+++ b/README.md
@@ -9,7 +9,8 @@ cmd2
cmd2 is a tool for writing command-line interactive applications for Python 2.7 and Python 3.3+. It is based on the
Python Standard Library's [cmd](https://docs.python.org/3/library/cmd.html) module, and can be used any place cmd is used simply by importing cmd2 instead. It is
-pure Python code with the only 3rd-party dependencies being on [six](https://pypi.python.org/pypi/six) and [pyparsing](http://pyparsing.wikispaces.com).
+pure Python code with the only 3rd-party dependencies being on [six](https://pypi.python.org/pypi/six), [pyparsing](http://pyparsing.wikispaces.com),
+and [pyperclip](https://github.com/asweigart/pyperclip).
The latest documentation for cmd2 can be read online here: https://cmd2.readthedocs.io/en/latest/
diff --git a/cmd2.py b/cmd2.py
index f54a8555..cf98ba05 100755
--- a/cmd2.py
+++ b/cmd2.py
@@ -44,6 +44,7 @@ from code import InteractiveConsole
from optparse import make_option
import pyparsing
+import pyperclip
# next(it) gets next item of iterator it. This is a replacement for calling it.next() in Python 2 and next(it) in Py3
from six import next
@@ -301,148 +302,32 @@ def options(option_list, arg_desc="arg"):
return option_setup
-# Prefix to use on all OSes when the appropriate library or CLI tool isn't installed for getting access to paste buffer
-pastebufferr = """Redirecting to or from paste buffer requires %s
-to be installed on operating system.
-%s"""
-
# Can we access the clipboard?
-can_clip = False
-if sys.platform == "win32":
- # Running on Windows
- try:
- # noinspection PyUnresolvedReferences
- import win32clipboard
-
- def get_paste_buffer():
- """Get the contents of the clipboard for Windows OSes.
+can_clip = True
- :return: str - contents of the clipboard
- """
- win32clipboard.OpenClipboard(0)
- try:
- result = win32clipboard.GetClipboardData()
- except TypeError:
- result = '' # non-text
- win32clipboard.CloseClipboard()
- return result
- def write_to_paste_buffer(txt):
- """Paste text to the clipboard for Windows OSes.
-
- :param txt: str - text to paste to the clipboard
- """
- win32clipboard.OpenClipboard(0)
- win32clipboard.EmptyClipboard()
- win32clipboard.SetClipboardText(txt)
- win32clipboard.CloseClipboard()
-
- can_clip = True
- except ImportError:
- # noinspection PyUnusedLocal
- def get_paste_buffer(*args):
- """For Windows OSes without the appropriate library installed to get text from clipboard, raise an exception.
- """
- raise OSError(pastebufferr % ('pywin32', 'Download from http://sourceforge.net/projects/pywin32/'))
-
- write_to_paste_buffer = get_paste_buffer
-elif sys.platform == 'darwin':
- # Running on Mac OS X
- try:
- # Warning: subprocess.call() and subprocess.check_call() should never be called with stdout=PIPE or stderr=PIPE
- # because the child process will block if it generates enough output to a pipe to fill up the OS pipe buffer.
- # Starting with Python 3.5 there is a newer, safer API based on the run() function.
-
- # Python 3.3+ supports subprocess.DEVNULL, but that isn't defined for Python 2.7
- with open(os.devnull, 'w') as DEVNULL:
- # test for pbcopy - AFAIK, should always be installed on MacOS
- subprocess.check_call(['pbcopy', '-help'], stdin=subprocess.PIPE, stdout=DEVNULL, stderr=DEVNULL)
- can_clip = True
- except (subprocess.CalledProcessError, OSError, IOError):
- pass
- if can_clip:
- def get_paste_buffer():
- """Get the contents of the clipboard for Mac OS X.
-
- :return: str - contents of the clipboard
- """
- pbcopyproc = subprocess.Popen('pbpaste', stdin=subprocess.PIPE, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- stdout, stderr = pbcopyproc.communicate()
- if six.PY3:
- return stdout.decode()
- else:
- return stdout
+def get_paste_buffer():
+ """Get the contents of the clipboard / paste buffer.
- def write_to_paste_buffer(txt):
- """Paste text to the clipboard for Mac OS X.
+ :return: str - contents of the clipboard
+ """
+ pb_unicode = pyperclip.paste()
- :param txt: str - text to paste to the clipboard
- """
- pbcopyproc = subprocess.Popen('pbcopy', stdin=subprocess.PIPE, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- if six.PY3:
- pbcopyproc.communicate(txt.encode())
- else:
- pbcopyproc.communicate(txt)
+ if six.PY3:
+ pb_str = pb_unicode
else:
- # noinspection PyUnusedLocal
- def get_paste_buffer(*args):
- """For Mac OS X without the appropriate tool installed to get text from clipboard, raise an exception."""
- raise OSError(pastebufferr % ('pbcopy',
- 'On MacOS X - error should not occur - part of the default installation'))
-
- write_to_paste_buffer = get_paste_buffer
-else:
- # Running on Linux
- try:
- with open(os.devnull, 'w') as DEVNULL:
- subprocess.check_call(['uptime', '|', 'xclip'], stdin=subprocess.PIPE, stdout=DEVNULL, stderr=DEVNULL)
- can_clip = True
- except (subprocess.CalledProcessError, OSError, IOError):
- pass # something went wrong with xclip and we cannot use it
- if can_clip:
- def get_paste_buffer():
- """Get the contents of the clipboard for Linux OSes.
-
- :return: str - contents of the clipboard
- """
- xclipproc = subprocess.Popen(['xclip', '-o', '-selection', 'clipboard'], stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- stdout, stderr = xclipproc.communicate()
- if six.PY3:
- return stdout.decode()
- else:
- return stdout
+ import unicodedata
+ pb_str = unicodedata.normalize('NFKD', pb_unicode).encode('ascii', 'ignore')
- def write_to_paste_buffer(txt):
- """Paste text to the clipboard for Linux OSes.
+ return pb_str
- :param txt: str - text to paste to the clipboard
- """
- xclipproc = subprocess.Popen(['xclip', '-selection', 'clipboard'], stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- if six.PY3:
- xclipproc.stdin.write(txt.encode())
- else:
- xclipproc.stdin.write(txt)
- xclipproc.stdin.close()
-
- # but we want it in both the "primary" and "mouse" clipboards
- xclipproc = subprocess.Popen(['xclip'], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- if six.PY3:
- xclipproc.stdin.write(txt.encode())
- else:
- xclipproc.stdin.write(txt)
- xclipproc.stdin.close()
- else:
- # noinspection PyUnusedLocal
- def get_paste_buffer(*args):
- """For Linux without the appropriate tool installed to get text from clipboard, raise an exception."""
- raise OSError(pastebufferr % ('xclip', 'On Debian/Ubuntu, install with "sudo apt-get install xclip"'))
- write_to_paste_buffer = get_paste_buffer
+def write_to_paste_buffer(txt):
+ """Copy text to the clipboard / paste buffer.
+
+ :param txt: str - text to copy to the clipboard
+ """
+ pyperclip.copy(txt)
class ParsedString(str):
diff --git a/docs/install.rst b/docs/install.rst
index 78b330fc..6d293176 100644
--- a/docs/install.rst
+++ b/docs/install.rst
@@ -105,6 +105,7 @@ the following Python packages are installed:
* six
* pyparsing
+ * pyperclip
Upgrading cmd2
diff --git a/setup.py b/setup.py
index 3b3b58b5..c29c20f7 100755
--- a/setup.py
+++ b/setup.py
@@ -56,10 +56,10 @@ Programming Language :: Python :: Implementation :: PyPy
Topic :: Software Development :: Libraries :: Python Modules
""".splitlines())))
-INSTALL_REQUIRES = ['pyparsing >= 2.0.1', 'six']
+INSTALL_REQUIRES = ['pyparsing >= 2.0.1', 'pyperclip', 'six']
# unitest.mock was added in Python 3.3. mock is a backport of unittest.mock to all versions of Python
TESTS_REQUIRE = ['mock', 'pytest']
-DOCS_REQUIRE = ['sphinx', 'sphinx_rtd_theme', 'pyparsing', 'six']
+DOCS_REQUIRE = ['sphinx', 'sphinx_rtd_theme', 'pyparsing', 'pyperclip', 'six']
setup(
name="cmd2",
diff --git a/tox.ini b/tox.ini
index 869a53ce..6dd6f08e 100644
--- a/tox.ini
+++ b/tox.ini
@@ -7,6 +7,7 @@ deps =
codecov
mock
pyparsing
+ pyperclip
pytest
pytest-cov
six