diff options
| author | Todd Leonhardt <todd.leonhardt@gmail.com> | 2017-06-25 16:31:59 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-06-25 16:31:59 -0400 |
| commit | f1af8b762a4b1aec6501cd2ae67dd3a0aeef2bfb (patch) | |
| tree | a9c304d8b36bffc6b402bfaea54a8f5cff01a3e3 | |
| parent | b73117be943403cb000efd9f97bc72586261630d (diff) | |
| parent | b44ffe7d637d513be5be916f867b29272470c9bd (diff) | |
| download | cmd2-git-f1af8b762a4b1aec6501cd2ae67dd3a0aeef2bfb.tar.gz | |
Merge pull request #141 from python-cmd2/code_coverage
Fix pastebuffer/clipboard issues on macOS and Linux
| -rw-r--r-- | .coveragerc | 3 | ||||
| -rw-r--r-- | .travis.yml | 40 | ||||
| -rwxr-xr-x | cmd2.py | 54 | ||||
| -rw-r--r-- | tests/test_cmd2.py | 27 | ||||
| -rw-r--r-- | tests/test_transcript.py | 9 |
5 files changed, 79 insertions, 54 deletions
diff --git a/.coveragerc b/.coveragerc index ea915f7c..bdd0ee85 100644 --- a/.coveragerc +++ b/.coveragerc @@ -13,9 +13,6 @@ exclude_lines = # Don't complain if non-runnable code isn't run: if __name__ == .__main__.: - # Don't complain about macOS-specific code until we have a CI platform that builds on macOS - if sys\.platform == 'darwin': - # (integer): the number of digits after the decimal point to display for reported coverage percentages. precision = 1 diff --git a/.travis.yml b/.travis.yml index f246f8cf..2d59e8c5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: python -sudo: false # enable container-based build for fast boot times on Linux +sudo: false # false enables container-based build for fast boot times on Linux matrix: include: - os: linux @@ -27,26 +27,26 @@ matrix: # - os: linux # python: pypy3 # env: TOXENV=pypy3 -# # Stock OSX Python -# - os: osx -# language: generic -# env: TOXENV=py27 -# # Latest Python 3.x from Homebrew -# - os: osx -# language: generic -# env: -# - TOXENV=py36 -# - BREW_INSTALL=python3 + # Stock OSX Python + - os: osx + language: generic + env: TOXENV=py27 + # Latest Python 3.x from Homebrew + - os: osx + language: generic + env: + - TOXENV=py36 + - BREW_INSTALL=python3 install: - - pip install tox -# - | -# if [[ $TRAVIS_OS_NAME == 'osx' ]]; then -# if [[ -n "$BREW_INSTALL" ]]; then -# brew update -# brew install "$BREW_INSTALL" -# fi -# fi -# pip install tox +# - pip install tox + - | + if [[ $TRAVIS_OS_NAME == 'osx' ]]; then + if [[ -n "$BREW_INSTALL" ]]; then + brew update + brew install "$BREW_INSTALL" + fi + fi + pip install tox script: - tox @@ -196,17 +196,6 @@ def remaining_args(opts_plus_args, arg_list): return remaining -def _attr_get_(obj, attr): - """Returns an attribute's value, or None (no error) if undefined. - Analogous to .get() for dictionaries. Useful when checking for - value of options that may not have been defined on a given - method.""" - try: - return getattr(obj, attr) - except AttributeError: - return None - - def _which(editor): try: return subprocess.Popen(['which', editor], stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0] @@ -229,10 +218,6 @@ def strip_quotes(arg): return arg -optparse.Values.get = _attr_get_ -options_defined = [] # used to distinguish --options from SQL-style --comments - - def options(option_list, arg_desc="arg"): """Used as a decorator and passed a list of optparse-style options, alters a cmd2 method to populate its ``opts`` argument from its @@ -250,9 +235,8 @@ def options(option_list, arg_desc="arg"): self.fast_button = True """ if not isinstance(option_list, list): + # If passed a single option instead of a list of options, convert it to a list with one option option_list = [option_list] - for opt in option_list: - options_defined.append(pyparsing.Literal(opt.get_opt_string())) def option_setup(func): """Decorator function which modifies on of the do_* methods that use the @options decorator. @@ -387,9 +371,13 @@ elif sys.platform == 'darwin': :return: str - contents of the clipboard """ - pbcopyproc = subprocess.Popen('pbcopy -help', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, + pbcopyproc = subprocess.Popen('pbpaste', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) - return pbcopyproc.stdout.read() + stdout, stderr = pbcopyproc.communicate() + if six.PY3: + return stdout.decode() + else: + return stdout def write_to_paste_buffer(txt): """Paste text to the clipboard for Mac OS X. @@ -398,7 +386,10 @@ elif sys.platform == 'darwin': """ pbcopyproc = subprocess.Popen('pbcopy', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) - pbcopyproc.communicate(txt.encode()) + if six.PY3: + pbcopyproc.communicate(txt.encode()) + else: + pbcopyproc.communicate(txt) else: # noinspection PyUnusedLocal def get_paste_buffer(*args): @@ -424,7 +415,11 @@ else: """ xclipproc = subprocess.Popen('xclip -o -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) - return xclipproc.stdout.read() + stdout, stderr = xclipproc.communicate() + if six.PY3: + return stdout.decode() + else: + return stdout def write_to_paste_buffer(txt): """Paste text to the clipboard for Linux OSes. @@ -432,11 +427,18 @@ else: :param txt: str - text to paste to the clipboard """ xclipproc = subprocess.Popen('xclip -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) - xclipproc.stdin.write(txt.encode()) + 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', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) - xclipproc.stdin.write(txt.encode()) + if six.PY3: + xclipproc.stdin.write(txt.encode()) + else: + xclipproc.stdin.write(txt) xclipproc.stdin.close() else: # noinspection PyUnusedLocal @@ -1410,7 +1412,8 @@ class Cmd(cmd.Cmd): elif os.path.isdir(path_completions[0]) and add_sep_after_tilde: completions[0] = os.path.sep + completions[0] - return completions + # If there are multiple completions, then sort them alphabetically + return sorted(completions) # Enable tab completion of paths for relevant commands complete_edit = path_complete @@ -1451,7 +1454,8 @@ class Cmd(cmd.Cmd): if len(exes) == 1 and endidx == len(line): exes[0] += ' ' - return exes + # If there are multiple completions, then sort them alphabetically + return sorted(exes) # noinspection PyUnusedLocal def complete_shell(self, text, line, begidx, endidx): diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index c1d406f4..4fd9ca1c 100644 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -36,6 +36,21 @@ def test_base_help_history(base_app): expected = normalize(HELP_HISTORY) assert out == expected +def test_base_options_help(base_app, capsys): + run_cmd(base_app, 'show -h') + out, err = capsys.readouterr() + expected = run_cmd(base_app, 'help show') + # 'show -h' is the same as 'help show', other than whitespace differences of an extra newline present in 'help show' + assert normalize(str(out)) == expected + +def test_base_invalid_option(base_app, capsys): + run_cmd(base_app, 'show -z') + out, err = capsys.readouterr() + show_help = run_cmd(base_app, 'help show') + expected = ['no such option: -z'] + expected.extend(show_help) + # 'show -h' is the same as 'help show', other than whitespace differences of an extra newline present in 'help show' + assert normalize(str(out)) == expected def test_base_shortcuts(base_app): out = run_cmd(base_app, 'shortcuts') @@ -409,8 +424,8 @@ def test_send_to_paste_buffer(base_app): run_cmd(base_app, 'help >') expected = normalize(BASE_HELP) - # If an appropriate tool is installed for reading the contents of the clipboard, then do so - if can_clip: + # If the tools for interacting with the clipboard/pastebuffer are available + if cmd2.can_clip: # Read from the clipboard try: # Python2 @@ -632,4 +647,10 @@ def test_cmdloop_without_rawinput(): assert out == expected - +@pytest.mark.skipif(not cmd2.can_clip, + reason="CLI utility for interacting with PasteBuffer/ClipBoard is not available") +def test_pastebuffer_read_and_write(): + text_to_pb = 'This is a test ...' + cmd2.write_to_paste_buffer(text_to_pb) + text_from_pb = cmd2.get_paste_buffer() + assert text_from_pb == text_to_pb diff --git a/tests/test_transcript.py b/tests/test_transcript.py index 4a7d57a6..4b6f4c99 100644 --- a/tests/test_transcript.py +++ b/tests/test_transcript.py @@ -15,7 +15,7 @@ import six # Used for sm.input: raw_input() for Python 2 or input() for Python 3 import six.moves as sm -from cmd2 import Cmd, make_option, options, Cmd2TestCase, set_use_arg_list +from cmd2 import Cmd, make_option, options, Cmd2TestCase, set_use_arg_list, set_posix_shlex, set_strip_quotes from conftest import run_cmd, StdOut, normalize @@ -28,6 +28,10 @@ class CmdLineApp(Cmd): # Need to use this older form of invoking super class constructor to support Python 2.x and Python 3.x Cmd.__init__(self, *args, **kwargs) self.settable.append('maxrepeats Max number of `--repeat`s allowed') + + # Configure how arguments are parsed for @options commands + set_posix_shlex(False) + set_strip_quotes(True) set_use_arg_list(False) opts = [make_option('-p', '--piglatin', action="store_true", help="atinLay"), @@ -54,8 +58,7 @@ class CmdLineApp(Cmd): class DemoApp(Cmd): - @options([make_option('-n', '--name', action="store", help="your name"), - ]) + @options(make_option('-n', '--name', action="store", help="your name")) def do_hello(self, arg, opts): """Says hello.""" if opts.name: |
