diff options
-rw-r--r-- | cmd2/ansi.py | 16 | ||||
-rw-r--r-- | cmd2/cmd2.py | 2 | ||||
-rw-r--r-- | docs/api/ansi.rst | 5 | ||||
-rw-r--r-- | docs/api/cmd.rst | 17 | ||||
-rw-r--r-- | docs/api/index.rst | 1 | ||||
-rw-r--r-- | docs/doc_conventions.rst | 190 | ||||
-rw-r--r-- | docs/features/builtin_commands.rst | 75 | ||||
-rw-r--r-- | docs/features/generating_output.rst | 196 | ||||
-rw-r--r-- | docs/features/index.rst | 1 | ||||
-rw-r--r-- | docs/features/settings.rst | 170 | ||||
-rw-r--r-- | docs/features/startup_commands.rst | 2 | ||||
-rw-r--r-- | docs/migrating/incompatibilities.rst | 15 | ||||
-rw-r--r-- | docs/migrating/next_steps.rst | 9 | ||||
-rwxr-xr-x | examples/colors.py | 26 | ||||
-rwxr-xr-x | examples/plumbum_colors.py | 2 |
15 files changed, 569 insertions, 158 deletions
diff --git a/cmd2/ansi.py b/cmd2/ansi.py index b3dd7cc1..d3af525a 100644 --- a/cmd2/ansi.py +++ b/cmd2/ansi.py @@ -94,7 +94,9 @@ def strip_style(text: str) -> str: def style_aware_wcswidth(text: str) -> int: """ Wrap wcswidth to make it compatible with strings that contains ANSI style sequences + :param text: the string being measured + :return: the width of the string when printed to the terminal """ # Strip ANSI style sequences since they cause wcswidth to return -1 return wcswidth(strip_style(text)) @@ -103,6 +105,7 @@ def style_aware_wcswidth(text: str) -> int: def style_aware_write(fileobj: IO, msg: str) -> None: """ Write a string to a fileobject and strip its ANSI style sequences if required by allow_style setting + :param fileobj: the file object being written to :param msg: the string being written """ @@ -115,9 +118,10 @@ def style_aware_write(fileobj: IO, msg: str) -> None: def fg_lookup(fg_name: str) -> str: """ Look up ANSI escape codes based on foreground color name. + :param fg_name: foreground color name to look up ANSI escape code(s) for :return: ANSI escape code(s) associated with this color - :raises ValueError if the color cannot be found + :raises ValueError: if the color cannot be found """ try: ansi_escape = FG_COLORS[fg_name.lower()] @@ -129,9 +133,10 @@ def fg_lookup(fg_name: str) -> str: def bg_lookup(bg_name: str) -> str: """ Look up ANSI escape codes based on background color name. + :param bg_name: background color name to look up ANSI escape code(s) for :return: ANSI escape code(s) associated with this color - :raises ValueError if the color cannot be found + :raises ValueError: if the color cannot be found """ try: ansi_escape = BG_COLORS[bg_name.lower()] @@ -193,8 +198,13 @@ def style(text: Any, *, fg: str = '', bg: str = '', bold: bool = False, # These can be altered to suit an application's needs and only need to be a # function with the following structure: func(str) -> str style_success = functools.partial(style, fg='green') +"""Partial function supplying arguments to :meth:`cmd2.ansi.style()` which colors text to signify success""" + style_warning = functools.partial(style, fg='bright_yellow') +"""Partial function supplying arguments to :meth:`cmd2.ansi.style()` which colors text to signify a warning""" + style_error = functools.partial(style, fg='bright_red') +"""Partial function supplying arguments to :meth:`cmd2.ansi.style()` which colors text to signify an error""" def async_alert_str(*, terminal_columns: int, prompt: str, line: str, cursor_offset: int, alert_msg: str) -> str: @@ -255,6 +265,6 @@ def set_title_str(title: str) -> str: """Get the required string, including ANSI escape codes, for setting window title for the terminal. :param title: new title for the window - :return string to write to sys.stderr in order to set the window title to the desired test + :return: string to write to sys.stderr in order to set the window title to the desired test """ return colorama.ansi.set_title(title) diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 434ade5f..cce281b7 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -290,7 +290,7 @@ class Cmd(cmd.Cmd): # The error that prints when a non-existent command is run self.default_error = "{} is not a recognized command, alias, or macro" - # If this string is non-empty, then this warning message will print if a broken pipe error occurs while printing + # If non-empty, this string will be displayed if a broken pipe error occurs self.broken_pipe_warning = '' # Commands that will run at the beginning of the command loop diff --git a/docs/api/ansi.rst b/docs/api/ansi.rst new file mode 100644 index 00000000..e361bd4f --- /dev/null +++ b/docs/api/ansi.rst @@ -0,0 +1,5 @@ +cmd2.ansi +========= + +.. automodule:: cmd2.ansi + :members: diff --git a/docs/api/cmd.rst b/docs/api/cmd.rst index 4f88101e..ee8d7c67 100644 --- a/docs/api/cmd.rst +++ b/docs/api/cmd.rst @@ -3,3 +3,20 @@ cmd2.Cmd .. autoclass:: cmd2.cmd2.Cmd :members: + + .. attribute:: help_error + + The error message displayed to the user when they request help for a + command with no help defined. + + .. attribute:: default_error + + The error message displayed when a non-existent command is run. + + .. attribute:: settable + + This dictionary contains the name and description of all settings available to users. + + Users use the :ref:`features/builtin_commands:set` command to view and + modify settings. Settings are stored in instance attributes with the + same name as the setting. diff --git a/docs/api/index.rst b/docs/api/index.rst index 94d0fdd4..f0324eab 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -7,5 +7,6 @@ API Reference cmd decorators exceptions + ansi utility_classes utility_functions diff --git a/docs/doc_conventions.rst b/docs/doc_conventions.rst index 39302fc6..9b714d45 100644 --- a/docs/doc_conventions.rst +++ b/docs/doc_conventions.rst @@ -19,61 +19,115 @@ In addition: documentation, and to the API reference +Style Checker +------------- + +Use `doc8 <https://pypi.org/project/doc8/>`_ to check the style of the +documentation. This tool can be invoked using the proper options by typing: + +.. code-block:: shell + + $ invoke doc8 + + Naming Files ------------ -- all lower case file names +All source files in the documentation must: + +- have all lower case file names - if the name has multiple words, separate them with an underscore -- all documentation file names end in '.rst' +- end in '.rst' -Heirarchy of headings ---------------------- +Indenting +--------- -show the heirarchy of sphinx headings we use, and the conventions (underline -only, no overline) +In reStructuredText all indenting is significant. Use 2 spaces per indenting +level. -Use '=', then '-', then '~'. If your document needs more levels than that, -break it into separate documents. -You only have to worry about the heirarchy of headings within a single file. -Sphinx handles the intra-file heirarchy magically on it's own. +Wrapping +-------- -Use two blank lines before every heading unless it's the first heading in the -file. Use one blank line after every heading +Hard wrap all text so that line lengths are no greater than 79 characters. It +makes everything easier when editing documentation, and has no impact on +reading documentation because we render to html. -Code ----- +Titles and Headings +------------------- -This documentation declares python as the default Sphinx domain. Python code -or interactive python sessions can be presented by either: +reStructuredText allows flexibility in how headings are defined. You only have +to worry about the heirarchy of headings within a single file. Sphinx magically +handles the intra-file heirarchy on it's own. This magic means that no matter +how you style titles and headings in the various files that comprise the +documentation, Sphinx will render properly structured output. To ensure we have +a similar consistency when viewing the source files, we use the following +conventions for titles and headings: -- finishing the preceding paragraph with a ``::`` and indenting the code -- use the ``.. code-block::`` directive +1. When creating a heading for a section, do not use the overline and underline +syntax. Use the underline syntax only:: + + Document Title + ============== + +2. The string of adornment characters on the line following the heading should +be the same length as the title. + +3. The title of a document should use the '=' adornment character on the next +line and only one heading of this level should appear in each file. + +4. Sections within a document should have their titles adorned with the '-' +character:: + + Section Title + ------------- + +5. Subsections within a section should have their titles adorned with the '~' +character:: + + Subsection Title + ~~~~~~~~~~~~~~~~ -If you want to show other code, like shell commands, then use ``.. code-block: -shell``. +6. Use two blank lines before every title unless it's the first heading in the +file. Use one blank line after every heading. +7. If your document needs more than three levels of sections, break it into +separate documents. + + +Inline Code +----------- + +This documentation declares ``python`` as the default Sphinx domain. Python +code or interactive Python sessions can be presented by either: + +- finishing the preceding paragraph with a ``::`` and indenting the code +- use the ``.. code-block::`` directive -Table of Contents and Captions ------------------------------- +If you want to show non-Python code, like shell commands, then use ``.. +code-block: shell``. -Hyperlinks ----------- +External Hyperlinks +------------------- If you want to use an external hyperlink target, define the target at the top -of the page, not the bottom. +of the page or the top of the section, not the bottom. The target definition +should always appear before it is referenced. +Links To Other Documentation Pages and Sections +----------------------------------------------- + We use the Sphinx `autosectionlabel <http://www.sphinx-doc.org/en/master/usage/extensions/autosectionlabel.html>`_ extension. This allows you to reference any header in any document by:: See :ref:`features/argument_processing:Help Messages` -or :: +or:: See :ref:`custom title<features/argument_processing:Help Messages>` @@ -85,44 +139,80 @@ and See :ref:`custom title<features/argument_processing:Help Messages>` -[TODO what's the right way to link to source code? Can we make it link to the -tag that the documentation is rendered from?] +API Documentation +----------------- -Autolinking ------------ +The API documentation is mostly pulled from docstrings in the source code using +the Sphinx `autodoc +<https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html>`_ +extension. However, Sphinx has issues generating documentation for instance +attributes (see `cmd2 issue 821 +<https://github.com/python-cmd2/cmd2/issues/821>`_ for the full discussion). We +have chosen to not use code as the source of instance attribute documentation. +Instead, it is added manually to the documentation files in ``cmd2/docs/api``. +See ``cmd2/docs/api/cmd.rst`` to see how to add documentation for an attribute. +For module data members and class attributes, the ``autodoc`` extension allows +documentation in a comment with special formatting (using a #: to start the +comment instead of just #), or in a docstring after the definition. This +project has standardized on the docstring after the definition approach. Do not +use the specially formatted comment approach. -Referencing cmd2 API documentation ----------------------------------- +Links to API Reference +---------------------- -Info and Warning Callouts -------------------------- +To reference a method or function, use one of the following approaches: +1. Reference the full dotted path of the method:: -Wrapping --------- + The :meth:`cmd2.cmd2.Cmd.poutput` method is similar to the Python built-in + print function. -Hard wrap all text with line lengths no greater than 79 characters. It makes -everything easier when editing documentation, and has no impact on reading -documentation because we render to html. +Which renders as: The :meth:`cmd2.cmd2.Cmd.poutput` method is similar to the +Python built-in print function. +2. Reference the full dotted path to the method, but only display the method +name:: -Referencing cmd2 ------------------ + The :meth:`~cmd2.cmd2.Cmd.poutput` method is similar to the Python built-in print function. -Whenever you reference ``cmd2`` in the documentation, enclose it in double -backticks. This indicates an inline literal in restructured text, and makes it -stand out when rendered as html. +Which renders as: The :meth:`~cmd2.cmd2.Cmd.poutput` method is similar to the +Python built-in print function. -Style Checker -------------- +3. Reference a portion of the dotted path of the method:: -Use `doc8 <https://pypi.org/project/doc8/>`_ to check the style of the -documentation. This tool can be invoked using the proper options by typing: + The :meth:`.cmd2.Cmd.poutput` method is similar to the Python built-in print + function. -.. code-block:: shell +Which renders as: The :meth:`.cmd2.Cmd.poutput` method is similar to the Python +built-in print function. - $ invoke doc8 +Avoid either of these approaches: + +1. Reference just the class name without enough dotted path:: + + The :meth:`.Cmd.poutput` method is similar to the Python built-in print + function. + +Because ``cmd2.Cmd`` subclasses ``cmd.Cmd`` from the standard library, this +approach does not clarify which class it is referring to. + +2. Reference just a method name:: + + The :meth:`poutput` method is similar to the Python built-in print + function. + +While Sphinx may be smart enough to generate the correct output, the potential +for multiple matching references is high, which causes Sphinx to generate +warnings. The build pipeline that renders the documentation treats warnings as +fatal errors. It's best to just be specific about what you are referencing. + +Referencing cmd2 +----------------- + +Whenever you reference ``cmd2`` in the documentation, enclose it in double +backticks. This indicates an inline literal in restructured text, and makes it +stand out when rendered as html. diff --git a/docs/features/builtin_commands.rst b/docs/features/builtin_commands.rst new file mode 100644 index 00000000..2a857339 --- /dev/null +++ b/docs/features/builtin_commands.rst @@ -0,0 +1,75 @@ +Builtin Commands +================ + +Applications which subclass :class:`cmd2.cmd2.Cmd` inherit a number of commands +which may be useful to your users. Developers can +:ref:`features/builtin_commands:Remove Builtin Commands` if they do not want +them to be part of the application. + +edit +---- + +This command launches an editor program and instructs it to open the given file +name. Here's an example: + +.. code-block:: text + + (Cmd) edit ~/.ssh/config + +The program to be launched is determined by the value of the +:ref:`features/settings:editor` setting. + + +set +--- + +A list of all user-settable parameters, with brief comments, is viewable from +within a running application: + +.. code-block:: text + + (Cmd) set --long + allow_style: Terminal # Allow ANSI text style sequences in output (valid values: Terminal, Always, Never) + continuation_prompt: > # On 2nd+ line of input + debug: False # Show full error stack on error + echo: False # Echo command issued into output + editor: vim # Program used by ``edit`` + feedback_to_output: False # include nonessentials in `|`, `>` results + locals_in_py: False # Allow access to your application in py via self + max_completion_items: 50 # Maximum number of CompletionItems to display during tab completion + prompt: (Cmd) # The prompt issued to solicit input + quiet: False # Don't print nonessential feedback + timing: False # Report execution times + +Any of these user-settable parameters can be set while running your app with +the ``set`` command like so: + +.. code-block:: text + + (Cmd) set allow_style Never + + +shell +----- + +Execute a command as if at the operating system shell prompt: + +.. code-block:: text + + (Cmd) shell pwd -P + /usr/local/bin + + +Remove Builtin Commands +----------------------- + +Developers may not want to offer the commands builtin to :class:`cmd2.cmd2.Cmd` +to users of their application. To remove a command you must delete the method +implementing that command from the :class:`cmd2.cmd2.Cmd` object at runtime. +For example, if you wanted to remove the :ref:`features/builtin_commands:shell` +command from your application:: + + class NoShellApp(cmd2.Cmd): + """A simple cmd2 application.""" + + delattr(cmd2.Cmd, 'do_shell') diff --git a/docs/features/generating_output.rst b/docs/features/generating_output.rst index e83b5fdf..f3d2a7f4 100644 --- a/docs/features/generating_output.rst +++ b/docs/features/generating_output.rst @@ -1,71 +1,169 @@ Generating Output ================= -how to generate output +A standard ``cmd`` application can produce output by using either of these +methods:: -- poutput -- perror -- paging -- exceptions -- color support + print("Greetings, Professor Falken.", file=self.stdout) + self.stdout.write("Shall we play a game?\n") -Standard ``cmd`` applications produce their output with -``self.stdout.write('output')`` (or with ``print``, but ``print`` decreases -output flexibility). ``cmd2`` applications can use ``self.poutput('output')``, -``self.pfeedback('message')``, ``self.perror('errmsg')``, and -``self.ppaged('text')`` instead. These methods have these advantages: +While you could send output directly to ``sys.stdout``, :mod:`cmd2.cmd2.Cmd` +can be initialized with a ``stdin`` and ``stdout`` variables, which it stores +as ``self.stdin`` and ``self.stdout``. By using these variables every time you +produce output, you can trivially change where all the output goes by changing +how you initialize your class. -- Handle output redirection to file and/or pipe appropriately -- More concise - - ``.pfeedback()`` destination is controlled by ``quiet`` parameter. -- Option to display long output using a pager via ``ppaged()`` +:mod:`cmd2.cmd2.Cmd` extends this approach in a number of convenient ways. See +:ref:`features/redirection:Output Redirection And Pipes` for information on how +users can change where the output of a command is sent. In order for those +features to work, the output you generate must be sent to ``self.stdout``. You +can use the methods described above, and everything will work fine. +:mod:`cmd2.cmd2.Cmd` also includes a number of output related methods which you +may use to enhance the output your application produces. -.. automethod:: cmd2.cmd2.Cmd.poutput - :noindex: -.. automethod:: cmd2.cmd2.Cmd.perror - :noindex: -.. automethod:: cmd2.cmd2.Cmd.pfeedback - :noindex: -.. automethod:: cmd2.cmd2.Cmd.ppaged - :noindex: +Ordinary Output +--------------- -Suppressing non-essential output --------------------------------- +The :meth:`~.cmd2.Cmd.poutput` method is similar to the Python +`built-in print function <https://docs.python.org/3/library/functions.html#print>`_. :meth:`~.cmd2.Cmd.poutput` adds two +conveniences: -The ``quiet`` setting controls whether ``self.pfeedback()`` actually produces -any output. If ``quiet`` is ``False``, then the output will be produced. If -``quiet`` is ``True``, no output will be produced. + 1. Since users can pipe output to a shell command, it catches + ``BrokenPipeError`` and outputs the contents of + ``self.broken_pipe_warning`` to ``stderr``. ``self.broken_pipe_warning`` + defaults to an empty string so this method will just swallow the exception. + If you want to show an error message, put it in + ``self.broken_pipe_warning`` when you initialize :mod:`~cmd2.cmd2.Cmd`. -This makes ``self.pfeedback()`` useful for non-essential output like status -messages. Users can control whether they would like to see these messages by -changing the value of the ``quiet`` setting. + 2. It examines and honors the :ref:`features/settings:allow_style` setting. + See :ref:`features/generating_output:Colored Output` below for more details. + +Here's a simple command that shows this method in action:: + + def do_echo(self, args): + """A simple command showing how poutput() works""" + self.poutput(args) + + +Error Messages +-------------- + +When an error occurs in your program, you can display it on ``sys.stderr`` by +calling the :meth:`~.cmd2.Cmd.perror` method. By default this method applies +:meth:`cmd2.ansi.style_error` to the output. + + +Warning Messages +---------------- + +:meth:`~.cmd2.Cmd.pwarning` is just like :meth:`~.cmd2.Cmd.perror` but applies +:meth:`cmd2.ansi.style_warning` to the output. + + +Feedback +-------- + +You may have the need to display information to the user which is not intended +to be part of the generated output. This could be debugging information or +status information about the progress of long running commands. It's not +output, it's not error messages, it's feedback. If you use the +:ref:`features/settings:Timing` setting, the output of how long it took the +command to run will be output as feedback. You can use the +:meth:`~.cmd2.Cmd.pfeedback` method to produce this type of output, and +several :ref:`features/settings:Settings` control how it is handled. + +If the :ref:`features/settings:quiet` setting is ``True``, then calling +:meth:`~.cmd2.Cmd.pfeedback` produces no output. If +:ref:`features/settings:quiet` is ``False``, the +:ref:`features/settings:feedback_to_output` setting is consulted to determine +whether to send the output to ``stdout`` or ``stderr``. + + +Exceptions +---------- + +If your app catches an exception and you would like to display the exception to +the user, the :meth:`~.cmd2.Cmd.pexcept` method can help. The default behavior +is to just display the message contained within the exception. However, if the +:ref:`features/settings:debug` setting is ``True``, then the entire stack trace +will be displayed. + + +Paging Output +------------- + +If you know you are going to generate a lot of output, you may want to display +it in a way that the user can scroll forwards and backwards through it. If you +pass all of the output to be displayed in a single call to +:meth:`~.cmd2.Cmd.ppaged`, it will be piped to an operating system appropriate +shell command to page the output. On Windows, the output is piped to ``more``; +on Unix-like operating systems like MacOS and Linux, it is piped to ``less``. Colored Output -------------- -The output methods in the previous section all honor the ``allow_style`` -setting, which has three possible values: +You can add your own `ANSI escape sequences +<https://en.wikipedia.org/wiki/ANSI_escape_code#Colors>`_ to your output which +tell the terminal to change the foreground and background colors. If you want +to give yourself a headache, you can generate these by hand. You could also use +a Python color library like `plumbum.colors +<https://plumbum.readthedocs.io/en/latest/colors.html>`_, `colored +<https://gitlab.com/dslackw/colored>`_, or `colorama +<https://github.com/tartley/colorama>`_. Colorama is unique because when it's +running on Windows, it wraps ``stdout``, looks for ANSI escape sequences, and +converts them into the appropriate ``win32`` calls to modify the state of the +terminal. + +``cmd2`` imports and uses Colorama and provides a number of convenience methods +for generating colorized output, measuring the screen width of colorized +output, setting the window title in the terminal, and removing ANSI text style +escape codes from a string. These functions are all documentated in +:mod:`cmd2.ansi`. + +After adding the desired escape sequences to your output, you should use one of +these methods to present the output to the user: + +- :meth:`.cmd2.Cmd.poutput` +- :meth:`.cmd2.Cmd.perror` +- :meth:`.cmd2.Cmd.pwarning` +- :meth:`.cmd2.Cmd.pexcept` +- :meth:`.cmd2.Cmd.pfeedback` +- :meth:`.cmd2.Cmd.ppaged` + +These methods all honor the :ref:`features/settings:allow_style` setting, which +users can modify to control whether these escape codes are passed through to +the terminal or not. + + +Aligning Text +-------------- + +If you would like to generate output which is left, center, or right aligned within a +specified width or the terminal width, the following functions can help: + +- :meth:`cmd2.utils.align_left` +- :meth:`cmd2.utils.align_center` +- :meth:`cmd2.utils.align_right` -Never - poutput(), pfeedback(), and ppaged() strip all ANSI style sequences - which instruct the terminal to colorize output +These functions differ from Python's string justifying functions in that they support +characters with display widths greater than 1. Additionally, ANSI style sequences are safely +ignored and do not count toward the display width. This means colored text is supported. If +text has line breaks, then each line is aligned independently. -Terminal - (the default value) poutput(), pfeedback(), and ppaged() do not strip any - ANSI style sequences when the output is a terminal, but if the output is a - pipe or a file the style sequences are stripped. If you want colorized - output you must add ANSI style sequences using either cmd2's internal ansi - module or another color library such as `plumbum.colors`, `colorama`, or - `colored`. -Always - poutput(), pfeedback(), and ppaged() never strip ANSI style sequences, - regardless of the output destination -Colored and otherwise styled output can be generated using the `ansi.style()` -function: +Columnar Output +--------------- -.. automethod:: cmd2.ansi.style +When generating output in multiple columns, you often need to calculate the +width of each item so you can pad it appropriately with spaces. However, there +are categories of Unicode characters that occupy 2 cells, and other that occupy +0. To further complicate matters, you might have included ANSI escape sequences +in the output to generate colors on the terminal. +The :meth:`cmd2.ansi.style_aware_wcswidth` function solves both of these +problems. Pass it a string, and regardless of which Unicode characters and ANSI +text style escape sequences it contains, it will tell you how many characters on the +screen that string will consume when printed. diff --git a/docs/features/index.rst b/docs/features/index.rst index efeb9e6c..dc64badd 100644 --- a/docs/features/index.rst +++ b/docs/features/index.rst @@ -5,6 +5,7 @@ Features :maxdepth: 1 argument_processing + builtin_commands clipboard commands completion diff --git a/docs/features/settings.rst b/docs/features/settings.rst index f305a68c..627f61a9 100644 --- a/docs/features/settings.rst +++ b/docs/features/settings.rst @@ -1,68 +1,138 @@ Settings ======== -- current settings and what they do -- how a developer can add their own -- how to hide built in settings from a user +Settings provide a mechanism for a user to control the behavior of a ``cmd2`` +based application. A setting is stored in an instance attribute on your +subclass of :class:`cmd2.cmd2.Cmd` and must also appear in the +:attr:`cmd2.cmd2.Cmd.settable` dictionary. Developers may set default values +for these settings and users can modify them at runtime using the +:ref:`features/builtin_commands:set` command. Developers can +:ref:`features/settings:Create New Settings` and can also +:ref:`features/settings:Hide Builtin Settings` from the user. -Built In Settings + +Builtin Settings ----------------- -``cmd2`` has a number of built in settings, which a developer can set a default -value, and which users can modify to change the behavior of the application. +``cmd2`` has a number of builtin settings. These settings control the behavior +of certain application features and :ref:`features/builtin_commands:Builtin +Commands`. Users can use the :ref:`features/builtin_commands:set` command to +show all settings and to modify the value of any setting. -Timing -~~~~~~ +allow_style +~~~~~~~~~~~ + +Output generated by ``cmd2`` programs may contain ANSI escape seqences which +instruct the terminal to apply colors or text styling (i.e. bold) to the +output. The ``allow_style`` setting controls the behavior of these escape +sequences in output generated with any of the following methods: + +- :meth:`.cmd2.Cmd.poutput` +- :meth:`.cmd2.Cmd.perror` +- :meth:`.cmd2.Cmd.pwarning` +- :meth:`.cmd2.Cmd.pexcept` +- :meth:`.cmd2.Cmd.pfeedback` +- :meth:`.cmd2.Cmd.ppaged` + +This setting can be one of three values: + +- ``Never`` - all ANSI escape sequences which instruct the terminal to style + output are stripped from the output. + +- ``Terminal`` - (the default value) pass through ANSI escape sequences when + the output is being sent to the terminal, but if the output is redirected to + a pipe or a file the escape sequences are stripped. + +- ``Always`` - ANSI escape sequences are always passed through to the output + + +continuation_prompt +~~~~~~~~~~~~~~~~~~~ -Setting ``App.timing`` to ``True`` outputs timing data after every application -command is executed. |settable| +When a user types a :ref:`Multiline Command +<features/multiline_commands:Multiline Commands>` it may span more than one +line of input. The prompt for the first line of input is specified by the +:ref:`features/settings:prompt` setting. The prompt for subsequent lines of +input is defined by this setting. -Echo +debug +~~~~~ + +The default value of this setting is ``False``, which causes the +:meth:`~.cmd2.Cmd.pexcept` method to only display the message from an +exception. However, if the debug setting is ``True``, then the entire stack +trace will be printed. + + +echo ~~~~ If ``True``, each command the user issues will be repeated to the screen before -it is executed. This is particularly useful when running scripts. +it is executed. This is particularly useful when running scripts. This behavior +does not occur when a running command at the prompt. -Debug -~~~~~ +editor +~~~~~~ + +Similar to the ``EDITOR`` shell variable, this setting contains the name of the +program which should be run by the :ref:`features/builtin_commands:edit` +command. + + +feedback_to_output +~~~~~~~~~~~~~~~~~~ -Setting ``App.debug`` to ``True`` will produce detailed error stacks whenever -the application generates an error. |settable| +Controls whether feedback generated with the :meth:`~cmd2.cmd2.Cmd.pfeedback` +method is sent to ``sys.stdout`` or ``sys.stderr``. If ``False`` the output +will be sent to ``sys.stderr`` -.. |settable| replace:: The user can ``set`` this parameter - during application execution. - (See :ref:`parameters`) +If ``True`` the output is sent to ``stdout`` (which is often the screen but may +be :ref:`redirected <features/redirection:Output Redirection and Pipes>`). The +feedback output will be mixed in with and indistinguishable from output +generated with :meth:`~cmd2.cmd2.Cmd.poutput`. -.. _parameters: -Other user-settable parameters -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +locals_in_py +~~~~~~~~~~~~ -A list of all user-settable parameters, with brief -comments, is viewable from within a running application -with:: +Allow access to your application in one of the +:ref:`features/embedded_python_shells:Embedded Python Shells` via ``self``. - (Cmd) set --long - allow_style: Terminal # Allow ANSI text style sequences in output (valid values: Terminal, Always, Never) - continuation_prompt: > # On 2nd+ line of input - debug: False # Show full error stack on error - echo: False # Echo command issued into output - editor: vim # Program used by ``edit`` - feedback_to_output: False # include nonessentials in `|`, `>` results - locals_in_py: False # Allow access to your application in py via self - max_completion_items: 50 # Maximum number of CompletionItems to display during tab completion - prompt: (Cmd) # The prompt issued to solicit input - quiet: False # Don't print nonessential feedback - timing: False # Report execution times -Any of these user-settable parameters can be set while running your app with -the ``set`` command like so:: +max_completion_items +~~~~~~~~~~~~~~~~~~~~ - set allow_style Never +Maximum number of CompletionItems to display during tab completion. A CompletionItem +is a special kind of tab-completion hint which displays both a value and description +and uses one line for each hint. Tab complete the ``set`` command for an example. +If the number of tab-completion hints exceeds ``max_completion_items``, then they will +be displayed in the typical columnized format and will not include the description text +of the CompletionItem. + + +prompt +~~~~~~ + +This setting contains the string which should be printed as a prompt for user +input. + + +quiet +~~~~~ + +If ``True``, output generated by calling :meth:`~.cmd2.Cmd.pfeedback` is +suppressed. If ``False``, the :ref:`features/settings:feedback_to_output` +setting controls where the output is sent. + + +timing +~~~~~~ + +If ``True``, the elapsed time is reported for each command executed. Create New Settings @@ -100,3 +170,23 @@ changes a setting, and will receive both the old value and the new value. now: 13 (Cmd) sunbathe It's 13 C - are you a penguin? + + +Hide Builtin Settings +---------------------- + +You may want to prevent a user from modifying a builtin setting. A setting +must appear in the :attr:`cmd2.cmd2.Cmd.settable` dictionary in order for it +to be available to the :ref:`features/builtin_commands:set` command. + +Let's say your program does not have any +:ref:`features/multiline_commands:Multiline Commands`. You might want to hide +the :ref:`features/settings:continuation_prompt` setting from your users since +it is only applicable to multiline commands. To do so, remove it from the +:attr:`cmd2.cmd2.Cmd.settable` dictionary after you initialize your object:: + + class MyApp(cmd2.Cmd): + + def __init__(self): + super().__init__() + self.settable.pop('continuation_prompt') diff --git a/docs/features/startup_commands.rst b/docs/features/startup_commands.rst index 93984db6..aaaf7722 100644 --- a/docs/features/startup_commands.rst +++ b/docs/features/startup_commands.rst @@ -5,7 +5,7 @@ Startup Commands after your application starts up: 1. Commands at Invocation -1. Startup Script +2. Startup Script Commands run as part of a startup script are always run immediately after the application finishes initializing so they are guaranteed to run before any diff --git a/docs/migrating/incompatibilities.rst b/docs/migrating/incompatibilities.rst index 7526b51b..ba6f2ed1 100644 --- a/docs/migrating/incompatibilities.rst +++ b/docs/migrating/incompatibilities.rst @@ -46,15 +46,16 @@ writing a :ref:`Postparsing Hook <features/hooks:Postparsing Hooks>`. Cmd.cmdqueue ------------ + In cmd_, the `Cmd.cmdqueue <https://docs.python.org/3/library/cmd.html#cmd.Cmd.cmdqueue>`_ attribute -contains A list of queued input lines. The cmdqueue list is checked in +contains a list of queued input lines. The cmdqueue list is checked in ``cmdloop()`` when new input is needed; if it is nonempty, its elements will be processed in order, as if entered at the prompt. -Since version 0.9.13 ``cmd2`` has removed support for ``Cmd.cmdqueue``. -Because ``cmd2`` supports running commands via the main ``cmdloop()``, text -scripts, Python scripts, transcripts, and history replays, the only way to -preserve consistent behavior across these methods was to eliminate the command -queue. Additionally, reasoning about application behavior is much easier -without this queue present. +Since version 0.9.13 ``cmd2`` has removed support for ``Cmd.cmdqueue``. Because +``cmd2`` supports running commands via the main ``cmdloop()``, text scripts, +Python scripts, transcripts, and history replays, the only way to preserve +consistent behavior across these methods was to eliminate the command queue. +Additionally, reasoning about application behavior is much easier without this +queue present. diff --git a/docs/migrating/next_steps.rst b/docs/migrating/next_steps.rst index f43c69c9..d95473ee 100644 --- a/docs/migrating/next_steps.rst +++ b/docs/migrating/next_steps.rst @@ -15,9 +15,8 @@ For all but the simplest of commands, it's probably easier to use `argparse ``cmd2`` provides a ``@with_argparser()`` decorator which associates an ``ArgumentParser`` object with one of your commands. Using this method will: -1. Pass your command a `Namespace -<https://docs.python.org/3/library/argparse.html#argparse.Namespace>`_ -containing the arguments instead of a string of text. +1. Pass your command a `Namespace <https://docs.python.org/3/library/argparse.html#argparse.Namespace>`_ + containing the arguments instead of a string of text. 2. Properly handle quoted string input from your users. @@ -52,8 +51,8 @@ Generating Output ----------------- If your program generates output by printing directly to ``sys.stdout``, you -should consider switching to :meth:`cmd2.cmd2.Cmd.poutput`, -:meth:`cmd2.cmd2.Cmd.perror`, and :meth:`cmd2.cmd2.Cmd.pfeedback`. These +should consider switching to :meth:`~cmd2.cmd2.Cmd.poutput`, +:meth:`~cmd2.cmd2.Cmd.perror`, and :meth:`~cmd2.cmd2.Cmd.pfeedback`. These methods work with several of the built in :ref:`features/settings:Settings` to allow the user to view or suppress feedback (i.e. progress or status output). They also properly handle ansi colored output according to user preference. diff --git a/examples/colors.py b/examples/colors.py index ea8be479..bbb3b2ad 100755 --- a/examples/colors.py +++ b/examples/colors.py @@ -24,9 +24,11 @@ Always regardless of the output destination """ import argparse +from typing import Any import cmd2 from cmd2 import ansi +from colorama import Fore, Back, Style class CmdLineApp(cmd2.Cmd): @@ -66,9 +68,31 @@ class CmdLineApp(cmd2.Cmd): repetitions = args.repeat or 1 output_str = ansi.style(' '.join(words), fg=args.fg, bg=args.bg, bold=args.bold, underline=args.underline) - for i in range(min(repetitions, self.maxrepeats)): + for _ in range(min(repetitions, self.maxrepeats)): # .poutput handles newlines, and accommodates output redirection too self.poutput(output_str) + self.perror('error message at the end') + + @staticmethod + def perror(msg: Any, *, end: str = '\n', apply_style: bool = True) -> None: + """Override perror() method from `cmd2.Cmd` + + Use colorama native approach for styling the text instead of `cmd2.ansi` methods + + :param msg: message to print (anything convertible to a str with '{}'.format() is OK) + :param end: string appended after the end of the message, default a newline + :param apply_style: If True, then ansi.style_error will be applied to the message text. Set to False in cases + where the message text already has the desired style. Defaults to True. + """ + if apply_style: + final_msg = "{}{}{}{}".format(Fore.RED, Back.YELLOW, msg, Style.RESET_ALL) + else: + final_msg = "{}".format(msg) + ansi.ansi_aware_write(sys.stderr, final_msg + end) + + def do_timetravel(self, args): + """A command which always generates an error message, to demonstrate custom error colors""" + self.perror('Mr. Fusion failed to start. Could not energize flux capacitor.') if __name__ == '__main__': diff --git a/examples/plumbum_colors.py b/examples/plumbum_colors.py index 8eaf3fe8..fe692805 100755 --- a/examples/plumbum_colors.py +++ b/examples/plumbum_colors.py @@ -104,7 +104,7 @@ class CmdLineApp(cmd2.Cmd): repetitions = args.repeat or 1 output_str = ansi.style(' '.join(words), fg=args.fg, bg=args.bg, bold=args.bold, underline=args.underline) - for i in range(min(repetitions, self.maxrepeats)): + for _ in range(min(repetitions, self.maxrepeats)): # .poutput handles newlines, and accommodates output redirection too self.poutput(output_str) |