Skip to content

Commit 2397280

Browse files
kmvanbruntanselor
authored andcommitted
Renamed use_ipython keyword parameter of cmd2.Cmd.__init__() to include_ipy.
Added include_py keyword parameter to cmd2.Cmd.__init__(). If False, then the py command will not be available. Removed ability to run Python commands from the command line with py. Made banners and exit messages of Python and IPython consistent. Changed utils.is_text_file() to raise OSError if file cannot be read.
1 parent 070262e commit 2397280

23 files changed

+231
-289
lines changed

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
object that holds the settable attribute. `cmd2.Cmd.settables` is no longer a public dict attribute - it is now a
2323
property that aggregates all Settables across all registered CommandSets.
2424
* Failed transcript testing now sets self.exit_code to 1 instead of -1.
25+
* Renamed `use_ipython` keyword parameter of `cmd2.Cmd.__init__()` to `include_ipy`.
26+
* `py` command is only enabled if `include_py` parameter is `True`. See Enhancements for a description
27+
of this parameter.
28+
* Removed ability to run Python commands from the command line with `py`. Now `py` takes no arguments
29+
and just opens an interactive Python shell.
2530
* Enhancements
2631
* Added support for custom tab completion and up-arrow input history to `cmd2.Cmd2.read_input`.
2732
See [read_input.py](https://github.com/python-cmd2/cmd2/blob/master/examples/read_input.py)
@@ -37,7 +42,9 @@
3742
attribute added to the cmd2 instance itself.
3843
* Raising ``SystemExit`` or calling ``sys.exit()`` in a command or hook function will set ``self.exit_code``
3944
to the exit code used in those calls. It will also result in the command loop stopping.
40-
* ipy command now includes all of `self.py_locals` in the IPython environment
45+
* ipy command now includes all of `self.py_locals` in the IPython environment
46+
* Added `include_py` keyword parameter to `cmd2.Cmd.__init__()`. If `False`, then the `py` command will
47+
not be available. Defaults to `False`. `run_pyscript` is not affected by this parameter.
4148

4249
## 1.5.0 (January 31, 2021)
4350
* Bug Fixes

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ Main Features
2626
- Pipe command output to shell commands with `|`
2727
- Redirect command output to file with `>`, `>>`
2828
- Bare `>`, `>>` with no filename send output to paste buffer (clipboard)
29-
- `py` enters interactive Python console (opt-in `ipy` for IPython console)
29+
- Optional `py` command runs interactive Python console which can be used to debug your application
30+
- Optional `ipy` command runs interactive IPython console which can be used to debug your application
3031
- Option to display long output using a pager with ``cmd2.Cmd.ppaged()``
3132
- Multi-line commands
3233
- Special-character command shortcuts (beyond cmd's `?` and `!`)
@@ -249,9 +250,8 @@ class CmdLineApp(cmd2.Cmd):
249250
shortcuts = dict(cmd2.DEFAULT_SHORTCUTS)
250251
shortcuts.update({'&': 'speak'})
251252

252-
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
253-
super().__init__(use_ipython=False, multiline_commands=['orate'], shortcuts=shortcuts)
254-
253+
super().__init__(multiline_commands=['orate'], shortcuts=shortcuts)
254+
255255
# Make maxrepeats settable at runtime
256256
self.add_settable(cmd2.Settable('maxrepeats', int, 'max repetitions for speak command'))
257257

cmd2/cmd2.py

Lines changed: 102 additions & 129 deletions
Large diffs are not rendered by default.

cmd2/utils.py

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -186,38 +186,28 @@ def set_value(self, value: Any) -> Any:
186186

187187

188188
def is_text_file(file_path: str) -> bool:
189-
"""Returns if a file contains only ASCII or UTF-8 encoded text.
189+
"""Returns if a file contains only ASCII or UTF-8 encoded text and isn't empty.
190190
191191
:param file_path: path to the file being checked
192-
:return: True if the file is a text file, False if it is binary.
192+
:return: True if the file is a non-empty text file, otherwise False
193+
:raises OSError if file can't be read
193194
"""
194195
import codecs
195196

196197
expanded_path = os.path.abspath(os.path.expanduser(file_path.strip()))
197198
valid_text_file = False
198199

199-
# Check if the file is ASCII
200+
# Only need to check for utf-8 compliance since that covers ASCII, too
200201
try:
201-
with codecs.open(expanded_path, encoding='ascii', errors='strict') as f:
202-
# Make sure the file has at least one line of text
203-
# noinspection PyUnusedLocal
204-
if sum(1 for line in f) > 0:
202+
with codecs.open(expanded_path, encoding='utf-8', errors='strict') as f:
203+
# Make sure the file has only utf-8 text and is not empty
204+
if sum(1 for _ in f) > 0:
205205
valid_text_file = True
206-
except OSError: # pragma: no cover
207-
pass
206+
except OSError:
207+
raise
208208
except UnicodeDecodeError:
209-
# The file is not ASCII. Check if it is UTF-8.
210-
try:
211-
with codecs.open(expanded_path, encoding='utf-8', errors='strict') as f:
212-
# Make sure the file has at least one line of text
213-
# noinspection PyUnusedLocal
214-
if sum(1 for line in f) > 0:
215-
valid_text_file = True
216-
except OSError: # pragma: no cover
217-
pass
218-
except UnicodeDecodeError:
219-
# Not UTF-8
220-
pass
209+
# Not UTF-8
210+
pass
221211

222212
return valid_text_file
223213

docs/features/embedded_python_shells.rst

Lines changed: 30 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
11
Embedded Python Shells
22
======================
33

4-
The ``py`` command will run its arguments as a Python command. Entered without
5-
arguments, it enters an interactive Python session. The session can call
6-
"back" to your application through the name defined in ``self.pyscript_name``
7-
(defaults to ``app``). This wrapper provides access to execute commands in
8-
your ``cmd2`` application while maintaining isolation.
4+
Python (optional)
5+
------------------
6+
If the ``cmd2.Cmd`` class is instantiated with ``include_py=True``, then the
7+
optional ``py`` command will be present and run an interactive Python shell::
8+
9+
from cmd2 import Cmd
10+
class App(Cmd):
11+
def __init__(self):
12+
Cmd.__init__(self, include_py=True)
13+
14+
The Python shell can run CLI commands from you application using the object
15+
named in ``self.pyscript_name`` (defaults to ``app``). This wrapper provides
16+
access to execute commands in your ``cmd2`` application while maintaining
17+
isolation from the full `Cmd` instance. For example, any application command
18+
can be run with ``app("command ...")``.
919

1020
You may optionally enable full access to to your application by setting
1121
``self.self_in_py`` to ``True``. Enabling this flag adds ``self`` to the
@@ -17,77 +27,29 @@ in the CLI's environment.
1727

1828
Anything in ``self.py_locals`` is always available in the Python environment.
1929

20-
The ``app`` object (or your custom name) provides access to application
21-
commands through raw commands. For example, any application command call be
22-
called with ``app("<command>")``.
23-
24-
More Python examples:
25-
26-
::
27-
28-
(Cmd) py print("-".join("spelling"))
29-
s-p-e-l-l-i-n-g
30-
(Cmd) py
31-
Python 3.9.0 (default, Nov 11 2020, 21:21:51)
32-
[Clang 12.0.0 (clang-1200.0.32.21)] on darwin
33-
Type "help", "copyright", "credits" or "license" for more information.
34-
35-
End with `Ctrl-D` (Unix) / `Ctrl-Z` (Windows), `quit()`, `exit()`.
36-
Non-Python commands can be issued with: app("your command")
37-
(CmdLineApp)
38-
39-
End with `Ctrl-D` (Unix) / `Ctrl-Z` (Windows), `quit()`, `exit()`.
40-
Non-python commands can be issued with: app("your command")
41-
Run python code from external script files with: run("script.py")
42-
43-
>>> import os
44-
>>> os.uname()
45-
('Linux', 'eee', '2.6.31-19-generic', '#56-Ubuntu SMP Thu Jan 28 01:26:53 UTC 2010', 'i686')
46-
>>> app("say --piglatin {os}".format(os=os.uname()[0]))
47-
inuxLay
48-
>>> self.prompt
49-
'(Cmd) '
50-
>>> self.prompt = 'Python was here > '
51-
>>> quit()
52-
Python was here >
53-
54-
The ``py`` command also allows you to run Python scripts via ``py
55-
run('myscript.py')``. This provides a more complicated and more powerful
56-
scripting capability than that provided by the simple text file scripts
57-
discussed in :ref:`features/scripting:Scripting`. Python scripts can include
30+
All of these parameters are also available to Python scripts which run in your
31+
application via the ``run_pyscript`` command:
32+
33+
- supports tab completion of file system paths
34+
- has the ability to pass command-line arguments to the scripts invoked
35+
36+
This command provides a more complicated and more powerful scripting capability
37+
than that provided by the simple text file scripts. Python scripts can include
5838
conditional control flow logic. See the **python_scripting.py** ``cmd2``
5939
application and the **script_conditional.py** script in the ``examples`` source
6040
code directory for an example of how to achieve this in your own applications.
41+
See :ref:`features/scripting:Scripting` for an explanation of both scripting
42+
methods in **cmd2** applications.
6143

62-
Using ``py`` to run scripts directly is considered deprecated. The newer
63-
``run_pyscript`` command is superior for doing this in two primary ways:
64-
65-
- it supports tab completion of file system paths
66-
- it has the ability to pass command-line arguments to the scripts invoked
67-
68-
There are no disadvantages to using ``run_pyscript`` as opposed to ``py
69-
run()``. A simple example of using ``run_pyscript`` is shown below along with
70-
the arg_printer_ script::
44+
A simple example of using ``run_pyscript`` is shown below along with the
45+
arg_printer_ script::
7146

7247
(Cmd) run_pyscript examples/scripts/arg_printer.py foo bar baz
7348
Running Python script 'arg_printer.py' which was called with 3 arguments
7449
arg 1: 'foo'
7550
arg 2: 'bar'
7651
arg 3: 'baz'
7752

78-
.. note::
79-
80-
If you want to be able to pass arguments with spaces to commands, then we
81-
strongly recommend using one of the decorators, such as
82-
``with_argument_list``. ``cmd2`` will pass your **do_*** methods a list of
83-
arguments in this case.
84-
85-
When using this decorator, you can then put arguments in quotes like so::
86-
87-
$ examples/arg_print.py
88-
(Cmd) lprint foo "bar baz"
89-
lprint was called with the following list of arguments: ['foo', 'bar baz']
90-
9153
.. _arg_printer:
9254
https://github.com/python-cmd2/cmd2/blob/master/examples/scripts/arg_printer.py
9355

@@ -96,13 +58,13 @@ IPython (optional)
9658
------------------
9759

9860
**If** IPython_ is installed on the system **and** the ``cmd2.Cmd`` class is
99-
instantiated with ``use_ipython=True``, then the optional ``ipy`` command will
100-
be present::
61+
instantiated with ``include_ipy=True``, then the optional ``ipy`` command will
62+
run an interactive IPython shell::
10163

10264
from cmd2 import Cmd
10365
class App(Cmd):
10466
def __init__(self):
105-
Cmd.__init__(self, use_ipython=True)
67+
Cmd.__init__(self, include_ipy=True)
10668

10769
The ``ipy`` command enters an interactive IPython_ session. Similar to an
10870
interactive Python session, this shell can access your application instance via

docs/features/initialization.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ capabilities which you may wish to utilize while initializing the app::
2626

2727
def __init__(self):
2828
super().__init__(multiline_commands=['echo'], persistent_history_file='cmd2_history.dat',
29-
startup_script='scripts/startup.txt', use_ipython=True)
29+
startup_script='scripts/startup.txt', include_ipy=True)
3030

3131
# Prints an intro banner once upon application startup
3232
self.intro = style('Welcome to cmd2!', fg=fg.red, bg=bg.white, bold=True)

examples/arg_decorators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
class ArgparsingApp(cmd2.Cmd):
1111
def __init__(self):
12-
super().__init__(use_ipython=True)
12+
super().__init__(include_ipy=True)
1313
self.intro = 'cmd2 has awesome decorators to make it easy to use Argparse to parse command arguments'
1414

1515
# do_fsize parser

examples/basic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def __init__(self):
2424
multiline_commands=['echo'],
2525
persistent_history_file='cmd2_history.dat',
2626
startup_script='scripts/startup.txt',
27-
use_ipython=True,
27+
include_ipy=True,
2828
)
2929

3030
self.intro = style('Welcome to PyOhio 2019 and cmd2!', fg=fg.red, bg=bg.white, bold=True) + ' 😀'

examples/cmd_as_argument.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ class CmdLineApp(cmd2.Cmd):
3030
def __init__(self):
3131
shortcuts = dict(cmd2.DEFAULT_SHORTCUTS)
3232
shortcuts.update({'&': 'speak'})
33-
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
34-
super().__init__(allow_cli_args=False, use_ipython=True, multiline_commands=['orate'], shortcuts=shortcuts)
33+
# Set include_ipy to True to enable the "ipy" command which runs an interactive IPython shell
34+
super().__init__(allow_cli_args=False, include_ipy=True, multiline_commands=['orate'], shortcuts=shortcuts)
3535

3636
self.self_in_py = True
3737
self.maxrepeats = 3

examples/colors.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ class CmdLineApp(cmd2.Cmd):
4444
"""Example cmd2 application demonstrating colorized output."""
4545

4646
def __init__(self):
47-
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
48-
super().__init__(use_ipython=True)
47+
# Set include_ipy to True to enable the "ipy" command which runs an interactive IPython shell
48+
super().__init__(include_ipy=True)
4949

5050
self.maxrepeats = 3
5151
# Make maxrepeats settable at runtime

0 commit comments

Comments
 (0)