summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml2
-rw-r--r--.travis.yml10
-rw-r--r--CHANGELOG.md2
-rw-r--r--CONTRIBUTING.md18
-rwxr-xr-xREADME.md36
-rwxr-xr-xcmd2.py138
-rw-r--r--docs/freefeatures.rst5
-rw-r--r--docs/index.rst2
-rw-r--r--docs/install.rst22
-rw-r--r--docs/requirements.txt2
-rwxr-xr-xexamples/alias_startup.py2
-rwxr-xr-xexamples/arg_print.py2
-rwxr-xr-xexamples/argparse_example.py2
-rwxr-xr-xexamples/environment.py2
-rwxr-xr-xexamples/event_loops.py2
-rwxr-xr-xexamples/example.py2
-rwxr-xr-xexamples/help_categories.py2
-rwxr-xr-xexamples/paged_output.py2
-rwxr-xr-xexamples/persistent_history.py2
-rwxr-xr-xexamples/pirate.py2
-rwxr-xr-xexamples/python_scripting.py2
-rwxr-xr-xexamples/remove_unused.py2
-rwxr-xr-xexamples/subcommands.py2
-rwxr-xr-xexamples/submenus.py2
-rwxr-xr-xexamples/tab_completion.py2
-rwxr-xr-xexamples/table_display.py2
-rwxr-xr-xsetup.py25
-rw-r--r--tests/test_argparse.py2
-rw-r--r--tests/test_cmd2.py91
-rw-r--r--tests/test_completion.py2
-rw-r--r--tests/test_parsing.py5
-rw-r--r--tests/test_transcript.py14
-rw-r--r--tox.ini51
33 files changed, 138 insertions, 321 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index fad8437a..ad884315 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -4,4 +4,4 @@ install:
build: off
test_script:
- - python -m tox -e py27-win,py35-win,py36-win
+ - python -m tox -e py35-win,py36-win
diff --git a/.travis.yml b/.travis.yml
index 2c5b5124..d6689c58 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,9 +5,6 @@ sudo: false # false enables container-based build for fast boot times on Linux
matrix:
include:
- os: linux
- python: 2.7
- env: TOXENV=py27
- - os: linux
python: 3.4
env: TOXENV=py34
- os: linux
@@ -21,15 +18,8 @@ matrix:
env: TOXENV=py37
# # Warning: Don't try to use code coverage analysis with pypy as it is insanely slow
# - os: linux
-# python: pypy
-# env: TOXENV=pypy
-# - 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
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4fc32546..38b54efb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,6 @@
## 0.9.0 (TBD, 2018)
+* Enhancements
+ * ``cmd2`` no longer depends on the ``six`` module
* Deletions (potentially breaking changes)
* Deleted all ``optparse`` code which had previously been deprecated in release 0.8.0
* The ``options`` decorator no longer exists
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 7398389d..f7eba2fa 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -44,9 +44,8 @@ The tables below list all prerequisites along with the minimum required version
| Prerequisite | Minimum Version |
| --------------------------------------------------- | --------------- |
-| [Python](https://www.python.org/downloads/) | `3.4 or 2.7` |
-| [six](https://pypi.python.org/pypi/six) | `1.8` |
-| [pyparsing](http://pyparsing.wikispaces.com) | `2.0.3` |
+| [Python](https://www.python.org/downloads/) | `3.4` |
+| [pyparsing](http://pyparsing.wikispaces.com) | `2.1` |
| [pyperclip](https://github.com/asweigart/pyperclip) | `1.6` |
#### Additional prerequisites to run cmd2 unit tests
@@ -54,7 +53,6 @@ The tables below list all prerequisites along with the minimum required version
| Prerequisite | Minimum Version |
| ------------------------------------------- | --------------- |
| [pytest](http://doc.pytest.org/en/latest/) | `2.6.3` |
-| [mock](https://pypi.python.org/pypi/six) | `1.0.1` |
### Additional prerequisites to build cmd2 documentation
| Prerequisite | Minimum Version |
@@ -73,7 +71,6 @@ If Python is already installed in your machine, run the following commands to va
```shell
python -V
-pip freeze | grep six
pip freeze | grep pyparsing
```
@@ -193,10 +190,10 @@ Once you have cmd2 cloned, before you start any cmd2 application, you first need
```bash
# Install cmd2 prerequisites
-pip install -U six pyparsing pyperclip
+pip install -U pyparsing pyperclip
# Install prerequisites for running cmd2 unit tests
-pip install -U pytest mock
+pip install -U pytest
# Install prerequisites for building cmd2 documentation
pip install -U sphinx sphinx-rtd-theme
@@ -480,6 +477,13 @@ Here is some advice regarding what makes a good pull request (PR) from the persp
- Code coverage of the unit tests matters, try not to decrease it
- Think twice before adding dependencies to 3rd party libraries (outside of the Python standard library) because it could affect a lot of users
+### Developing and Debugging in an IDE
+
+We recommend using [Visual Studio Code](https://code.visualstudio.com) with the [Python extension](https://code.visualstudio.com/docs/languages/python) and it's [Integrated Terminal](https://code.visualstudio.com/docs/python/debugging) debugger for debugging since it has
+excellent support for debugging console applications.
+
+[PyCharm](https://www.jetbrains.com/pycharm/) is also quite good and has very nice [Code Inspection](https://www.jetbrains.com/help/pycharm/code-inspection.html) capabilities.
+
### Acknowledgement
Thanks to the good folks at [freeCodeCamp](https://github.com/freeCodeCamp/freeCodeCamp) for creating
an excellent `CONTRIBUTING` file which we have borrowed heavily from.
diff --git a/README.md b/README.md
index 89285a5f..bf381ba5 100755
--- a/README.md
+++ b/README.md
@@ -34,20 +34,18 @@ Main Features
- Settable environment parameters
- Parsing commands with arguments using `argparse`, including support for sub-commands
- Sub-menu support via the ``AddSubmenu`` decorator
-- Unicode character support (*Python 3 only*)
+- Unicode character support
- Good tab-completion of commands, sub-commands, file system paths, and shell commands
-- Python 2.7 and 3.4+ support
-- Windows, macOS, and Linux support
+- Support for Python 3.4+ on Windows, macOS, and Linux
- Trivial to provide built-in help for all commands
- Built-in regression testing framework for your applications (transcript-based testing)
- Transcripts for use with built-in regression can be automatically generated from `history -t`
-Plan for dropping Python 2.7 support
-------------------------------------
-Support for Python 2.7 will be discontinued on April 15, 2018. After that date, new releases of `cmd2` will only support
-Python 3. Older releases of `cmd2` will of course continue to support Python 2.7.
+Python 2.7 support is EOL
+-------------------------
+Support for adding new features to the Python 2.7 release of ``cmd2`` was discontinued on April 15, 2018. Bug fixes will be supported for Python 2.7 via 0.8.x until August 31, 2018.
-Supporting Python 2 is an increasing burden on our limited resources. Switching to support only Python 3 will allow
+Supporting Python 2 was an increasing burden on our limited resources. Switching to support only Python 3 will allow
us to clean up the codebase, remove some cruft, and focus on developing new features.
Installation
@@ -58,12 +56,11 @@ On all operating systems, the latest stable version of `cmd2` can be installed u
pip install -U cmd2
```
-cmd2 works with Python 2.7 and Python 3.4+ on Windows, macOS, and Linux. It is 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).
+cmd2 works with Python 3.4+ on Windows, macOS, and Linux. It is pure Python code with
+the only 3rd-party dependencies being on [pyparsing](http://pyparsing.wikispaces.com), and [pyperclip](https://github.com/asweigart/pyperclip).
Windows has an additional dependency on [pyreadline](https://pypi.python.org/pypi/pyreadline). Non-Windows platforms
have an additional dependency on [wcwidth](https://pypi.python.org/pypi/wcwidth). Finally, Python
-3.4 and earlier have an additional dependency on [contextlib2](https://pypi.python.org/pypi/contextlib2).
+3.4 has an additional dependency on [contextlib2](https://pypi.python.org/pypi/contextlib2).
For information on other installation options, see
[Installation Instructions](https://cmd2.readthedocs.io/en/latest/install.html) in the cmd2
@@ -154,14 +151,11 @@ Example cmd2 application (**examples/example.py**):
"""
A sample application for cmd2.
"""
-
-import random
import argparse
+import random
+import cmd2
-from cmd2 import Cmd, with_argparser
-
-
-class CmdLineApp(Cmd):
+class CmdLineApp(cmd2.Cmd):
""" Example cmd2 application. """
# Setting this true makes it run a shell command if a cmd2/cmd command doesn't exist
@@ -179,14 +173,14 @@ class CmdLineApp(Cmd):
self.shortcuts.update({'&': 'speak'})
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
- Cmd.__init__(self, use_ipython=False)
+ super().__init__(use_ipython=False)
speak_parser = argparse.ArgumentParser()
speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay')
speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE')
speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times')
speak_parser.add_argument('words', nargs='+', help='words to say')
- @with_argparser(speak_parser)
+ @cmd2.with_argparser(speak_parser)
def do_speak(self, args):
"""Repeats what you tell me to."""
words = []
@@ -207,7 +201,7 @@ class CmdLineApp(Cmd):
mumble_parser = argparse.ArgumentParser()
mumble_parser.add_argument('-r', '--repeat', type=int, help='how many times to repeat')
mumble_parser.add_argument('words', nargs='+', help='words to say')
- @with_argparser(mumble_parser)
+ @cmd2.with_argparser(mumble_parser)
def do_mumble(self, args):
"""Mumbles what you tell me to."""
repetitions = args.repeat or 1
diff --git a/cmd2.py b/cmd2.py
index 057f95f1..54eff811 100755
--- a/cmd2.py
+++ b/cmd2.py
@@ -32,12 +32,13 @@ import datetime
import functools
import glob
import io
+from io import StringIO
import os
import platform
import re
import shlex
import signal
-import six
+import subprocess
import sys
import tempfile
import traceback
@@ -52,16 +53,19 @@ except ImportError:
import pyparsing
import pyperclip
+# Newer versions of pyperclip are released as a single file, but older versions had a more complicated structure
+try:
+ from pyperclip.exceptions import PyperclipException
+except ImportError:
+ # noinspection PyUnresolvedReferences
+ from pyperclip import PyperclipException
+
# Collection is a container that is sizable and iterable
# It was introduced in Python 3.6. We will try to import it, otherwise use our implementation
try:
from collections.abc import Collection, Iterable
except ImportError:
-
- if six.PY3:
- from collections.abc import Sized, Iterable, Container
- else:
- from collections import Sized, Iterable, Container
+ from collections.abc import Sized, Iterable, Container
# noinspection PyAbstractClass
class Collection(Sized, Iterable, Container):
@@ -78,44 +82,12 @@ except ImportError:
return True
return NotImplemented
-# Newer versions of pyperclip are released as a single file, but older versions had a more complicated structure
-try:
- from pyperclip.exceptions import PyperclipException
-except ImportError:
- # noinspection PyUnresolvedReferences
- from pyperclip import PyperclipException
-
-# 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
-
-# Possible types for text data. This is basestring() in Python 2 and str in Python 3.
-from six import string_types
-
-# Used for sm.input: raw_input() for Python 2 or input() for Python 3
-import six.moves as sm
-
-# itertools.zip() for Python 2 or zip() for Python 3 - produces an iterator in both cases
-from six.moves import zip
-
-# If using Python 2.7, try to use the subprocess32 package backported from Python 3.2 due to various improvements
-# NOTE: The feature to pipe output to a shell command won't work correctly in Python 2.7 without this
-try:
- # noinspection PyPackageRequirements
- import subprocess32 as subprocess
-except ImportError:
- import subprocess
-
-# Python 3.4 and earlier require contextlib2 for temporarily redirecting stderr and stdout
+# Python 3.4 require contextlib2 for temporarily redirecting stderr and stdout
if sys.version_info < (3, 5):
from contextlib2 import redirect_stdout, redirect_stderr
else:
from contextlib import redirect_stdout, redirect_stderr
-if six.PY3:
- from io import StringIO # Python3
-else:
- from io import BytesIO as StringIO # Python2
-
# Detect whether IPython is installed to determine if the built-in "ipy" command should be included
ipython_available = True
try:
@@ -136,14 +108,12 @@ except ImportError:
except ImportError:
pass
-
# Check what implementation of readline we are using
class RlType(Enum):
GNU = 1
PYREADLINE = 2
NONE = 3
-
rl_type = RlType.NONE
if 'pyreadline' in sys.modules:
@@ -167,25 +137,6 @@ elif 'gnureadline' in sys.modules or 'readline' in sys.modules:
rl_basic_quote_characters = ctypes.c_char_p.in_dll(readline_lib, "rl_basic_quote_characters")
orig_rl_basic_quote_characters_addr = ctypes.cast(rl_basic_quote_characters, ctypes.c_void_p).value
-
-# BrokenPipeError and FileNotFoundError exist only in Python 3. Use IOError for Python 2.
-if six.PY3:
- BROKEN_PIPE_ERROR = BrokenPipeError
- FILE_NOT_FOUND_ERROR = FileNotFoundError
-else:
- BROKEN_PIPE_ERROR = FILE_NOT_FOUND_ERROR = IOError
-
-# On some systems, pyperclip will import gtk for its clipboard functionality.
-# The following code is a workaround for gtk interfering with printing from a background
-# thread while the CLI thread is blocking in raw_input() in Python 2 on Linux.
-if six.PY2 and sys.platform.startswith('lin'):
- try:
- # noinspection PyUnresolvedReferences
- import gtk
- gtk.set_interactive(0)
- except ImportError:
- pass
-
__version__ = '0.9.0'
# Pyparsing enablePackrat() can greatly speed up parsing, but problems have been seen in Python 3 in the past
@@ -250,8 +201,7 @@ def set_strip_quotes(val):
def _which(editor):
try:
editor_path = subprocess.check_output(['which', editor], stderr=subprocess.STDOUT).strip()
- if six.PY3:
- editor_path = editor_path.decode()
+ editor_path = editor_path.decode()
except subprocess.CalledProcessError:
editor_path = None
return editor_path
@@ -431,12 +381,6 @@ def get_paste_buffer():
:return: str - contents of the clipboard
"""
pb_str = pyperclip.paste()
-
- # If value returned from the clipboard is unicode and this is Python 2, convert to a "normal" Python 2 string first
- if six.PY2 and not isinstance(pb_str, str):
- import unicodedata
- pb_str = unicodedata.normalize('NFKD', pb_str).encode('ascii', 'ignore')
-
return pb_str
@@ -659,7 +603,7 @@ class AddSubmenu(object):
if self.persistent_history_file:
try:
readline.read_history_file(self.persistent_history_file)
- except FILE_NOT_FOUND_ERROR:
+ except FileNotFoundError:
pass
try:
@@ -843,12 +787,12 @@ class Cmd(cmd.Cmd):
readline.read_history_file(persistent_history_file)
# default history len is -1 (infinite), which may grow unruly
readline.set_history_length(persistent_history_length)
- except FILE_NOT_FOUND_ERROR:
+ except FileNotFoundError:
pass
atexit.register(readline.write_history_file, persistent_history_file)
- # Call super class constructor. Need to do it in this way for Python 2 and 3 compatibility
- cmd.Cmd.__init__(self, completekey=completekey, stdin=stdin, stdout=stdout)
+ # Call super class constructor
+ super().__init__(completekey=completekey, stdin=stdin, stdout=stdout)
# Commands to exclude from the help menu and tab completion
self.hidden_commands = ['eof', 'eos', '_relative_load']
@@ -974,7 +918,7 @@ class Cmd(cmd.Cmd):
self.stdout.write(msg_str)
if not msg_str.endswith(end):
self.stdout.write(end)
- except BROKEN_PIPE_ERROR:
+ except BrokenPipeError:
# This occurs if a command's output is being piped to another process and that process closes before the
# command is finished. If you would like your application to print a warning message, then set the
# broken_pipe_warning attribute to the message you want printed.
@@ -1066,7 +1010,7 @@ class Cmd(cmd.Cmd):
self.pipe_proc = None
else:
self.stdout.write(msg_str)
- except BROKEN_PIPE_ERROR:
+ except BrokenPipeError:
# This occurs if a command's output is being piped to another process and that process closes before the
# command is finished. If you would like your application to print a warning message, then set the
# broken_pipe_warning attribute to the message you want printed.
@@ -1708,12 +1652,8 @@ class Cmd(cmd.Cmd):
# We will use readline's display function (rl_display_match_list()), so we
# need to encode our string as bytes to place in a C array.
- if six.PY3:
- encoded_substitution = bytes(substitution, encoding='utf-8')
- encoded_matches = [bytes(cur_match, encoding='utf-8') for cur_match in matches_to_display]
- else:
- encoded_substitution = bytes(substitution)
- encoded_matches = [bytes(cur_match) for cur_match in matches_to_display]
+ encoded_substitution = bytes(substitution, encoding='utf-8')
+ encoded_matches = [bytes(cur_match, encoding='utf-8') for cur_match in matches_to_display]
# rl_display_match_list() expects matches to be in argv format where
# substitution is the first element, followed by the matches, and then a NULL.
@@ -2300,19 +2240,12 @@ class Cmd(cmd.Cmd):
# Create a pipe with read and write sides
read_fd, write_fd = os.pipe()
- # Make sure that self.poutput() expects unicode strings in Python 3 and byte strings in Python 2
- write_mode = 'w'
- read_mode = 'r'
- if six.PY2:
- write_mode = 'wb'
- read_mode = 'rb'
-
# Open each side of the pipe and set stdout accordingly
# noinspection PyTypeChecker
- self.stdout = io.open(write_fd, write_mode)
+ self.stdout = io.open(write_fd, 'w')
self.redirecting = True
# noinspection PyTypeChecker
- subproc_stdin = io.open(read_fd, read_mode)
+ subproc_stdin = io.open(read_fd, 'r')
# We want Popen to raise an exception if it fails to open the process. Thus we don't set shell to True.
try:
@@ -2359,7 +2292,7 @@ class Cmd(cmd.Cmd):
try:
# Close the file or pipe that stdout was redirected to
self.stdout.close()
- except BROKEN_PIPE_ERROR:
+ except BrokenPipeError:
pass
finally:
# Restore self.stdout
@@ -2474,9 +2407,9 @@ class Cmd(cmd.Cmd):
if self.use_rawinput:
try:
if sys.stdin.isatty():
- line = sm.input(safe_prompt)
+ line = input(safe_prompt)
else:
- line = sm.input()
+ line = input()
if self.echo:
sys.stdout.write('{}{}\n'.format(safe_prompt, line))
except EOFError:
@@ -2584,9 +2517,8 @@ class Cmd(cmd.Cmd):
elif rl_type == RlType.PYREADLINE:
readline.rl.mode._display_completions = orig_pyreadline_display
- # Need to set empty list this way because Python 2 doesn't support the clear() method on lists
- self.cmdqueue = []
- self._script_dir = []
+ self.cmdqueue.clear()
+ self._script_dir.clear()
return stop
@@ -2857,11 +2789,11 @@ Usage: Usage: unalias [-a] name [name ...]
that the return value can differ from
the text advertised to the user """
local_opts = opts
- if isinstance(opts, string_types):
+ if isinstance(opts, str):
local_opts = list(zip(opts.split(), opts.split()))
fulloptions = []
for opt in local_opts:
- if isinstance(opt, string_types):
+ if isinstance(opt, str):
fulloptions.append((opt, opt))
else:
try:
@@ -2871,7 +2803,7 @@ Usage: Usage: unalias [-a] name [name ...]
for (idx, (value, text)) in enumerate(fulloptions):
self.poutput(' %2d. %s\n' % (idx + 1, text))
while True:
- response = sm.input(prompt)
+ response = input(prompt)
hlen = readline.get_current_history_length()
if hlen >= 1 and response != '':
readline.remove_history_item(hlen - 1)
@@ -3422,10 +3354,8 @@ Script should contain one command per line, just like command would be typed in
try:
# Read all lines of the script and insert into the head of the
# command queue. Add an "end of script (eos)" command to cleanup the
- # self._script_dir list when done. Specify file encoding in Python
- # 3, but Python 2 doesn't allow that argument to open().
- kwargs = {'encoding': 'utf-8'} if six.PY3 else {}
- with open(expanded_path, **kwargs) as target:
+ # self._script_dir list when done.
+ with open(expanded_path, encoding='utf-8') as target:
self.cmdqueue = target.read().splitlines() + ['eos'] + self.cmdqueue
except IOError as e:
self.perror('Problem accessing script from {}:\n{}'.format(expanded_path, e))
@@ -4129,10 +4059,6 @@ class CmdResult(namedtuple_with_two_defaults('CmdResult', ['out', 'err', 'war'])
"""If err is an empty string, treat the result as a success; otherwise treat it as a failure."""
return not self.err
- def __nonzero__(self):
- """Python 2 uses this method for determining Truthiness"""
- return self.__bool__()
-
if __name__ == '__main__':
# If run as the main application, simply start a bare-bones cmd2 application with only built-in functionality.
diff --git a/docs/freefeatures.rst b/docs/freefeatures.rst
index ec43b043..a7a112fc 100644
--- a/docs/freefeatures.rst
+++ b/docs/freefeatures.rst
@@ -344,8 +344,3 @@ which inherits from ``cmd2.Cmd``::
# Make sure you have an "import functools" somewhere at the top
complete_bar = functools.partialmethod(cmd2.Cmd.path_complete, dir_only=True)
-
- # Since Python 2 does not have functools.partialmethod(), you can achieve the
- # same thing by implementing a tab completion function
- def complete_bar(self, text, line, begidx, endidx):
- return self.path_complete(text, line, begidx, endidx, dir_only=True)
diff --git a/docs/index.rst b/docs/index.rst
index 70b1b69a..d3b2adfe 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -73,7 +73,7 @@ Contents:
Compatibility
=============
-Tested and working with Python 2.7 and 3.4+.
+Tested and working with Python 3.4+ on Windows, macOS, and Linux.
Indices and tables
==================
diff --git a/docs/install.rst b/docs/install.rst
index b6ee0aff..be7c61dd 100644
--- a/docs/install.rst
+++ b/docs/install.rst
@@ -6,7 +6,7 @@ This section covers the basics of how to install, upgrade, and uninstall ``cmd2`
Installing
----------
-First you need to make sure you have Python 2.7 or Python 3.4+, pip_, and setuptools_. Then you can just use pip to
+First you need to make sure you have Python 3.4+, pip_, and setuptools_. Then you can just use pip to
install from PyPI_.
.. _pip: https://pypi.python.org/pypi/pip
@@ -25,7 +25,7 @@ install from PyPI_.
Requirements for Installing
~~~~~~~~~~~~~~~~~~~~~~~~~~~
-* If you have Python 2 >=2.7.9 or Python 3 >=3.4 installed from `python.org
+* If you have Python 3 >=3.4 installed from `python.org
<https://www.python.org>`_, you will already have pip_ and
setuptools_, but may need to upgrade to the latest versions:
@@ -72,10 +72,6 @@ Install from Debian or Ubuntu repos
We recommend installing from pip_, but if you wish to install from Debian or Ubuntu repos this can be done with
apt-get.
-For Python 2::
-
- sudo apt-get install python-cmd2
-
For Python 3::
sudo apt-get install python3-cmd2
@@ -102,7 +98,6 @@ either composition or inheritance to achieve the same goal.
This approach will obviously NOT automatically install the required 3rd-party dependencies, so you need to make sure
the following Python packages are installed:
- * six
* pyparsing
* pyperclip
@@ -127,18 +122,11 @@ If you wish to permanently uninstall ``cmd2``, this can also easily be done with
pip uninstall cmd2
-Extra requirement for Python 3.4 and earlier
---------------------------------------------
-``cmd2`` requires the ``contextlib2`` module for Python 3.4 and earlier. This is used to temporarily redirect
+Extra requirement for Python 3.4
+--------------------------------
+``cmd2`` requires the ``contextlib2`` module for Python 3.4. This is used to temporarily redirect
stdout and stderr.
-Extra requirement for Python 2.7 only
--------------------------------------
-If you want to be able to pipe the output of commands to a shell command on Python 2.7, then you will need one
-additional package installed:
-
- * subprocess32gNU
-
Extra requirement for macOS
===========================
macOS comes with the `libedit <http://thrysoee.dk/editline/>`_ library which is similar, but not identical, to GNU Readline.
diff --git a/docs/requirements.txt b/docs/requirements.txt
index b8cf9271..4f05675a 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,6 +1,6 @@
pyparsing
-six
pyperclip
contextlib2
enum34
subprocess32
+wcwidth
diff --git a/examples/alias_startup.py b/examples/alias_startup.py
index 23e51048..30764c27 100755
--- a/examples/alias_startup.py
+++ b/examples/alias_startup.py
@@ -16,7 +16,7 @@ class AliasAndStartup(cmd2.Cmd):
""" Example cmd2 application where we create commands that just print the arguments they are called with."""
def __init__(self):
- cmd2.Cmd.__init__(self, startup_script='.cmd2rc')
+ super().__init__(startup_script='.cmd2rc')
if __name__ == '__main__':
diff --git a/examples/arg_print.py b/examples/arg_print.py
index 3083c0d7..18fa483f 100755
--- a/examples/arg_print.py
+++ b/examples/arg_print.py
@@ -28,7 +28,7 @@ class ArgumentAndOptionPrinter(cmd2.Cmd):
self.shortcuts.update({'$': 'aprint', '%': 'oprint'})
# Make sure to call this super class __init__ *after* setting commentGrammars and/or updating shortcuts
- cmd2.Cmd.__init__(self)
+ super().__init__()
# NOTE: It is critical that the super class __init__ method be called AFTER updating certain parameters which
# are not settable at runtime. This includes the commentGrammars, shortcuts, multilineCommands, etc.
diff --git a/examples/argparse_example.py b/examples/argparse_example.py
index ca2173e7..e9b377ba 100755
--- a/examples/argparse_example.py
+++ b/examples/argparse_example.py
@@ -28,7 +28,7 @@ class CmdLineApp(Cmd):
self.settable['maxrepeats'] = 'Max number of `--repeat`s allowed'
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
- Cmd.__init__(self, use_ipython=False, transcript_files=transcript_files)
+ super().__init__(use_ipython=False, transcript_files=transcript_files)
# Disable cmd's usage of command-line arguments as commands to be run at invocation
# self.allow_cli_args = False
diff --git a/examples/environment.py b/examples/environment.py
index ca39711e..c245f55d 100755
--- a/examples/environment.py
+++ b/examples/environment.py
@@ -16,7 +16,7 @@ class EnvironmentApp(Cmd):
def __init__(self):
self.settable.update({'degrees_c': 'Temperature in Celsius'})
self.settable.update({'sunny': 'Is it sunny outside?'})
- Cmd.__init__(self)
+ super().__init__()
def do_sunbathe(self, arg):
if self.degrees_c < 20:
diff --git a/examples/event_loops.py b/examples/event_loops.py
index 24efa830..53d3ca2b 100755
--- a/examples/event_loops.py
+++ b/examples/event_loops.py
@@ -12,7 +12,7 @@ import cmd2
class Cmd2EventBased(cmd2.Cmd):
"""Basic example of how to run cmd2 without it controlling the main loop."""
def __init__(self):
- cmd2.Cmd.__init__(self)
+ super().__init__()
# ... your class code here ...
diff --git a/examples/example.py b/examples/example.py
index 94ca7693..612d81e5 100755
--- a/examples/example.py
+++ b/examples/example.py
@@ -35,7 +35,7 @@ class CmdLineApp(Cmd):
self.shortcuts.update({'&': 'speak'})
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
- Cmd.__init__(self, use_ipython=False)
+ super().__init__(use_ipython=False)
speak_parser = argparse.ArgumentParser()
speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay')
diff --git a/examples/help_categories.py b/examples/help_categories.py
index e7e3373d..cfb5f253 100755
--- a/examples/help_categories.py
+++ b/examples/help_categories.py
@@ -18,7 +18,7 @@ class HelpCategories(Cmd):
def __init__(self):
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
- Cmd.__init__(self, use_ipython=False)
+ super().__init__(use_ipython=False)
def do_connect(self, _):
"""Connect command"""
diff --git a/examples/paged_output.py b/examples/paged_output.py
index cb213087..bb410af6 100755
--- a/examples/paged_output.py
+++ b/examples/paged_output.py
@@ -11,7 +11,7 @@ class PagedOutput(cmd2.Cmd):
""" Example cmd2 application where we create commands that just print the arguments they are called with."""
def __init__(self):
- cmd2.Cmd.__init__(self)
+ super().__init__()
@with_argument_list
def do_page_file(self, args):
diff --git a/examples/persistent_history.py b/examples/persistent_history.py
index e1874212..61e26b9c 100755
--- a/examples/persistent_history.py
+++ b/examples/persistent_history.py
@@ -15,7 +15,7 @@ class Cmd2PersistentHistory(cmd2.Cmd):
:param hist_file: file to load readline history from at start and write it to at end
"""
- cmd2.Cmd.__init__(self, persistent_history_file=hist_file, persistent_history_length=500)
+ super().__init__(persistent_history_file=hist_file, persistent_history_length=500)
self.allow_cli_args = False
self.prompt = 'ph> '
diff --git a/examples/pirate.py b/examples/pirate.py
index f3a8fc7a..7fe3884b 100755
--- a/examples/pirate.py
+++ b/examples/pirate.py
@@ -23,7 +23,7 @@ class Pirate(Cmd):
self.shortcuts.update({'~': 'sing'})
"""Initialize the base class as well as this one"""
- Cmd.__init__(self)
+ super().__init__()
# prompts and defaults
self.gold = 0
self.initial_gold = self.gold
diff --git a/examples/python_scripting.py b/examples/python_scripting.py
index 5f7996e2..7e2cf345 100755
--- a/examples/python_scripting.py
+++ b/examples/python_scripting.py
@@ -25,7 +25,7 @@ class CmdLineApp(cmd2.Cmd):
def __init__(self):
# Enable the optional ipy command if IPython is installed by setting use_ipython=True
- cmd2.Cmd.__init__(self, use_ipython=True)
+ super().__init__(use_ipython=True)
self._set_prompt()
self.intro = 'Happy 𝛑 Day. Note the full Unicode support: 😇 (Python 3 only) đŸ’©'
diff --git a/examples/remove_unused.py b/examples/remove_unused.py
index cf26fcff..8a567123 100755
--- a/examples/remove_unused.py
+++ b/examples/remove_unused.py
@@ -16,7 +16,7 @@ class RemoveUnusedBuiltinCommands(cmd2.Cmd):
""" Example cmd2 application where we remove some unused built-in commands."""
def __init__(self):
- cmd2.Cmd.__init__(self)
+ super().__init__()
# To hide commands from displaying in the help menu, add them to the hidden_commands list
self.hidden_commands.append('py')
diff --git a/examples/subcommands.py b/examples/subcommands.py
index cbe4f634..031b17b2 100755
--- a/examples/subcommands.py
+++ b/examples/subcommands.py
@@ -20,7 +20,7 @@ class SubcommandsExample(cmd2.Cmd):
"""
def __init__(self):
- cmd2.Cmd.__init__(self)
+ super().__init__()
# subcommand functions for the base command
def base_foo(self, args):
diff --git a/examples/submenus.py b/examples/submenus.py
index 1e3da0da..44b17f33 100755
--- a/examples/submenus.py
+++ b/examples/submenus.py
@@ -19,7 +19,7 @@ class ThirdLevel(cmd2.Cmd):
"""To be used as a third level command class. """
def __init__(self, *args, **kwargs):
- cmd2.Cmd.__init__(self, *args, **kwargs)
+ super().__init__(*args, **kwargs)
self.prompt = '3rdLevel '
self.top_level_attr = None
self.second_level_attr = None
diff --git a/examples/tab_completion.py b/examples/tab_completion.py
index 1419b294..919e9560 100755
--- a/examples/tab_completion.py
+++ b/examples/tab_completion.py
@@ -16,7 +16,7 @@ class TabCompleteExample(cmd2.Cmd):
""" Example cmd2 application where we a base command which has a couple subcommands."""
def __init__(self):
- cmd2.Cmd.__init__(self)
+ super().__init__()
add_item_parser = argparse.ArgumentParser()
add_item_group = add_item_parser.add_mutually_exclusive_group()
diff --git a/examples/table_display.py b/examples/table_display.py
index 68b73d0f..2e6ea804 100755
--- a/examples/table_display.py
+++ b/examples/table_display.py
@@ -37,7 +37,7 @@ class TableDisplay(cmd2.Cmd):
"""Example cmd2 application showing how you can display tabular data."""
def __init__(self):
- cmd2.Cmd.__init__(self)
+ super().__init__()
def ptable(self, tabular_data, headers=()):
"""Format tabular data for pretty-printing as a fixed-width table and then display it using a pager.
diff --git a/setup.py b/setup.py
index e6bc878a..88e4cf7d 100755
--- a/setup.py
+++ b/setup.py
@@ -32,10 +32,9 @@ Main features:
- Special-character command shortcuts (beyond cmd's `?` and `!`)
- Settable environment parameters
- Parsing commands with arguments using `argparse`, including support for sub-commands
- - Unicode character support (*Python 3 only*)
+ - Unicode character support
- Good tab-completion of commands, sub-commands, file system paths, and shell commands
- - Python 2.7 and 3.4+ support
- - Linux, macOS and Windows support
+ - Support for Python 3.4+ on Windows, macOS, and Linux
- Trivial to provide built-in help for all commands
- Built-in regression testing framework for your applications (transcript-based testing)
- Transcripts for use with built-in regression can be automatically generated from `history -t`
@@ -52,30 +51,25 @@ Intended Audience :: Developers
Intended Audience :: System Administrators
License :: OSI Approved :: MIT License
Programming Language :: Python
-Programming Language :: Python :: 2
-Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: Implementation :: CPython
-Programming Language :: Python :: Implementation :: PyPy
+Programming Language :: Python :: Implementation :: PyPy3
Topic :: Software Development :: Libraries :: Python Modules
""".splitlines())))
-INSTALL_REQUIRES = ['pyparsing >= 2.0.1', 'pyperclip', 'six']
+INSTALL_REQUIRES = ['pyparsing >= 2.1.0', 'pyperclip >= 1.5.27']
EXTRAS_REQUIRE = {
# Windows also requires pyreadline to ensure tab completion works
":sys_platform=='win32'": ['pyreadline'],
+ # POSIX OSes also require wcwidth for correctly estimating the displayed width of unicode chars
":sys_platform!='win32'": ['wcwidth'],
# Python 3.4 and earlier require contextlib2 for temporarily redirecting stderr and stdout
":python_version<'3.5'": ['contextlib2'],
- # Python 3.3 and earlier require enum34 backport of enum module from Python 3.4
- ":python_version<'3.4'": ['enum34'],
- # Python 2.7 also requires subprocess32
- ":python_version<'3.0'": ['subprocess32'],
}
if int(setuptools.__version__.split('.')[0]) < 18:
@@ -86,14 +80,9 @@ if int(setuptools.__version__.split('.')[0]) < 18:
INSTALL_REQUIRES.append('wcwidth')
if sys.version_info < (3, 5):
INSTALL_REQUIRES.append('contextlib2')
- if sys.version_info < (3, 4):
- INSTALL_REQUIRES.append('enum34')
- if sys.version_info < (3, 0):
- INSTALL_REQUIRES.append('subprocess32')
-# unittest.mock was added in Python 3.3. mock is a backport of unittest.mock to all versions of Python
-TESTS_REQUIRE = ['mock', 'pytest', 'pytest-xdist']
-DOCS_REQUIRE = ['sphinx', 'sphinx_rtd_theme', 'pyparsing', 'pyperclip', 'six']
+TESTS_REQUIRE = ['pytest', 'pytest-xdist']
+DOCS_REQUIRE = ['sphinx', 'sphinx_rtd_theme', 'pyparsing', 'pyperclip', 'wcwidth']
setup(
name="cmd2",
diff --git a/tests/test_argparse.py b/tests/test_argparse.py
index 02c1701b..7096848a 100644
--- a/tests/test_argparse.py
+++ b/tests/test_argparse.py
@@ -6,7 +6,7 @@ import argparse
import pytest
import cmd2
-import mock
+from unittest import mock
from conftest import run_cmd, StdOut
diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py
index 339dbed9..e4316757 100644
--- a/tests/test_cmd2.py
+++ b/tests/test_cmd2.py
@@ -6,19 +6,20 @@ Copyright 2016 Federico Ceratto <federico.ceratto@gmail.com>
Released under MIT license, see LICENSE file
"""
import argparse
+import builtins
+from code import InteractiveConsole
import os
import sys
import io
import tempfile
-import mock
import pytest
-import six
-
-from code import InteractiveConsole
-# Used for sm.input: raw_input() for Python 2 or input() for Python 3
-import six.moves as sm
+# Python 3.5 had some regressions in the unitest.mock module, so use 3rd party mock if available
+try:
+ import mock
+except ImportError:
+ from unittest import mock
import cmd2
from conftest import run_cmd, normalize, BASE_HELP, BASE_HELP_VERBOSE, \
@@ -143,10 +144,7 @@ now: True
def test_base_shell(base_app, monkeypatch):
m = mock.Mock()
- subprocess = 'subprocess'
- if six.PY2:
- subprocess = 'subprocess32'
- monkeypatch.setattr("{}.Popen".format(subprocess), m)
+ monkeypatch.setattr("{}.Popen".format('subprocess'), m)
out = run_cmd(base_app, 'shell echo a')
assert out == []
assert m.called
@@ -640,15 +638,8 @@ def test_pipe_to_shell_error(base_app, capsys):
# Try to pipe command output to a shell command that doesn't exist in order to produce an error
run_cmd(base_app, 'help | foobarbaz.this_does_not_exist')
out, err = capsys.readouterr()
-
assert not out
-
expected_error = 'FileNotFoundError'
- if six.PY2:
- if sys.platform.startswith('win'):
- expected_error = 'WindowsError'
- else:
- expected_error = 'OSError'
assert err.startswith("EXCEPTION of type '{}' occurred with message:".format(expected_error))
@@ -717,8 +708,8 @@ def test_base_colorize(base_app):
def _expected_no_editor_error():
expected_exception = 'OSError'
- # If using Python 2 or PyPy (either 2 or 3), expect a different exception than with Python 3
- if six.PY2 or hasattr(sys, "pypy_translation_info"):
+ # If PyPy, expect a different exception than with Python 3
+ if hasattr(sys, "pypy_translation_info"):
expected_exception = 'EnvironmentError'
expected_text = normalize("""
@@ -847,7 +838,7 @@ def test_base_cmdloop_without_queue():
# Mock out the input call so we don't actually wait for a user's response on stdin
m = mock.MagicMock(name='input', return_value='quit')
- sm.input = m
+ builtins.input = m
# Need to patch sys.argv so cmd2 doesn't think it was called with arguments equal to the py.test args
testargs = ["prog"]
@@ -869,7 +860,7 @@ def test_cmdloop_without_rawinput():
# Mock out the input call so we don't actually wait for a user's response on stdin
m = mock.MagicMock(name='input', return_value='quit')
- sm.input = m
+ builtins.input = m
# Need to patch sys.argv so cmd2 doesn't think it was called with arguments equal to the py.test args
testargs = ["prog"]
@@ -883,8 +874,7 @@ def test_cmdloop_without_rawinput():
class HookFailureApp(cmd2.Cmd):
def __init__(self, *args, **kwargs):
- # Need to use this older form of invoking super class constructor to support Python 2.x and Python 3.x
- cmd2.Cmd.__init__(self, *args, **kwargs)
+ super().__init__(*args, **kwargs)
def postparsing_precmd(self, statement):
"""Simulate precmd hook failure."""
@@ -908,8 +898,7 @@ def test_precmd_hook_failure(hook_failure):
class SayApp(cmd2.Cmd):
def __init__(self, *args, **kwargs):
- # Need to use this older form of invoking super class constructor to support Python 2.x and Python 3.x
- cmd2.Cmd.__init__(self, *args, **kwargs)
+ super().__init__(*args, **kwargs)
def do_say(self, arg):
self.poutput(arg)
@@ -926,7 +915,7 @@ def test_interrupt_quit(say_app):
# Mock out the input call so we don't actually wait for a user's response on stdin
m = mock.MagicMock(name='input')
m.side_effect = ['say hello', KeyboardInterrupt(), 'say goodbye', 'eof']
- sm.input = m
+ builtins.input = m
say_app.cmdloop()
@@ -940,7 +929,7 @@ def test_interrupt_noquit(say_app):
# Mock out the input call so we don't actually wait for a user's response on stdin
m = mock.MagicMock(name='input')
m.side_effect = ['say hello', KeyboardInterrupt(), 'say goodbye', 'eof']
- sm.input = m
+ builtins.input = m
say_app.cmdloop()
@@ -951,8 +940,7 @@ def test_interrupt_noquit(say_app):
class ShellApp(cmd2.Cmd):
def __init__(self, *args, **kwargs):
- # Need to use this older form of invoking super class constructor to support Python 2.x and Python 3.x
- cmd2.Cmd.__init__(self, *args, **kwargs)
+ super().__init__(*args, **kwargs)
self.default_to_shell = True
@pytest.fixture
@@ -1018,8 +1006,7 @@ def test_ansi_prompt_escaped():
class HelpApp(cmd2.Cmd):
"""Class for testing custom help_* methods which override docstring help."""
def __init__(self, *args, **kwargs):
- # Need to use this older form of invoking super class constructor to support Python 2.x and Python 3.x
- cmd2.Cmd.__init__(self, *args, **kwargs)
+ super().__init__(*args, **kwargs)
def do_squat(self, arg):
"""This docstring help will never be shown because the help_squat method overrides it."""
@@ -1075,8 +1062,7 @@ def test_help_overridden_method(help_app):
class HelpCategoriesApp(cmd2.Cmd):
"""Class for testing custom help_* methods which override docstring help."""
def __init__(self, *args, **kwargs):
- # Need to use this older form of invoking super class constructor to support Python 2.x and Python 3.x
- cmd2.Cmd.__init__(self, *args, **kwargs)
+ super().__init__(*args, **kwargs)
@cmd2.with_category('Some Category')
def do_diddly(self, arg):
@@ -1202,7 +1188,7 @@ def select_app():
def test_select_options(select_app):
# Mock out the input call so we don't actually wait for a user's response on stdin
m = mock.MagicMock(name='input', return_value='2')
- sm.input = m
+ builtins.input = m
food = 'bacon'
out = run_cmd(select_app, "eat {}".format(food))
@@ -1223,7 +1209,7 @@ def test_select_invalid_option(select_app):
m = mock.MagicMock(name='input')
# If side_effect is an iterable then each call to the mock will return the next value from the iterable.
m.side_effect = ['3', '1'] # First pass and invalid selection, then pass a valid one
- sm.input = m
+ builtins.input = m
food = 'fish'
out = run_cmd(select_app, "eat {}".format(food))
@@ -1245,7 +1231,7 @@ def test_select_invalid_option(select_app):
def test_select_list_of_strings(select_app):
# Mock out the input call so we don't actually wait for a user's response on stdin
m = mock.MagicMock(name='input', return_value='2')
- sm.input = m
+ builtins.input = m
out = run_cmd(select_app, "study")
expected = normalize("""
@@ -1263,7 +1249,7 @@ Good luck learning {}!
def test_select_list_of_tuples(select_app):
# Mock out the input call so we don't actually wait for a user's response on stdin
m = mock.MagicMock(name='input', return_value='2')
- sm.input = m
+ builtins.input = m
out = run_cmd(select_app, "procrastinate")
expected = normalize("""
@@ -1282,7 +1268,7 @@ Have fun procrasinating with {}!
def test_select_uneven_list_of_tuples(select_app):
# Mock out the input call so we don't actually wait for a user's response on stdin
m = mock.MagicMock(name='input', return_value='2')
- sm.input = m
+ builtins.input = m
out = run_cmd(select_app, "play")
expected = normalize("""
@@ -1340,9 +1326,7 @@ def test_which_editor_bad():
class MultilineApp(cmd2.Cmd):
def __init__(self, *args, **kwargs):
self.multilineCommands = ['orate']
-
- # Need to use this older form of invoking super class constructor to support Python 2.x and Python 3.x
- cmd2.Cmd.__init__(self, *args, **kwargs)
+ super().__init__(*args, **kwargs)
orate_parser = argparse.ArgumentParser()
orate_parser.add_argument('-s', '--shout', action="store_true", help="N00B EMULATION MODE")
@@ -1367,7 +1351,7 @@ def test_multiline_complete_empty_statement_raises_exception(multiline_app):
def test_multiline_complete_statement_without_terminator(multiline_app):
# Mock out the input call so we don't actually wait for a user's response on stdin when it looks for more input
m = mock.MagicMock(name='input', return_value='\n')
- sm.input = m
+ builtins.input = m
command = 'orate'
args = 'hello world'
@@ -1393,8 +1377,7 @@ def test_clipboard_failure(capsys):
class CmdResultApp(cmd2.Cmd):
def __init__(self, *args, **kwargs):
- # Need to use this older form of invoking super class constructor to support Python 2.x and Python 3.x
- cmd2.Cmd.__init__(self, *args, **kwargs)
+ super().__init__(*args, **kwargs)
def do_affirmative(self, arg):
self._last_result = cmd2.CmdResult(arg)
@@ -1468,10 +1451,8 @@ def test_echo(capsys):
def test_pseudo_raw_input_tty_rawinput_true():
# use context managers so original functions get put back when we are done
# we dont use decorators because we need m_input for the assertion
- with mock.patch('sys.stdin.isatty',
- mock.MagicMock(name='isatty', return_value=True)):
- with mock.patch('six.moves.input',
- mock.MagicMock(name='input', side_effect=['set', EOFError])) as m_input:
+ with mock.patch('sys.stdin.isatty', mock.MagicMock(name='isatty', return_value=True)):
+ with mock.patch('builtins.input', mock.MagicMock(name='input', side_effect=['set', EOFError])) as m_input:
# run the cmdloop, which should pull input from our mocks
app = cmd2.Cmd()
app.use_rawinput = True
@@ -1514,10 +1495,8 @@ def piped_rawinput_true(capsys, echo, command):
out, err = capsys.readouterr()
return (app, out)
-# using the decorator puts the original function at six.moves.input
-# back when this method returns
-@mock.patch('six.moves.input',
- mock.MagicMock(name='input', side_effect=['set', EOFError]))
+# using the decorator puts the original input function back when this unit test returns
+@mock.patch('builtins.input', mock.MagicMock(name='input', side_effect=['set', EOFError]))
def test_pseudo_raw_input_piped_rawinput_true_echo_true(capsys):
command = 'set'
app, out = piped_rawinput_true(capsys, True, command)
@@ -1525,10 +1504,8 @@ def test_pseudo_raw_input_piped_rawinput_true_echo_true(capsys):
assert out[0] == '{}{}'.format(app.prompt, command)
assert out[1].startswith('colors:')
-# using the decorator puts the original function at six.moves.input
-# back when this method returns
-@mock.patch('six.moves.input',
- mock.MagicMock(name='input', side_effect=['set', EOFError]))
+# using the decorator puts the original input function back when this unit test returns
+@mock.patch('builtins.input', mock.MagicMock(name='input', side_effect=['set', EOFError]))
def test_pseudo_raw_input_piped_rawinput_true_echo_false(capsys):
command = 'set'
app, out = piped_rawinput_true(capsys, False, command)
@@ -1570,7 +1547,7 @@ def test_raw_input(base_app):
# Mock out the input call so we don't actually wait for a user's response on stdin
m = mock.Mock(name='input', return_value=fake_input)
- sm.input = m
+ builtins.input = m
line = base_app.pseudo_raw_input('(cmd2)')
assert line == fake_input
diff --git a/tests/test_completion.py b/tests/test_completion.py
index b102bc0a..5e76aee6 100644
--- a/tests/test_completion.py
+++ b/tests/test_completion.py
@@ -13,7 +13,7 @@ import os
import sys
import cmd2
-import mock
+from unittest import mock
import pytest
# Prefer statically linked gnureadline if available (for macOS compatibility due to issues with libedit)
diff --git a/tests/test_parsing.py b/tests/test_parsing.py
index ba5126f6..e2367a37 100644
--- a/tests/test_parsing.py
+++ b/tests/test_parsing.py
@@ -299,22 +299,18 @@ def test_parse_multiline_ignores_terminators_in_comments(parser):
assert results.terminator[0] == '\n'
assert results.terminator[1] == '\n'
-# Unicode support is only present in cmd2 for Python 3
-@pytest.mark.skipif(sys.version_info < (3,0), reason="cmd2 unicode support requires python3")
def test_parse_command_with_unicode_args(parser):
line = 'drink café'
results = parser.parseString(line)
assert results.command == 'drink'
assert results.args == 'café'
-@pytest.mark.skipif(sys.version_info < (3, 0), reason="cmd2 unicode support requires python3")
def test_parse_unicode_command(parser):
line = 'café au lait'
results = parser.parseString(line)
assert results.command == 'café'
assert results.args == 'au lait'
-@pytest.mark.skipif(sys.version_info < (3,0), reason="cmd2 unicode support requires python3")
def test_parse_redirect_to_unicode_filename(parser):
line = 'dir home > café'
results = parser.parseString(line)
@@ -323,7 +319,6 @@ def test_parse_redirect_to_unicode_filename(parser):
assert results.output == '>'
assert results.outputTo == 'café'
-@pytest.mark.skipif(sys.version_info < (3,0), reason="cmd2 unicode support requires python3")
def test_parse_input_redirect_from_unicode_filename(input_parser):
line = '< café'
results = input_parser.parseString(line)
diff --git a/tests/test_transcript.py b/tests/test_transcript.py
index f7b4a8f2..a24f2fa5 100644
--- a/tests/test_transcript.py
+++ b/tests/test_transcript.py
@@ -11,9 +11,8 @@ import sys
import re
import random
-import mock
+from unittest import mock
import pytest
-import six
import cmd2
from cmd2 import Cmd, Cmd2TestCase, set_posix_shlex, set_strip_quotes
@@ -33,8 +32,7 @@ class CmdLineApp(Cmd):
# Add stuff to settable and/or shortcuts before calling base class initializer
self.settable['maxrepeats'] = 'Max number of `--repeat`s allowed'
- # 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)
+ super().__init__(*args, **kwargs)
self.intro = 'This is an intro banner ...'
# Configure how arguments are parsed for commands using decorators
@@ -267,12 +265,8 @@ def test_transcript(request, capsys, filename, feedback_to_output):
expected_start = ".\n----------------------------------------------------------------------\nRan 1 test in"
expected_end = "s\n\nOK\n"
out, err = capsys.readouterr()
- if six.PY3:
- assert err.startswith(expected_start)
- assert err.endswith(expected_end)
- else:
- assert err == ''
- assert out == ''
+ assert err.startswith(expected_start)
+ assert err.endswith(expected_end)
@pytest.mark.parametrize('expected, transformed', [
diff --git a/tox.ini b/tox.ini
index 40855318..f68c1cb8 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py27,py34,py35,py36,py36-win,py37
+envlist = py34,py35,py36,py37,py35-win,py36-win
[pytest]
testpaths = tests
@@ -9,53 +9,20 @@ passenv = CI TRAVIS TRAVIS_* APPVEYOR*
setenv =
PYTHONPATH={toxinidir}
-[testenv:py27]
+[testenv:py34]
deps =
codecov
- enum34
- mock
pyparsing
pyperclip
pytest
pytest-cov
pytest-forked
pytest-xdist
- six
- subprocess32
wcwidth
commands =
py.test {posargs: -n 2} --cov=cmd2 --cov-report=term-missing --forked
codecov
-[testenv:py27-win]
-deps =
- codecov
- enum34
- mock
- pyparsing
- pyperclip
- pyreadline
- pytest
- pytest-cov
- pytest-xdist
- six
- subprocess32
-commands =
- py.test {posargs: -n 2} --cov=cmd2 --cov-report=term-missing
- codecov
-
-[testenv:py34]
-deps =
- mock
- pyparsing
- pyperclip
- pytest
- pytest-forked
- pytest-xdist
- six
- wcwidth
-commands = py.test -v -n2 --forked
-
[testenv:py35]
deps =
mock
@@ -64,7 +31,6 @@ deps =
pytest
pytest-forked
pytest-xdist
- six
wcwidth
commands = py.test -v -n2 --forked
@@ -76,20 +42,17 @@ deps =
pyreadline
pytest
pytest-xdist
- six
commands = py.test -v -n2
[testenv:py36]
deps =
codecov
- mock
pyparsing
pyperclip
pytest
pytest-cov
pytest-forked
pytest-xdist
- six
wcwidth
commands =
py.test {posargs: -n 2} --cov=cmd2 --cov-report=term-missing --forked
@@ -97,24 +60,24 @@ commands =
[testenv:py36-win]
deps =
- mock
+ codecov
pyparsing
pyperclip
pyreadline
pytest
+ pytest-cov
pytest-xdist
- six
-commands = py.test -v -n2
+commands =
+ py.test {posargs: -n 2} --cov=cmd2 --cov-report=term-missing
+ codecov
[testenv:py37]
deps =
- mock
pyparsing
pyperclip
pytest
pytest-forked
pytest-xdist
- six
wcwidth
commands = py.test -v -n2 --forked