diff options
-rw-r--r-- | cmd2/ansi.py | 12 | ||||
-rw-r--r-- | cmd2/cmd2.py | 29 | ||||
-rw-r--r-- | docs/api/ansi.rst | 5 | ||||
-rw-r--r-- | docs/api/cmd.rst | 6 | ||||
-rw-r--r-- | docs/api/index.rst | 1 | ||||
-rw-r--r-- | docs/doc_conventions.rst | 170 | ||||
-rw-r--r-- | docs/features/builtin_commands.rst | 48 | ||||
-rw-r--r-- | docs/features/generating_output.rst | 191 | ||||
-rw-r--r-- | docs/features/index.rst | 1 | ||||
-rw-r--r-- | docs/features/settings.rst | 158 | ||||
-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, 518 insertions, 157 deletions
diff --git a/cmd2/ansi.py b/cmd2/ansi.py index c875a9d1..86161ac2 100644 --- a/cmd2/ansi.py +++ b/cmd2/ansi.py @@ -91,6 +91,7 @@ def ansi_safe_wcswidth(text: str) -> int: Wrap wcswidth to make it compatible with strings that contains ANSI escape sequences :param text: the string being measured + :return: the width of the string when printed to the terminal """ # Strip ANSI escape sequences since they cause wcswidth to return -1 return wcswidth(strip_ansi(text)) @@ -114,7 +115,7 @@ def fg_lookup(fg_name: str) -> str: :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()] @@ -128,7 +129,7 @@ def bg_lookup(bg_name: str) -> str: :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()] @@ -184,8 +185,13 @@ def style(text: Any, *, fg: str = '', bg: str = '', bold: bool = False, underlin # 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 style() to generate bold green text""" + style_warning = functools.partial(style, fg='bright_yellow') +"""Partial function supplying arguments to ansi.style() to generate yellow text""" + style_error = functools.partial(style, fg='bright_red') +"""Partial function supplying arguments to ansi.style() to generate bright red text""" def async_alert_str(*, terminal_columns: int, prompt: str, line: str, cursor_offset: int, alert_msg: str) -> str: @@ -246,6 +252,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 753fb5ab..b025043e 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -133,6 +133,9 @@ class Cmd(cmd.Cmd): " This command is for internal use and is not intended to be called from the\n" " command line.") + debug = False + """Set to True to display a full stack trace when exceptions occur""" + # Sorting keys for strings ALPHABETICAL_SORT_KEY = utils.norm_fold NATURAL_SORT_KEY = utils.natural_keys @@ -222,6 +225,12 @@ class Cmd(cmd.Cmd): 'quiet': "Don't print nonessential feedback", 'timing': 'Report execution times' } + """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. + """ # Commands to exclude from the help menu and tab completion self.hidden_commands = ['eof', '_relative_load', '_relative_run_script'] @@ -289,8 +298,8 @@ 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" + """The error message displayed when a non-existent command is run.""" - # If this string is non-empty, then this warning message will print if a broken pipe error occurs while printing self.broken_pipe_warning = '' # Commands that will run at the beginning of the command loop @@ -413,6 +422,24 @@ class Cmd(cmd.Cmd): self.perror('Invalid value: {} (valid values: {}, {}, {})'.format(new_val, ansi.ANSI_TERMINAL, ansi.ANSI_ALWAYS, ansi.ANSI_NEVER)) + @property + def broken_pipe_warning(self) -> str: + """Message to display if a BrokenPipeError is raised while writing output. + + The following methods catch BrokenPipeError exceptions and output this message: + + - :meth:`~cmd2.cmd2.Cmd.poutput()` + - :meth:`~cmd2.cmd2.Cmd.ppaged()` + + The default value is an empty string meaning the BrokenPipeError is + silently swallowed. + """ + return self.broken_pipe_error + + @broken_pipe_warning.setter + def broken_pipe_warning(self, new_val: str) -> None: + self.broken_pipe_error = new_val + def _completion_supported(self) -> bool: """Return whether tab completion is supported""" return self.use_rawinput and self.completekey and rl_type != RlType.NONE 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..f50db544 100644 --- a/docs/api/cmd.rst +++ b/docs/api/cmd.rst @@ -3,3 +3,9 @@ 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. + 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..082235da 100644 --- a/docs/doc_conventions.rst +++ b/docs/doc_conventions.rst @@ -19,53 +19,107 @@ 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 + ~~~~~~~~~~~~~~~~ + +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 +----------- -If you want to show other code, like shell commands, then use ``.. code-block: -shell``. +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>`_ @@ -73,7 +127,7 @@ 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,60 @@ 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?] +Links to API Reference +---------------------- -Autolinking ------------ +To reference a method or function, use one of the following approaches: +1. Reference the full dotted path of the method:: -Referencing cmd2 API documentation ----------------------------------- + The :meth:`cmd2.cmd2.Cmd.poutput` method is similar to the Python built-in + print function. +Which renders as: The :meth:`cmd2.cmd2.Cmd.poutput` method is similar to the +Python built-in print function. -Info and Warning Callouts -------------------------- +2. Reference the full dotted path to the method, but only display the method +name:: + The :meth:`~cmd2.cmd2.Cmd.poutput` method is similar to the Python built-in print function. -Wrapping --------- +Which renders as: 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. +3. Reference a portion of the dotted path of the method:: + The :meth:`.cmd2.Cmd.poutput` method is similar to the Python built-in print + function. -Referencing cmd2 ------------------ +Which renders as: The :meth:`.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. +Avoid either of these approaches: -Style Checker -------------- +1. Reference just the class name without enough dotted path:: -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:`.Cmd.poutput` method is similar to the Python built-in print + function. -.. code-block:: shell +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. - $ invoke doc8 +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..ab4ca60e --- /dev/null +++ b/docs/features/builtin_commands.rst @@ -0,0 +1,48 @@ +Builtin Commands +================ + +Applications which subclass :class:`cmd2.cmd2.Cmd` inherit a number of commands +which may be useful to your users. + +edit +---- + +This command launches an editor program and instructs it to open the given file +name. Here's an example:: + + (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:: + + (Cmd) set --long + allow_ansi: Terminal # Allow ANSI escape 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:: + + (Cmd) set allow_ansi Never + + +Removing A Builtin Command +-------------------------- + +[TODO] show how to remove a builtin command if you don't want it available to +your users. diff --git a/docs/features/generating_output.rst b/docs/features/generating_output.rst index 2ee820f1..34416092 100644 --- a/docs/features/generating_output.rst +++ b/docs/features/generating_output.rst @@ -1,71 +1,164 @@ 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``, ``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()`` +``cmd2`` 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. ``cmd2`` +also adds a number of output related methods to ``Cmd2.Cmd`` 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.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 ``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_ansi` 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) Colored Output -------------- -The output methods in the previous section all honor the ``allow_ansi`` -setting, which has three possible values: +You may want to generate output in different colors, which is typically done by +adding `ANSI escape sequences +<https://en.wikipedia.org/wiki/ANSI_escape_code#Colors>`_ 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 another +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 escape +codes from a string. These functions are all documentated in +:mod:`cmd2.ansi`. + +:mod:`cmd2.cmd2.Cmd` includes an :ref:`features/settings:allow_ansi` setting, +which controls whether ANSI escape sequences that instruct the terminal to +colorize output are stripped from the output. The recommended approach is to +construct your application so that it generates colorized output, and then +allow your users to use this setting to remove the colorization if they do not +want it. + +Output generated by any of these +methods will honor the :ref:`features/settings:allow_ansi` setting: + +- :meth:`~.cmd2.Cmd.poutput` +- :meth:`~.cmd2.Cmd.perror` +- :meth:`~.cmd2.Cmd.pwarning` +- :meth:`~.cmd2.Cmd.pexcept` +- :meth:`~.cmd2.Cmd.pfeedback` +- :meth:`~.cmd2.Cmd.ppaged` + + +Error Messages +-------------- + +When an error occurs in your program, you can display it on ``sys.stderr`` by +calling the :meth:`~.cmd2.Cmd.perror` method. + + +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. -Never - poutput(), pfeedback(), and ppaged() strip all ANSI escape sequences - which instruct the terminal to colorize output +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``. + + +Centering Text +-------------- -Terminal - (the default value) poutput(), pfeedback(), and ppaged() do not strip any - ANSI escape sequences when the output is a terminal, but if the output is a - pipe or a file the escape sequences are stripped. If you want colorized - output you must add ANSI escape sequences using either cmd2's internal ansi - module or another color library such as `plumbum.colors`, `colorama`, or - `colored`. +If you would like to generate output which is centered in the user's terminal, +the :meth:`cmd2.utils.center_text` method can help. Pass it a string and it +will figure out the width of the terminal and return you a new string, +appropriately padded so it will be centered. -Always - poutput(), pfeedback(), and ppaged() never strip ANSI escape 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.ansi_safe_wcswidth` function solves both of these +problems. Pass it a string, and regardless of which Unicode characters and ANSI +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 b0575468..88d76dc8 100644 --- a/docs/features/settings.rst +++ b/docs/features/settings.rst @@ -1,68 +1,128 @@ 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_ansi +~~~~~~~~~~ + +The ``allow_ansi`` setting controls the behavior of ANSI escape sequences +in output generated with any of the following methods: + +- ``poutput()`` +- ``perror()`` +- ``pwarning()`` +- ``pfeedback()`` +- ``ppaged()`` + +This setting can be one of three values: + +- ``Never`` - all ANSI escape sequences which instruct the terminal to colorize + 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, regardless + -Setting ``App.timing`` to ``True`` outputs timing data after every application -command is executed. |settable| +continuation_prompt +~~~~~~~~~~~~~~~~~~~ +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. -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. + -Setting ``App.debug`` to ``True`` will produce detailed error stacks whenever -the application generates an error. |settable| +feedback_to_output +~~~~~~~~~~~~~~~~~~ -.. |settable| replace:: The user can ``set`` this parameter - during application execution. - (See :ref:`parameters`) +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`` -.. _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`. -Other user-settable parameters -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -A list of all user-settable parameters, with brief -comments, is viewable from within a running application -with:: +locals_in_py +~~~~~~~~~~~~ - (Cmd) set --long - allow_ansi: Terminal # Allow ANSI escape 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 +Allow access to your application in one of the +:ref:`features/embedded_python_shells:Embedded Python Shells` via ``self``. -Any of these user-settable parameters can be set while running your app with -the ``set`` command like so:: - set allow_ansi Never +max_completion_items +~~~~~~~~~~~~~~~~~~~~ +The maximum number of tab-completion items to display. + + +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 +160,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 8c54dfa4..7a4d15e6 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 a969c4de..b4f0ad1c 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) |