Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## 0.9.15 (July TBD, 2019)
* **Renamed Commands Notice**
* The following commands were renamed in the last release and have been removed in this release
* `load` - replaced by `run_script`
* `_relative_load` - replaced by `_relative_run_script`
* `pyscript` - replaced by `run_pyscript`
* We apologize for any inconvenience, but the new names are more self-descriptive
* Lots of end users were confused particularly about what exactly `load` should be loading

## 0.9.14 (June 29, 2019)
* Enhancements
* Added support for and testing with Python 3.8, starting with 3.8 beta
Expand Down
24 changes: 4 additions & 20 deletions cmd2/clipboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,14 @@
"""
This module provides basic ability to copy from and paste to the clipboard/pastebuffer.
"""
import sys

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: # pragma: no cover
# noinspection PyUnresolvedReferences,PyProtectedMember
from pyperclip import PyperclipException
# noinspection PyProtectedMember
from pyperclip import PyperclipException

# Can we access the clipboard? Should always be true on Windows and Mac, but only sometimes on Linux
# noinspection PyUnresolvedReferences
try:
# Get the version of the pyperclip module as a float
pyperclip_ver = float('.'.join(pyperclip.__version__.split('.')[:2]))

# The extraneous output bug in pyperclip on Linux using xclip was fixed in more recent versions of pyperclip
if sys.platform.startswith('linux') and pyperclip_ver < 1.6:
# Avoid extraneous output to stderr from xclip when clipboard is empty at cost of overwriting clipboard contents
pyperclip.copy('')
else:
# Try getting the contents of the clipboard
_ = pyperclip.paste()
# Try getting the contents of the clipboard
_ = pyperclip.paste()
except PyperclipException:
can_clip = False
else:
Expand Down
93 changes: 34 additions & 59 deletions cmd2/cmd2.py
Original file line number Diff line number Diff line change
Expand Up @@ -3261,16 +3261,9 @@ def do_run_pyscript(self, args: argparse.Namespace) -> bool:
finally:
# Restore command line arguments to original state
sys.argv = orig_args
if args.__statement__.command == "pyscript":
warning = ("pyscript has been renamed and will be removed in the next release, "
"please use run_pyscript instead\n")
self.perror(ansi.style_warning(warning))

return py_return

# pyscript is deprecated
do_pyscript = do_run_pyscript

# Only include the do_ipy() method if IPython is available on the system
if ipython_available: # pragma: no cover
@with_argparser(ACArgumentParser())
Expand Down Expand Up @@ -3644,59 +3637,49 @@ def do_run_script(self, args: argparse.Namespace) -> Optional[bool]:
"""
expanded_path = os.path.abspath(os.path.expanduser(args.script_path))

# Wrap everything in a try/finally just to make sure the warning prints at end if `load` was called
try:
# Make sure the path exists and we can access it
if not os.path.exists(expanded_path):
self.perror("'{}' does not exist or cannot be accessed".format(expanded_path))
return
# Make sure the path exists and we can access it
if not os.path.exists(expanded_path):
self.perror("'{}' does not exist or cannot be accessed".format(expanded_path))
return

# Make sure expanded_path points to a file
if not os.path.isfile(expanded_path):
self.perror("'{}' is not a file".format(expanded_path))
return
# Make sure expanded_path points to a file
if not os.path.isfile(expanded_path):
self.perror("'{}' is not a file".format(expanded_path))
return

# Make sure the file is not empty
if os.path.getsize(expanded_path) == 0:
self.perror("'{}' is empty".format(expanded_path))
return
# Make sure the file is not empty
if os.path.getsize(expanded_path) == 0:
self.perror("'{}' is empty".format(expanded_path))
return

# Make sure the file is ASCII or UTF-8 encoded text
if not utils.is_text_file(expanded_path):
self.perror("'{}' is not an ASCII or UTF-8 encoded text file".format(expanded_path))
return
# Make sure the file is ASCII or UTF-8 encoded text
if not utils.is_text_file(expanded_path):
self.perror("'{}' is not an ASCII or UTF-8 encoded text file".format(expanded_path))
return

try:
# Read all lines of the script
with open(expanded_path, encoding='utf-8') as target:
script_commands = target.read().splitlines()
except OSError as ex: # pragma: no cover
self.pexcept("Problem accessing script from '{}': {}".format(expanded_path, ex))
return
try:
# Read all lines of the script
with open(expanded_path, encoding='utf-8') as target:
script_commands = target.read().splitlines()
except OSError as ex: # pragma: no cover
self.pexcept("Problem accessing script from '{}': {}".format(expanded_path, ex))
return

orig_script_dir_count = len(self._script_dir)
orig_script_dir_count = len(self._script_dir)

try:
self._script_dir.append(os.path.dirname(expanded_path))
try:
self._script_dir.append(os.path.dirname(expanded_path))

if args.transcript:
self._generate_transcript(script_commands, os.path.expanduser(args.transcript))
else:
return self.runcmds_plus_hooks(script_commands)
if args.transcript:
self._generate_transcript(script_commands, os.path.expanduser(args.transcript))
else:
return self.runcmds_plus_hooks(script_commands)

finally:
with self.sigint_protection:
# Check if a script dir was added before an exception occurred
if orig_script_dir_count != len(self._script_dir):
self._script_dir.pop()
finally:
if args.__statement__.command == "load":
warning = ("load has been renamed and will be removed in the next release, "
"please use run_script instead\n")
self.perror(ansi.style_warning(warning))

# load has been deprecated
do_load = do_run_script
with self.sigint_protection:
# Check if a script dir was added before an exception occurred
if orig_script_dir_count != len(self._script_dir):
self._script_dir.pop()

relative_run_script_description = run_script_description
relative_run_script_description += (
Expand All @@ -3717,19 +3700,11 @@ def do__relative_run_script(self, args: argparse.Namespace) -> Optional[bool]:
Run commands in script file that is encoded as either ASCII or UTF-8 text
:return: True if running of commands should stop
"""
if args.__statement__.command == "_relative_load":
warning = ("_relative_load has been renamed and will be removed in the next release, "
"please use _relative_run_script instead\n")
self.perror(ansi.style_warning(warning))

