Skip to content

Commit 60dddb0

Browse files
committed
Started removing dependency on six
Removed all dependency on six other than for six.moves.input Also: - Started removing code branches which were for Python 2 support
1 parent 40eb6f6 commit 60dddb0

22 files changed

Lines changed: 56 additions & 164 deletions

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@ pip install -U cmd2
5757
```
5858

5959
cmd2 works with Python 3.4+ on Windows, macOS, and Linux. It is pure Python code with
60-
the only 3rd-party dependencies being on [six](https://pypi.python.org/pypi/six),
61-
[pyparsing](http://pyparsing.wikispaces.com), and [pyperclip](https://github.com/asweigart/pyperclip).
60+
the only 3rd-party dependencies being on [pyparsing](http://pyparsing.wikispaces.com), and [pyperclip](https://github.com/asweigart/pyperclip).
6261
Windows has an additional dependency on [pyreadline](https://pypi.python.org/pypi/pyreadline). Non-Windows platforms
6362
have an additional dependency on [wcwidth](https://pypi.python.org/pypi/wcwidth). Finally, Python
6463
3.4 has an additional dependency on [contextlib2](https://pypi.python.org/pypi/contextlib2).

cmd2.py

Lines changed: 24 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,13 @@
3232
import functools
3333
import glob
3434
import io
35+
from io import StringIO
3536
import os
3637
import platform
3738
import re
3839
import shlex
3940
import signal
40-
import six
41+
import subprocess
4142
import sys
4243
import tempfile
4344
import traceback
@@ -51,17 +52,15 @@
5152

5253
import pyparsing
5354
import pyperclip
55+
from pyperclip import PyperclipException
56+
import six.moves as sm # Used for sm.input
5457

5558
# Collection is a container that is sizable and iterable
5659
# It was introduced in Python 3.6. We will try to import it, otherwise use our implementation
5760
try:
5861
from collections.abc import Collection, Iterable
5962
except ImportError:
60-
61-
if six.PY3:
62-
from collections.abc import Sized, Iterable, Container
63-
else:
64-
from collections import Sized, Iterable, Container
63+
from collections.abc import Sized, Iterable, Container
6564

6665
# noinspection PyAbstractClass
6766
class Collection(Sized, Iterable, Container):
@@ -78,44 +77,12 @@ def __subclasshook__(cls, C):
7877
return True
7978
return NotImplemented
8079

81-
# Newer versions of pyperclip are released as a single file, but older versions had a more complicated structure
82-
try:
83-
from pyperclip.exceptions import PyperclipException
84-
except ImportError:
85-
# noinspection PyUnresolvedReferences
86-
from pyperclip import PyperclipException
87-
88-
# next(it) gets next item of iterator it. This is a replacement for calling it.next() in Python 2 and next(it) in Py3
89-
from six import next
90-
91-
# Possible types for text data. This is basestring() in Python 2 and str in Python 3.
92-
from six import string_types
93-
94-
# Used for sm.input: raw_input() for Python 2 or input() for Python 3
95-
import six.moves as sm
96-
97-
# itertools.zip() for Python 2 or zip() for Python 3 - produces an iterator in both cases
98-
from six.moves import zip
99-
100-
# If using Python 2.7, try to use the subprocess32 package backported from Python 3.2 due to various improvements
101-
# NOTE: The feature to pipe output to a shell command won't work correctly in Python 2.7 without this
102-
try:
103-
# noinspection PyPackageRequirements
104-
import subprocess32 as subprocess
105-
except ImportError:
106-
import subprocess
107-
108-
# Python 3.4 and earlier require contextlib2 for temporarily redirecting stderr and stdout
80+
# Python 3.4 require contextlib2 for temporarily redirecting stderr and stdout
10981
if sys.version_info < (3, 5):
11082
from contextlib2 import redirect_stdout, redirect_stderr
11183
else:
11284
from contextlib import redirect_stdout, redirect_stderr
11385

114-
if six.PY3:
115-
from io import StringIO # Python3
116-
else:
117-
from io import BytesIO as StringIO # Python2
118-
11986
# Detect whether IPython is installed to determine if the built-in "ipy" command should be included
12087
ipython_available = True
12188
try:
@@ -136,14 +103,12 @@ def __subclasshook__(cls, C):
136103
except ImportError:
137104
pass
138105

139-
140106
# Check what implementation of readline we are using
141107
class RlType(Enum):
142108
GNU = 1
143109
PYREADLINE = 2
144110
NONE = 3
145111

146-
147112
rl_type = RlType.NONE
148113

149114
if 'pyreadline' in sys.modules:
@@ -167,25 +132,6 @@ class RlType(Enum):
167132
rl_basic_quote_characters = ctypes.c_char_p.in_dll(readline_lib, "rl_basic_quote_characters")
168133
orig_rl_basic_quote_characters_addr = ctypes.cast(rl_basic_quote_characters, ctypes.c_void_p).value
169134

170-
171-
# BrokenPipeError and FileNotFoundError exist only in Python 3. Use IOError for Python 2.
172-
if six.PY3:
173-
BROKEN_PIPE_ERROR = BrokenPipeError
174-
FILE_NOT_FOUND_ERROR = FileNotFoundError
175-
else:
176-
BROKEN_PIPE_ERROR = FILE_NOT_FOUND_ERROR = IOError
177-
178-
# On some systems, pyperclip will import gtk for its clipboard functionality.
179-
# The following code is a workaround for gtk interfering with printing from a background
180-
# thread while the CLI thread is blocking in raw_input() in Python 2 on Linux.
181-
if six.PY2 and sys.platform.startswith('lin'):
182-
try:
183-
# noinspection PyUnresolvedReferences
184-
import gtk
185-
gtk.set_interactive(0)
186-
except ImportError:
187-
pass
188-
189135
__version__ = '0.9.0'
190136

191137
# Pyparsing enablePackrat() can greatly speed up parsing, but problems have been seen in Python 3 in the past
@@ -250,8 +196,7 @@ def set_strip_quotes(val):
250196
def _which(editor):
251197
try:
252198
editor_path = subprocess.check_output(['which', editor], stderr=subprocess.STDOUT).strip()
253-
if six.PY3:
254-
editor_path = editor_path.decode()
199+
editor_path = editor_path.decode()
255200
except subprocess.CalledProcessError:
256201
editor_path = None
257202
return editor_path
@@ -431,12 +376,6 @@ def get_paste_buffer():
431376
:return: str - contents of the clipboard
432377
"""
433378
pb_str = pyperclip.paste()
434-
435-
# If value returned from the clipboard is unicode and this is Python 2, convert to a "normal" Python 2 string first
436-
if six.PY2 and not isinstance(pb_str, str):
437-
import unicodedata
438-
pb_str = unicodedata.normalize('NFKD', pb_str).encode('ascii', 'ignore')
439-
440379
return pb_str
441380

442381

@@ -659,7 +598,7 @@ def enter_submenu(parent_cmd, line):
659598
if self.persistent_history_file:
660599
try:
661600
readline.read_history_file(self.persistent_history_file)
662-
except FILE_NOT_FOUND_ERROR:
601+
except FileNotFoundError:
663602
pass
664603

665604
try:
@@ -843,12 +782,12 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, persistent_histor
843782
readline.read_history_file(persistent_history_file)
844783
# default history len is -1 (infinite), which may grow unruly
845784
readline.set_history_length(persistent_history_length)
846-
except FILE_NOT_FOUND_ERROR:
785+
except FileNotFoundError:
847786
pass
848787
atexit.register(readline.write_history_file, persistent_history_file)
849788

850-
# Call super class constructor. Need to do it in this way for Python 2 and 3 compatibility
851-
cmd.Cmd.__init__(self, completekey=completekey, stdin=stdin, stdout=stdout)
789+
# Call super class constructor
790+
super().__init__(completekey=completekey, stdin=stdin, stdout=stdout)
852791

853792
# Commands to exclude from the help menu and tab completion
854793
self.hidden_commands = ['eof', 'eos', '_relative_load']
@@ -974,7 +913,7 @@ def poutput(self, msg, end='\n'):
974913
self.stdout.write(msg_str)
975914
if not msg_str.endswith(end):
976915
self.stdout.write(end)
977-
except BROKEN_PIPE_ERROR:
916+
except BrokenPipeError:
978917
# This occurs if a command's output is being piped to another process and that process closes before the
979918
# command is finished. If you would like your application to print a warning message, then set the
980919
# broken_pipe_warning attribute to the message you want printed.
@@ -1066,7 +1005,7 @@ def ppaged(self, msg, end='\n'):
10661005
self.pipe_proc = None
10671006
else:
10681007
self.stdout.write(msg_str)
1069-
except BROKEN_PIPE_ERROR:
1008+
except BrokenPipeError:
10701009
# This occurs if a command's output is being piped to another process and that process closes before the
10711010
# command is finished. If you would like your application to print a warning message, then set the
10721011
# broken_pipe_warning attribute to the message you want printed.
@@ -1708,12 +1647,8 @@ def _display_matches_gnu_readline(self, substitution, matches, longest_match_len
17081647

17091648
# We will use readline's display function (rl_display_match_list()), so we
17101649
# need to encode our string as bytes to place in a C array.
1711-
if six.PY3:
1712-
encoded_substitution = bytes(substitution, encoding='utf-8')
1713-
encoded_matches = [bytes(cur_match, encoding='utf-8') for cur_match in matches_to_display]
1714-
else:
1715-
encoded_substitution = bytes(substitution)
1716-
encoded_matches = [bytes(cur_match) for cur_match in matches_to_display]
1650+
encoded_substitution = bytes(substitution, encoding='utf-8')
1651+
encoded_matches = [bytes(cur_match, encoding='utf-8') for cur_match in matches_to_display]
17171652

17181653
# rl_display_match_list() expects matches to be in argv format where
17191654
# substitution is the first element, followed by the matches, and then a NULL.
@@ -2300,19 +2235,12 @@ def _redirect_output(self, statement):
23002235
# Create a pipe with read and write sides
23012236
read_fd, write_fd = os.pipe()
23022237

2303-
# Make sure that self.poutput() expects unicode strings in Python 3 and byte strings in Python 2
2304-
write_mode = 'w'
2305-
read_mode = 'r'
2306-
if six.PY2:
2307-
write_mode = 'wb'
2308-
read_mode = 'rb'
2309-
23102238
# Open each side of the pipe and set stdout accordingly
23112239
# noinspection PyTypeChecker
2312-
self.stdout = io.open(write_fd, write_mode)
2240+
self.stdout = io.open(write_fd, 'w')
23132241
self.redirecting = True
23142242
# noinspection PyTypeChecker
2315-
subproc_stdin = io.open(read_fd, read_mode)
2243+
subproc_stdin = io.open(read_fd, 'r')
23162244

23172245
# We want Popen to raise an exception if it fails to open the process. Thus we don't set shell to True.
23182246
try:
@@ -2359,7 +2287,7 @@ def _restore_output(self, statement):
23592287
try:
23602288
# Close the file or pipe that stdout was redirected to
23612289
self.stdout.close()
2362-
except BROKEN_PIPE_ERROR:
2290+
except BrokenPipeError:
23632291
pass
23642292
finally:
23652293
# Restore self.stdout
@@ -2584,9 +2512,8 @@ def _cmdloop(self):
25842512
elif rl_type == RlType.PYREADLINE:
25852513
readline.rl.mode._display_completions = orig_pyreadline_display
25862514

2587-
# Need to set empty list this way because Python 2 doesn't support the clear() method on lists
2588-
self.cmdqueue = []
2589-
self._script_dir = []
2515+
self.cmdqueue.clear()
2516+
self._script_dir.clear()
25902517

25912518
return stop
25922519

@@ -2857,11 +2784,11 @@ def select(self, opts, prompt='Your choice? '):
28572784
that the return value can differ from
28582785
the text advertised to the user """
28592786
local_opts = opts
2860-
if isinstance(opts, string_types):
2787+
if isinstance(opts, str):
28612788
local_opts = list(zip(opts.split(), opts.split()))
28622789
fulloptions = []
28632790
for opt in local_opts:
2864-
if isinstance(opt, string_types):
2791+
if isinstance(opt, str):
28652792
fulloptions.append((opt, opt))
28662793
else:
28672794
try:
@@ -3422,10 +3349,8 @@ def do_load(self, arglist):
34223349
try:
34233350
# Read all lines of the script and insert into the head of the
34243351
# command queue. Add an "end of script (eos)" command to cleanup the
3425-
# self._script_dir list when done. Specify file encoding in Python
3426-
# 3, but Python 2 doesn't allow that argument to open().
3427-
kwargs = {'encoding': 'utf-8'} if six.PY3 else {}
3428-
with open(expanded_path, **kwargs) as target:
3352+
# self._script_dir list when done.
3353+
with open(expanded_path, encoding='utf-8') as target:
34293354
self.cmdqueue = target.read().splitlines() + ['eos'] + self.cmdqueue
34303355
except IOError as e:
34313356
self.perror('Problem accessing script from {}:\n{}'.format(expanded_path, e))
@@ -4129,10 +4054,6 @@ def __bool__(self):
41294054
"""If err is an empty string, treat the result as a success; otherwise treat it as a failure."""
41304055
return not self.err
41314056

4132-
def __nonzero__(self):
4133-
"""Python 2 uses this method for determining Truthiness"""
4134-
return self.__bool__()
4135-
41364057

41374058
if __name__ == '__main__':
41384059
# If run as the main application, simply start a bare-bones cmd2 application with only built-in functionality.

examples/alias_startup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class AliasAndStartup(cmd2.Cmd):
1616
""" Example cmd2 application where we create commands that just print the arguments they are called with."""
1717

1818
def __init__(self):
19-
cmd2.Cmd.__init__(self, startup_script='.cmd2rc')
19+
super().__init__(startup_script='.cmd2rc')
2020

2121

2222
if __name__ == '__main__':

examples/arg_print.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def __init__(self):
2828
self.shortcuts.update({'$': 'aprint', '%': 'oprint'})
2929

3030
# Make sure to call this super class __init__ *after* setting commentGrammars and/or updating shortcuts
31-
cmd2.Cmd.__init__(self)
31+
super().__init__()
3232
# NOTE: It is critical that the super class __init__ method be called AFTER updating certain parameters which
3333
# are not settable at runtime. This includes the commentGrammars, shortcuts, multilineCommands, etc.
3434

examples/argparse_example.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def __init__(self, ip_addr=None, port=None, transcript_files=None):
2828
self.settable['maxrepeats'] = 'Max number of `--repeat`s allowed'
2929

3030
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
31-
Cmd.__init__(self, use_ipython=False, transcript_files=transcript_files)
31+
super().__init__(use_ipython=False, transcript_files=transcript_files)
3232

3333
# Disable cmd's usage of command-line arguments as commands to be run at invocation
3434
# self.allow_cli_args = False

examples/environment.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class EnvironmentApp(Cmd):
1616
def __init__(self):
1717
self.settable.update({'degrees_c': 'Temperature in Celsius'})
1818
self.settable.update({'sunny': 'Is it sunny outside?'})
19-
Cmd.__init__(self)
19+
super().__init__()
2020

2121
def do_sunbathe(self, arg):
2222
if self.degrees_c < 20:

examples/event_loops.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
class Cmd2EventBased(cmd2.Cmd):
1313
"""Basic example of how to run cmd2 without it controlling the main loop."""
1414
def __init__(self):
15-
cmd2.Cmd.__init__(self)
15+
super().__init__()
1616

1717
# ... your class code here ...
1818

examples/example.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def __init__(self):
3535
self.shortcuts.update({'&': 'speak'})
3636

3737
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
38-
Cmd.__init__(self, use_ipython=False)
38+
super().__init__(use_ipython=False)
3939

4040
speak_parser = argparse.ArgumentParser()
4141
speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay')

examples/help_categories.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class HelpCategories(Cmd):
1818

1919
def __init__(self):
2020
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
21-
Cmd.__init__(self, use_ipython=False)
21+
super().__init__(use_ipython=False)
2222

2323
def do_connect(self, _):
2424
"""Connect command"""

examples/paged_output.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class PagedOutput(cmd2.Cmd):
1111
""" Example cmd2 application where we create commands that just print the arguments they are called with."""
1212

1313
def __init__(self):
14-
cmd2.Cmd.__init__(self)
14+
super().__init__()
1515

1616
@with_argument_list
1717
def do_page_file(self, args):

0 commit comments

Comments
 (0)