file_path = args.file_path
# NOTE: Relative path is an absolute path, it is just relative to the current script directory
relative_path = os.path.join(self._current_script_dir or '', file_path)
return self.do_run_script(relative_path)

# _relative_load has been deprecated
do__relative_load = do__relative_run_script

def _run_transcript_tests(self, transcript_paths: List[str]) -> None:
"""Runs transcript tests for provided file(s).

Expand Down
5 changes: 2 additions & 3 deletions docs/integrating.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,8 @@ loop::

Documented commands (type help <topic>):
========================================
alias history mumble pyscript run_script shell
edit load orate quit say shortcuts
help macro py run_pyscript set speak
alias help macro orate quit run_script set shortcuts
edit history mumble py run_pyscript say shell speak

(Cmd)

Expand Down
17 changes: 8 additions & 9 deletions docs/unfreefeatures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -248,12 +248,8 @@ By default, the ``help`` command displays::

Documented commands (type help <topic>):
========================================
alias findleakers pyscript sessions status vminfo
config help quit set stop which
connect history redeploy shell thread_dump
deploy list resources shortcuts unalias
edit load restart sslconnectorciphers undeploy
expire py serverinfo start version
alias help ipy py run_pyscript set shortcuts
edit history macro quit run_script shell

If you have a large number of commands, you can optionally group your commands into categories.
Here's the output from the example ``help_categories.py``::
Expand All @@ -265,6 +261,10 @@ Here's the output from the example ``help_categories.py``::
deploy findleakers redeploy sessions stop
expire list restart start undeploy

Command Management
==================
disable_commands enable_commands

Connecting
==========
connect which
Expand All @@ -275,9 +275,8 @@ Here's the output from the example ``help_categories.py``::

Other
=====
alias edit history py quit shell unalias
config help load pyscript set shortcuts version

alias edit history py run_pyscript set shortcuts
config help macro quit run_script shell version

There are 2 methods of specifying command categories, using the ``@with_category`` decorator or with the
``categorize()`` function. Once a single command category is detected, the help output switches to a categorized
Expand Down
2 changes: 1 addition & 1 deletion examples/transcripts/exampleSession.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# The regex for editor will match whatever program you use.
# regexes on prompts just make the trailing space obvious
(Cmd) set
colors: /(Terminal|Always|Never)/
allow_ansi: /(Terminal|Always|Never)/
continuation_prompt: >/ /
debug: False
echo: False
Expand Down
2 changes: 1 addition & 1 deletion examples/transcripts/transcript_regex.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# The regex for editor will match whatever program you use.
# regexes on prompts just make the trailing space obvious
(Cmd) set
colors: /(Terminal|Always|Never)/
allow_ansi: /(Terminal|Always|Never)/
continuation_prompt: >/ /
debug: False
echo: False
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

SETUP_REQUIRES = ['setuptools_scm']

INSTALL_REQUIRES = ['pyperclip >= 1.5.27', 'colorama', 'attrs >= 16.3.0', 'wcwidth >= 0.1.7']
INSTALL_REQUIRES = ['pyperclip >= 1.6', 'colorama >= 0.3.7', 'attrs >= 16.3.0', 'wcwidth >= 0.1.7']

EXTRAS_REQUIRE = {
# Windows also requires pyreadline to ensure tab completion works
Expand Down
2 changes: 2 additions & 0 deletions tests/.cmd2rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
alias create ls !ls -hal
alias create pwd !pwd
34 changes: 21 additions & 13 deletions tests/test_cmd2.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,11 +306,6 @@ def test_run_script(base_app, request):
assert script_out == manual_out
assert script_err == manual_err

def test_load_deprecated(base_app):
"""Delete this when load alias is removed"""
_, err = run_cmd(base_app, "load fake")
assert "load has been renamed and will be removed" in err[-1]

def test_run_script_with_empty_args(base_app):
out, err = run_cmd(base_app, 'run_script')
assert "the following arguments are required" in err[1]
Expand Down Expand Up @@ -436,11 +431,6 @@ def test_relative_run_script_requires_an_argument(base_app):
out, err = run_cmd(base_app, '_relative_run_script')
assert 'Error: the following arguments' in err[1]

def test_relative_load_deprecated(base_app):
"""Delete this when _relative_load alias is removed"""
_, err = run_cmd(base_app, "_relative_load fake")
assert "_relative_load has been renamed and will be removed" in err[0]

def test_output_redirection(base_app):
fd, filename = tempfile.mkstemp(prefix='cmd2_test', suffix='.txt')
os.close(fd)
Expand Down Expand Up @@ -1490,7 +1480,6 @@ def test_poutput_ansi_always(outsim_app):
assert colored_msg != msg
assert out == expected


def test_poutput_ansi_never(outsim_app):
msg = 'Hello World'
ansi.allow_ansi = ansi.ANSI_NEVER
Expand Down Expand Up @@ -1848,8 +1837,8 @@ def test_onecmd_raw_str_quit(outsim_app):
def test_get_all_commands(base_app):
# Verify that the base app has the expected commands
commands = base_app.get_all_commands()
expected_commands = ['_relative_load', '_relative_run_script', 'alias', 'edit', 'eof', 'help', 'history', 'load',
'macro', 'py', 'pyscript', 'quit', 'run_pyscript', 'run_script', 'set', 'shell', 'shortcuts']
expected_commands = ['_relative_run_script', 'alias', 'edit', 'eof', 'help', 'history', 'macro',
'py', 'quit', 'run_pyscript', 'run_script', 'set', 'shell', 'shortcuts']
assert commands == expected_commands

def test_get_help_topics(base_app):
Expand Down Expand Up @@ -2154,3 +2143,22 @@ def test_disabled_message_command_name(disable_commands_app):

out, err = run_cmd(disable_commands_app, 'has_help_func')
assert err[0].startswith('has_help_func is currently disabled')


def test_startup_script(request):
test_dir = os.path.dirname(request.module.__file__)
startup_script = os.path.join(test_dir, '.cmd2rc')
app = cmd2.Cmd(allow_cli_args=False, startup_script=startup_script)
assert len(app._startup_commands) == 1
assert app._startup_commands[0] == "run_script '{}'".format(startup_script)
app._startup_commands.append('quit')
app.cmdloop()
out, err = run_cmd(app, 'alias list')
assert len(out) > 1
assert 'alias create ls' in out[0]


def test_transcripts_at_init():
transcript_files = ['foo', 'bar']
app = cmd2.Cmd(allow_cli_args=False, transcript_files=transcript_files)
assert app._transcript_files == transcript_files
5 changes: 0 additions & 5 deletions tests/test_run_pyscript.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,3 @@ def test_run_pyscript_stop(base_app, request):
python_script = os.path.join(test_dir, 'pyscript', 'stop.py')
stop = base_app.onecmd_plus_hooks('run_pyscript {}'.format(python_script))
assert stop

def test_pyscript_deprecated(base_app):
"""Delete this when pyscript alias is removed"""
_, err = run_cmd(base_app, "pyscript fake")
assert "pyscript has been renamed and will be removed" in err[-1]