cmd2 4.0.1__tar.gz → 4.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. {cmd2-4.0.1 → cmd2-4.1.1}/.pre-commit-config.yaml +2 -2
  2. {cmd2-4.0.1 → cmd2-4.1.1}/CHANGELOG.md +7 -0
  3. {cmd2-4.0.1 → cmd2-4.1.1}/PKG-INFO +1 -1
  4. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/argparse_utils.py +3 -3
  5. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/cmd2.py +35 -8
  6. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2.egg-info/PKG-INFO +1 -1
  7. cmd2-4.1.1/cmd2.egg-info/scm_version.json +8 -0
  8. cmd2-4.1.1/examples/async_printing.py +181 -0
  9. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_cmd2.py +71 -7
  10. cmd2-4.0.1/cmd2.egg-info/scm_version.json +0 -8
  11. cmd2-4.0.1/examples/async_printing.py +0 -182
  12. {cmd2-4.0.1 → cmd2-4.1.1}/.prettierignore +0 -0
  13. {cmd2-4.0.1 → cmd2-4.1.1}/.prettierrc +0 -0
  14. {cmd2-4.0.1 → cmd2-4.1.1}/.typos.toml +0 -0
  15. {cmd2-4.0.1 → cmd2-4.1.1}/GEMINI.md +0 -0
  16. {cmd2-4.0.1 → cmd2-4.1.1}/LICENSE +0 -0
  17. {cmd2-4.0.1 → cmd2-4.1.1}/MANIFEST.in +0 -0
  18. {cmd2-4.0.1 → cmd2-4.1.1}/Makefile +0 -0
  19. {cmd2-4.0.1 → cmd2-4.1.1}/README.md +0 -0
  20. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/__init__.py +0 -0
  21. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/annotated.py +0 -0
  22. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/argparse_completer.py +0 -0
  23. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/clipboard.py +0 -0
  24. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/colors.py +0 -0
  25. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/command_set.py +0 -0
  26. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/completion.py +0 -0
  27. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/constants.py +0 -0
  28. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/decorators.py +0 -0
  29. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/exceptions.py +0 -0
  30. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/history.py +0 -0
  31. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/parsing.py +0 -0
  32. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/plugin.py +0 -0
  33. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/pt_utils.py +0 -0
  34. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/py.typed +0 -0
  35. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/py_bridge.py +0 -0
  36. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/rich_utils.py +0 -0
  37. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/string_utils.py +0 -0
  38. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/styles.py +0 -0
  39. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/theme.py +0 -0
  40. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/types.py +0 -0
  41. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2/utils.py +0 -0
  42. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2.egg-info/SOURCES.txt +0 -0
  43. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2.egg-info/dependency_links.txt +0 -0
  44. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2.egg-info/requires.txt +0 -0
  45. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2.egg-info/scm_file_list.json +0 -0
  46. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2.egg-info/top_level.txt +0 -0
  47. {cmd2-4.0.1 → cmd2-4.1.1}/cmd2.png +0 -0
  48. {cmd2-4.0.1 → cmd2-4.1.1}/codecov.yml +0 -0
  49. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/annotated.md +0 -0
  50. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/argparse_completer.md +0 -0
  51. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/argparse_utils.md +0 -0
  52. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/clipboard.md +0 -0
  53. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/cmd.md +0 -0
  54. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/colors.md +0 -0
  55. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/command_set.md +0 -0
  56. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/completion.md +0 -0
  57. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/constants.md +0 -0
  58. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/decorators.md +0 -0
  59. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/exceptions.md +0 -0
  60. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/history.md +0 -0
  61. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/index.md +0 -0
  62. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/parsing.md +0 -0
  63. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/plugin.md +0 -0
  64. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/pt_utils.md +0 -0
  65. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/py_bridge.md +0 -0
  66. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/rich_utils.md +0 -0
  67. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/string_utils.md +0 -0
  68. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/styles.md +0 -0
  69. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/theme.md +0 -0
  70. {cmd2-4.0.1 → cmd2-4.1.1}/docs/api/utils.md +0 -0
  71. {cmd2-4.0.1 → cmd2-4.1.1}/docs/doc_conventions.md +0 -0
  72. {cmd2-4.0.1 → cmd2-4.1.1}/docs/examples/alternate_event_loops.md +0 -0
  73. {cmd2-4.0.1 → cmd2-4.1.1}/docs/examples/examples.md +0 -0
  74. {cmd2-4.0.1 → cmd2-4.1.1}/docs/examples/getting_started.md +0 -0
  75. {cmd2-4.0.1 → cmd2-4.1.1}/docs/examples/index.md +0 -0
  76. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/annotated.md +0 -0
  77. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/argument_processing.md +0 -0
  78. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/async_commands.md +0 -0
  79. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/builtin_commands.md +0 -0
  80. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/clipboard.md +0 -0
  81. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/commands.md +0 -0
  82. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/completion.md +0 -0
  83. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/disable_commands.md +0 -0
  84. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/embedded_python_shells.md +0 -0
  85. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/generating_output.md +0 -0
  86. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/help.md +0 -0
  87. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/history.md +0 -0
  88. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/hooks.md +0 -0
  89. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/index.md +0 -0
  90. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/initialization.md +0 -0
  91. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/misc.md +0 -0
  92. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/modular_commands.md +0 -0
  93. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/multiline_commands.md +0 -0
  94. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/os.md +0 -0
  95. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/packaging.md +0 -0
  96. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/plugins.md +0 -0
  97. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/prompt.md +0 -0
  98. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/redirection.md +0 -0
  99. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/scripting.md +0 -0
  100. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/settings.md +0 -0
  101. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/shortcuts_aliases_macros.md +0 -0
  102. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/startup_commands.md +0 -0
  103. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/table_creation.md +0 -0
  104. {cmd2-4.0.1 → cmd2-4.1.1}/docs/features/theme.md +0 -0
  105. {cmd2-4.0.1 → cmd2-4.1.1}/docs/index.md +0 -0
  106. {cmd2-4.0.1 → cmd2-4.1.1}/docs/javascripts/readthedocs.js +0 -0
  107. {cmd2-4.0.1 → cmd2-4.1.1}/docs/migrating/incompatibilities.md +0 -0
  108. {cmd2-4.0.1 → cmd2-4.1.1}/docs/migrating/index.md +0 -0
  109. {cmd2-4.0.1 → cmd2-4.1.1}/docs/migrating/minimum.md +0 -0
  110. {cmd2-4.0.1 → cmd2-4.1.1}/docs/migrating/next_steps.md +0 -0
  111. {cmd2-4.0.1 → cmd2-4.1.1}/docs/migrating/why.md +0 -0
  112. {cmd2-4.0.1 → cmd2-4.1.1}/docs/mixins/index.md +0 -0
  113. {cmd2-4.0.1 → cmd2-4.1.1}/docs/mixins/mixin_template.md +0 -0
  114. {cmd2-4.0.1 → cmd2-4.1.1}/docs/overrides/main.html +0 -0
  115. {cmd2-4.0.1 → cmd2-4.1.1}/docs/overview/alternatives.md +0 -0
  116. {cmd2-4.0.1 → cmd2-4.1.1}/docs/overview/index.md +0 -0
  117. {cmd2-4.0.1 → cmd2-4.1.1}/docs/overview/installation.md +0 -0
  118. {cmd2-4.0.1 → cmd2-4.1.1}/docs/overview/integrating.md +0 -0
  119. {cmd2-4.0.1 → cmd2-4.1.1}/docs/overview/resources.md +0 -0
  120. {cmd2-4.0.1 → cmd2-4.1.1}/docs/stylesheets/cmd2.css +0 -0
  121. {cmd2-4.0.1 → cmd2-4.1.1}/docs/testing.md +0 -0
  122. {cmd2-4.0.1 → cmd2-4.1.1}/docs/upgrades.md +0 -0
  123. {cmd2-4.0.1 → cmd2-4.1.1}/examples/.cmd2rc +0 -0
  124. {cmd2-4.0.1 → cmd2-4.1.1}/examples/README.md +0 -0
  125. {cmd2-4.0.1 → cmd2-4.1.1}/examples/annotated_example.py +0 -0
  126. {cmd2-4.0.1 → cmd2-4.1.1}/examples/argparse_completion.py +0 -0
  127. {cmd2-4.0.1 → cmd2-4.1.1}/examples/argparse_example.py +0 -0
  128. {cmd2-4.0.1 → cmd2-4.1.1}/examples/async_call.py +0 -0
  129. {cmd2-4.0.1 → cmd2-4.1.1}/examples/async_commands.py +0 -0
  130. {cmd2-4.0.1 → cmd2-4.1.1}/examples/basic_completion.py +0 -0
  131. {cmd2-4.0.1 → cmd2-4.1.1}/examples/cmd2_history.dat +0 -0
  132. {cmd2-4.0.1 → cmd2-4.1.1}/examples/cmd_as_argument.py +0 -0
  133. {cmd2-4.0.1 → cmd2-4.1.1}/examples/color.py +0 -0
  134. {cmd2-4.0.1 → cmd2-4.1.1}/examples/command_sets.py +0 -0
  135. {cmd2-4.0.1 → cmd2-4.1.1}/examples/completion_item_choices.py +0 -0
  136. {cmd2-4.0.1 → cmd2-4.1.1}/examples/custom_parser.py +0 -0
  137. {cmd2-4.0.1 → cmd2-4.1.1}/examples/custom_types.py +0 -0
  138. {cmd2-4.0.1 → cmd2-4.1.1}/examples/default_categories.py +0 -0
  139. {cmd2-4.0.1 → cmd2-4.1.1}/examples/dynamic_commands.py +0 -0
  140. {cmd2-4.0.1 → cmd2-4.1.1}/examples/environment.py +0 -0
  141. {cmd2-4.0.1 → cmd2-4.1.1}/examples/event_loops.py +0 -0
  142. {cmd2-4.0.1 → cmd2-4.1.1}/examples/exit_code.py +0 -0
  143. {cmd2-4.0.1 → cmd2-4.1.1}/examples/getting_started.py +0 -0
  144. {cmd2-4.0.1 → cmd2-4.1.1}/examples/hello_cmd2.py +0 -0
  145. {cmd2-4.0.1 → cmd2-4.1.1}/examples/help_categories.py +0 -0
  146. {cmd2-4.0.1 → cmd2-4.1.1}/examples/hooks.py +0 -0
  147. {cmd2-4.0.1 → cmd2-4.1.1}/examples/migrating.py +0 -0
  148. {cmd2-4.0.1 → cmd2-4.1.1}/examples/mixin.py +0 -0
  149. {cmd2-4.0.1 → cmd2-4.1.1}/examples/modular_commands/__init__.py +0 -0
  150. {cmd2-4.0.1 → cmd2-4.1.1}/examples/modular_commands/commandset_basic.py +0 -0
  151. {cmd2-4.0.1 → cmd2-4.1.1}/examples/modular_commands/commandset_complex.py +0 -0
  152. {cmd2-4.0.1 → cmd2-4.1.1}/examples/modular_commands/commandset_custominit.py +0 -0
  153. {cmd2-4.0.1 → cmd2-4.1.1}/examples/modular_commandsets.py +0 -0
  154. {cmd2-4.0.1 → cmd2-4.1.1}/examples/paged_output.py +0 -0
  155. {cmd2-4.0.1 → cmd2-4.1.1}/examples/persistent_history.py +0 -0
  156. {cmd2-4.0.1 → cmd2-4.1.1}/examples/pretty_print.py +0 -0
  157. {cmd2-4.0.1 → cmd2-4.1.1}/examples/python_scripting.py +0 -0
  158. {cmd2-4.0.1 → cmd2-4.1.1}/examples/read_input.py +0 -0
  159. {cmd2-4.0.1 → cmd2-4.1.1}/examples/remove_builtin_commands.py +0 -0
  160. {cmd2-4.0.1 → cmd2-4.1.1}/examples/remove_settable.py +0 -0
  161. {cmd2-4.0.1 → cmd2-4.1.1}/examples/rich_tables.py +0 -0
  162. {cmd2-4.0.1 → cmd2-4.1.1}/examples/rich_theme.py +0 -0
  163. {cmd2-4.0.1 → cmd2-4.1.1}/examples/scripts/arg_printer.py +0 -0
  164. {cmd2-4.0.1 → cmd2-4.1.1}/examples/scripts/conditional.py +0 -0
  165. {cmd2-4.0.1 → cmd2-4.1.1}/examples/scripts/nested.txt +0 -0
  166. {cmd2-4.0.1 → cmd2-4.1.1}/examples/scripts/quit.txt +0 -0
  167. {cmd2-4.0.1 → cmd2-4.1.1}/examples/scripts/save_help_text.py +0 -0
  168. {cmd2-4.0.1 → cmd2-4.1.1}/examples/scripts/script.py +0 -0
  169. {cmd2-4.0.1 → cmd2-4.1.1}/examples/scripts/script.txt +0 -0
  170. {cmd2-4.0.1 → cmd2-4.1.1}/examples/tmux_launch.sh +0 -0
  171. {cmd2-4.0.1 → cmd2-4.1.1}/examples/tmux_split.sh +0 -0
  172. {cmd2-4.0.1 → cmd2-4.1.1}/examples/unicode_commands.py +0 -0
  173. {cmd2-4.0.1 → cmd2-4.1.1}/mkdocs.yml +0 -0
  174. {cmd2-4.0.1 → cmd2-4.1.1}/package.json +0 -0
  175. {cmd2-4.0.1 → cmd2-4.1.1}/pyproject.toml +0 -0
  176. {cmd2-4.0.1 → cmd2-4.1.1}/ruff.toml +0 -0
  177. {cmd2-4.0.1 → cmd2-4.1.1}/scripts/validate_tag.py +0 -0
  178. {cmd2-4.0.1 → cmd2-4.1.1}/setup.cfg +0 -0
  179. {cmd2-4.0.1 → cmd2-4.1.1}/tests/.cmd2rc +0 -0
  180. {cmd2-4.0.1 → cmd2-4.1.1}/tests/__init__.py +0 -0
  181. {cmd2-4.0.1 → cmd2-4.1.1}/tests/conftest.py +0 -0
  182. {cmd2-4.0.1 → cmd2-4.1.1}/tests/pyscript/echo.py +0 -0
  183. {cmd2-4.0.1 → cmd2-4.1.1}/tests/pyscript/environment.py +0 -0
  184. {cmd2-4.0.1 → cmd2-4.1.1}/tests/pyscript/help.py +0 -0
  185. {cmd2-4.0.1 → cmd2-4.1.1}/tests/pyscript/py_locals.py +0 -0
  186. {cmd2-4.0.1 → cmd2-4.1.1}/tests/pyscript/pyscript_dir.py +0 -0
  187. {cmd2-4.0.1 → cmd2-4.1.1}/tests/pyscript/raises_exception.py +0 -0
  188. {cmd2-4.0.1 → cmd2-4.1.1}/tests/pyscript/recursive.py +0 -0
  189. {cmd2-4.0.1 → cmd2-4.1.1}/tests/pyscript/self_in_py.py +0 -0
  190. {cmd2-4.0.1 → cmd2-4.1.1}/tests/pyscript/stop.py +0 -0
  191. {cmd2-4.0.1 → cmd2-4.1.1}/tests/pyscript/test_print.py +0 -0
  192. {cmd2-4.0.1 → cmd2-4.1.1}/tests/relative_multiple.txt +0 -0
  193. {cmd2-4.0.1 → cmd2-4.1.1}/tests/script.py +0 -0
  194. {cmd2-4.0.1 → cmd2-4.1.1}/tests/script.txt +0 -0
  195. {cmd2-4.0.1 → cmd2-4.1.1}/tests/scripts/binary.bin +0 -0
  196. {cmd2-4.0.1 → cmd2-4.1.1}/tests/scripts/empty.txt +0 -0
  197. {cmd2-4.0.1 → cmd2-4.1.1}/tests/scripts/help.txt +0 -0
  198. {cmd2-4.0.1 → cmd2-4.1.1}/tests/scripts/nested.txt +0 -0
  199. {cmd2-4.0.1 → cmd2-4.1.1}/tests/scripts/one_down.txt +0 -0
  200. {cmd2-4.0.1 → cmd2-4.1.1}/tests/scripts/postcmds.txt +0 -0
  201. {cmd2-4.0.1 → cmd2-4.1.1}/tests/scripts/precmds.txt +0 -0
  202. {cmd2-4.0.1 → cmd2-4.1.1}/tests/scripts/utf8.txt +0 -0
  203. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_annotated.py +0 -0
  204. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_argparse.py +0 -0
  205. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_argparse_completer.py +0 -0
  206. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_argparse_subcommands.py +0 -0
  207. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_argparse_utils.py +0 -0
  208. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_categories.py +0 -0
  209. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_commandset.py +0 -0
  210. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_completion.py +0 -0
  211. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_dynamic_complete_style.py +0 -0
  212. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_future_annotations.py +0 -0
  213. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_history.py +0 -0
  214. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_parsing.py +0 -0
  215. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_plugin.py +0 -0
  216. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_pt_utils.py +0 -0
  217. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_py_completion.py +0 -0
  218. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_rich_utils.py +0 -0
  219. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_run_pyscript.py +0 -0
  220. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_string_utils.py +0 -0
  221. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_theme.py +0 -0
  222. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_utils.py +0 -0
  223. {cmd2-4.0.1 → cmd2-4.1.1}/tests/test_utils_defining_class.py +0 -0
@@ -14,7 +14,7 @@ repos:
14
14
  - id: trailing-whitespace
15
15
 
16
16
  - repo: https://github.com/astral-sh/ruff-pre-commit
17
- rev: "v0.15.17"
17
+ rev: "v0.15.21"
18
18
  hooks:
19
19
  - id: ruff-format
20
20
  args: [--config=ruff.toml]
@@ -30,7 +30,7 @@ repos:
30
30
  - prettier-plugin-toml@2.0.6
31
31
 
32
32
  - repo: https://github.com/crate-ci/typos
33
- rev: v1.47.2
33
+ rev: v1.48.0
34
34
  hooks:
35
35
  - id: typos
36
36
  exclude: |
@@ -1,3 +1,10 @@
1
+ ## 4.1.1 (July 9, 2026)
2
+
3
+ - Bug Fixes
4
+ - Fixed a couple type issues that emerged with the upgrade to `mypy` 2.2.0
5
+ - Enhancements
6
+ - Async alert messages can now be Rich renderables.
7
+
1
8
  ## 4.1.0 (July 7, 2026)
2
9
 
3
10
  - Breaking Changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cmd2
3
- Version: 4.0.1
3
+ Version: 4.1.1
4
4
  Summary: cmd2 - quickly build feature-rich and user-friendly interactive command line applications in Python
5
5
  Author: cmd2 Contributors
6
6
  License-Expression: MIT
@@ -1035,13 +1035,13 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
1035
1035
 
1036
1036
  self.exit(2, f"{formatted_message}\n")
1037
1037
 
1038
- def _get_formatter(self, **_kwargs: Any) -> Cmd2HelpFormatter:
1038
+ def _get_formatter(self, *_args: Any, **_kwargs: Any) -> Cmd2HelpFormatter:
1039
1039
  """Override with customizations for Cmd2HelpFormatter."""
1040
1040
  return self.formatter_class(prog=self.prog, file=self._thread_locals.current_output_file)
1041
1041
 
1042
- def format_help(self) -> str:
1042
+ def format_help(self, *args: Any, **kwargs: Any) -> str:
1043
1043
  """Override to add a newline."""
1044
- return super().format_help() + "\n"
1044
+ return super().format_help(*args, **kwargs) + "\n"
1045
1045
 
1046
1046
  def _get_nargs_pattern(self, action: argparse.Action) -> str:
1047
1047
  """Override to support nargs ranges."""
@@ -312,7 +312,11 @@ class CommandParsers:
312
312
  class AsyncAlert:
313
313
  """Contents of an asynchronous alert which display while user is at prompt.
314
314
 
315
- :param msg: an optional message to be printed above the prompt.
315
+ :param msg: an optional printable object (including Rich renderables) to be
316
+ printed above the prompt.
317
+ :param soft_wrap: Enable soft wrap mode. This only applies with msg is not None.
318
+ Defaults to True. See print_to() docstring for more details on
319
+ this parameter.
316
320
  :param prompt: an optional string to dynamically replace the current prompt.
317
321
 
318
322
  :ivar timestamp: monotonic creation time of the alert. If an alert was created
@@ -320,7 +324,8 @@ class AsyncAlert:
320
324
  to avoid a stale display, but its msg data will still be displayed.
321
325
  """
322
326
 
323
- msg: str | None = None
327
+ msg: Any | None = None
328
+ soft_wrap: bool = True
324
329
  prompt: str | None = None
325
330
  timestamp: float = field(default_factory=time.monotonic, init=False)
326
331
 
@@ -1572,7 +1577,7 @@ class Cmd:
1572
1577
  If False, Rich wraps text to fit the terminal width.
1573
1578
  Set this to False when printing structured Renderables like
1574
1579
  Tables, Panels, or Columns to ensure they render as expected.
1575
- For example, when soft_wrap is True Panels truncate text
1580
+ For example, when soft_wrap is True, Panels truncate text
1576
1581
  which is wider than the terminal.
1577
1582
  :param justify: justify method ("left", "center", "right", "full"). Defaults to None.
1578
1583
  :param emoji: If True, Rich will replace emoji codes (e.g., :smiley:) with their
@@ -3657,7 +3662,19 @@ class Cmd:
3657
3662
  # prevents async alerts from printing once a command starts.
3658
3663
 
3659
3664
  # Print all alerts at once to reduce flicker.
3660
- alert_text = "\n".join(alert.msg for alert in self._alert_queue if alert.msg)
3665
+ console = self._get_core_print_console(
3666
+ file=self.stdout,
3667
+ emoji=False,
3668
+ markup=False,
3669
+ highlight=False,
3670
+ )
3671
+
3672
+ with console.capture() as capture:
3673
+ for alert in self._alert_queue:
3674
+ if alert.msg is not None:
3675
+ console.print(alert.msg, soft_wrap=alert.soft_wrap)
3676
+
3677
+ alert_text = capture.get()
3661
3678
 
3662
3679
  # Find the latest prompt update among all pending alerts.
3663
3680
  latest_prompt = None
@@ -3681,7 +3698,7 @@ class Cmd:
3681
3698
  if alert_text:
3682
3699
  # Print the alert messages above the prompt.
3683
3700
  with patch_stdout():
3684
- print_formatted_text(pt_filter_style(alert_text))
3701
+ print_formatted_text(ANSI(alert_text), end="")
3685
3702
 
3686
3703
  elif latest_prompt is not None:
3687
3704
  # Refresh UI immediately to show the new prompt
@@ -5616,7 +5633,13 @@ class Cmd:
5616
5633
  # self.last_result will be set by do_run_script()
5617
5634
  return self.do_run_script(su.quote(relative_path))
5618
5635
 
5619
- def add_alert(self, *, msg: str | None = None, prompt: str | None = None) -> None:
5636
+ def add_alert(
5637
+ self,
5638
+ *,
5639
+ msg: Any | None = None,
5640
+ soft_wrap: bool = True,
5641
+ prompt: str | None = None,
5642
+ ) -> None:
5620
5643
  """Queue an asynchronous alert to be displayed when the prompt is active.
5621
5644
 
5622
5645
  Examples:
@@ -5624,7 +5647,11 @@ class Cmd:
5624
5647
  add_alert(prompt="user@host> ") # Update prompt only
5625
5648
  add_alert(msg="Done", prompt="> ") # Update both
5626
5649
 
5627
- :param msg: an optional message to be printed above the prompt.
5650
+ :param msg: an optional printable object (including Rich renderables) to be
5651
+ printed above the prompt.
5652
+ :param soft_wrap: Enable soft wrap mode. This only applies with msg is not None.
5653
+ Defaults to True. See print_to() docstring for more details on
5654
+ this parameter.
5628
5655
  :param prompt: an optional string to dynamically replace the current prompt.
5629
5656
 
5630
5657
  """
@@ -5632,7 +5659,7 @@ class Cmd:
5632
5659
  return
5633
5660
 
5634
5661
  with self._alert_condition:
5635
- alert = AsyncAlert(msg=msg, prompt=prompt)
5662
+ alert = AsyncAlert(msg=msg, soft_wrap=soft_wrap, prompt=prompt)
5636
5663
  self._alert_queue.append(alert)
5637
5664
  self._alert_condition.notify_all()
5638
5665
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cmd2
3
- Version: 4.0.1
3
+ Version: 4.1.1
4
4
  Summary: cmd2 - quickly build feature-rich and user-friendly interactive command line applications in Python
5
5
  Author: cmd2 Contributors
6
6
  License-Expression: MIT
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "4.1.1",
3
+ "distance": 0,
4
+ "node": "gb4d6cbbc607a8333ba538b765499e98eefa8e9a7",
5
+ "dirty": false,
6
+ "branch": "main",
7
+ "node_date": "2026-07-09"
8
+ }
@@ -0,0 +1,181 @@
1
+ #!/usr/bin/env python
2
+ """A simple example demonstrating an application that asynchronously prints alerts,
3
+ updates the prompt, and changes the window title.
4
+ """
5
+
6
+ import secrets
7
+ import threading
8
+ import time
9
+ from typing import Any
10
+
11
+ from rich.console import Group
12
+ from rich.panel import Panel
13
+ from rich.style import Style
14
+ from rich.table import Table
15
+ from rich.text import Text
16
+
17
+ import cmd2
18
+ from cmd2 import (
19
+ Color,
20
+ stylize,
21
+ )
22
+
23
+
24
+ def get_alerts() -> list[tuple[Any, bool]]:
25
+ """Return a list of (alert_msg, soft_wrap) tuples."""
26
+ table = Table("Command", "Description", title="Quick Help")
27
+ table.add_row("start_alerts", "Start the async alert generator")
28
+ table.add_row("stop_alerts", "Stop the async alert generator")
29
+ table.add_row("help", "Show help menu")
30
+
31
+ # Set soft_wrap to False when printing structured Renderables like Tables, Panels, or Columns
32
+ # to ensure they render as expected. For example, when soft_wrap is True, Panels truncate
33
+ # text which is wider than the terminal.
34
+ return [
35
+ (
36
+ Text(
37
+ "Watch as this application prints alerts asynchronously!",
38
+ style=Style(
39
+ color=Color.BRIGHT_CYAN,
40
+ bold=True,
41
+ ),
42
+ ),
43
+ True,
44
+ ),
45
+ ("Notice how alerts don't interfere with your typing or cursor location.", True),
46
+ (
47
+ Panel(
48
+ "This message is wrapped in a Rich Panel!",
49
+ title="System Alert",
50
+ border_style=Color.BRIGHT_BLUE,
51
+ expand=False,
52
+ ),
53
+ False,
54
+ ),
55
+ (table, False),
56
+ ("You can stop and start the alerts by typing stop_alerts and start_alerts.", True),
57
+ ]
58
+
59
+
60
+ class AlerterApp(cmd2.Cmd):
61
+ """An app that shows off add_alert() and set_window_title()."""
62
+
63
+ def __init__(self) -> None:
64
+ """Initialize AlerterApp."""
65
+ super().__init__()
66
+
67
+ self.prompt = "(APR)> "
68
+
69
+ # The thread that will asynchronously alert the user of events
70
+ self._stop_event = threading.Event()
71
+ self._add_alert_thread = threading.Thread()
72
+ self._alert_count = 0
73
+
74
+ # Create some hooks to handle the starting and stopping of our thread
75
+ self.register_preloop_hook(self._preloop_hook)
76
+ self.register_postloop_hook(self._postloop_hook)
77
+
78
+ # Create an instance of SystemRandom
79
+ self._secure_generator = secrets.SystemRandom()
80
+
81
+ def _preloop_hook(self) -> None:
82
+ """Start the alerter thread."""
83
+ self._stop_event.clear()
84
+ self._add_alert_thread = threading.Thread(name="alerter", target=self._add_alerts_func)
85
+ self._add_alert_thread.start()
86
+
87
+ def _postloop_hook(self) -> None:
88
+ """Stop the alerter thread."""
89
+ self._stop_event.set()
90
+ if self._add_alert_thread.is_alive():
91
+ self._add_alert_thread.join()
92
+
93
+ def do_start_alerts(self, _: cmd2.Statement) -> None:
94
+ """Start the alerter thread."""
95
+ if self._add_alert_thread.is_alive():
96
+ print("The alert thread is already started")
97
+ else:
98
+ self._stop_event.clear()
99
+ self._add_alert_thread = threading.Thread(name="alerter", target=self._add_alerts_func)
100
+ self._add_alert_thread.start()
101
+
102
+ def do_stop_alerts(self, _: cmd2.Statement) -> None:
103
+ """Stop the alerter thread."""
104
+ self._stop_event.set()
105
+ if self._add_alert_thread.is_alive():
106
+ self._add_alert_thread.join()
107
+ else:
108
+ print("The alert thread is already stopped")
109
+
110
+ def _build_colored_prompt(self) -> str:
111
+ """Randomly build a colored prompt.
112
+
113
+ :return: the new prompt.
114
+ """
115
+ rand_num = self._secure_generator.randint(1, 6)
116
+
117
+ status_color = Color.DEFAULT
118
+
119
+ if rand_num == 1:
120
+ status_color = Color.BRIGHT_RED
121
+ elif rand_num == 2:
122
+ status_color = Color.BRIGHT_YELLOW
123
+ elif rand_num == 3:
124
+ status_color = Color.CYAN
125
+ elif rand_num == 4:
126
+ status_color = Color.BRIGHT_GREEN
127
+ elif rand_num == 5:
128
+ status_color = Color.BRIGHT_BLUE
129
+
130
+ return stylize(self.visible_prompt, style=status_color)
131
+
132
+ def _add_alerts_func(self) -> None:
133
+ """Print alerts and update the prompt any time the prompt is showing."""
134
+ self._alert_count = 0
135
+
136
+ alerts = get_alerts()
137
+ alert_index = 0
138
+ last_alert_time = 0.0
139
+
140
+ while not self._stop_event.is_set():
141
+ cur_time = time.monotonic()
142
+ alert_msg = None
143
+ soft_wrap = True
144
+
145
+ # Trigger the next alert every 4 seconds
146
+ if cur_time - last_alert_time >= 4.0:
147
+ alert_msg, soft_wrap = alerts[alert_index]
148
+ alert_index = (alert_index + 1) % len(alerts)
149
+ self._alert_count += 1
150
+ last_alert_time = cur_time
151
+
152
+ # Build a new prompt (color changes randomly)
153
+ new_prompt = self._build_colored_prompt()
154
+
155
+ # Check if we have an alert to print
156
+ if alert_msg is not None:
157
+ # Wrap the alert message and an empty string in a Rich Group.
158
+ # This is a clean way to append a blank line after any RenderableType
159
+ # (strings, panels, tables, etc.) for visual separation.
160
+ self.add_alert(
161
+ msg=Group(alert_msg, ""),
162
+ soft_wrap=soft_wrap,
163
+ prompt=new_prompt,
164
+ )
165
+
166
+ new_title = f"Alerts Printed: {self._alert_count}"
167
+ self.set_window_title(new_title)
168
+
169
+ # Otherwise, check if the prompt needs to be updated or refreshed
170
+ elif self.prompt != new_prompt:
171
+ self.add_alert(prompt=new_prompt)
172
+
173
+ self._stop_event.wait(1.0)
174
+
175
+
176
+ if __name__ == "__main__":
177
+ import sys
178
+
179
+ app = AlerterApp()
180
+ app.set_window_title("Asynchronous Printer Test")
181
+ sys.exit(app.cmdloop())
@@ -1258,7 +1258,7 @@ def test_ctrl_d_at_prompt(say_app, monkeypatch) -> None:
1258
1258
  (None, "", False),
1259
1259
  ],
1260
1260
  )
1261
- def test_async_alert(base_app, msg, prompt, is_stale) -> None:
1261
+ def test_async_alert(base_app: cmd2.Cmd, msg: str, prompt: str, is_stale: bool) -> None:
1262
1262
  import time
1263
1263
 
1264
1264
  with (
@@ -1311,18 +1311,82 @@ def test_async_alert(base_app, msg, prompt, is_stale) -> None:
1311
1311
  assert base_app.prompt == prompt
1312
1312
 
1313
1313
 
1314
- def test_add_alert(base_app) -> None:
1315
- orig_num_alerts = len(base_app._alert_queue)
1314
+ @pytest.mark.skipif(
1315
+ sys.platform.startswith("win"),
1316
+ reason="Don't have a real Windows console with how we are currently running tests in GitHub Actions",
1317
+ )
1318
+ def test_async_alert_rich(base_app: cmd2.Cmd) -> None:
1319
+ from prompt_toolkit.formatted_text import ANSI
1320
+ from rich.table import Table
1321
+
1322
+ table = Table("Header")
1323
+ table.add_row("Value")
1324
+
1325
+ with (
1326
+ mock.patch("cmd2.cmd2.print_formatted_text") as mock_print,
1327
+ mock.patch("cmd2.cmd2.get_app") as mock_get_app,
1328
+ ):
1329
+ mock_app = mock.MagicMock()
1330
+ mock_get_app.return_value = mock_app
1331
+
1332
+ # Add alert with a Rich table
1333
+ base_app.add_alert(msg=table, soft_wrap=False)
1334
+
1335
+ with create_pipe_input() as pipe_input:
1336
+ base_app.main_session = PromptSession(
1337
+ input=pipe_input,
1338
+ output=DummyOutput(),
1339
+ history=base_app.main_session.history,
1340
+ completer=base_app.main_session.completer,
1341
+ )
1342
+ pipe_input.send_text("quit\n")
1343
+ base_app._cmdloop()
1344
+
1345
+ # Verify that print_formatted_text was called with a formatted ANSI object
1346
+ assert mock_print.called
1347
+ args, _kwargs = mock_print.call_args
1348
+ printed_arg = args[0]
1349
+ assert isinstance(printed_arg, ANSI)
1350
+
1351
+ # The printed text should contain our table values
1352
+ assert "Header" in printed_arg.value
1353
+ assert "Value" in printed_arg.value
1354
+
1355
+
1356
+ def test_add_alert(base_app: cmd2.Cmd) -> None:
1357
+ base_app._alert_queue.clear()
1316
1358
 
1317
1359
  # Nothing is added when both are None
1318
1360
  base_app.add_alert(msg=None, prompt=None)
1319
- assert len(base_app._alert_queue) == orig_num_alerts
1361
+ assert not base_app._alert_queue
1320
1362
 
1321
- # Now test valid alert arguments
1363
+ # Test that soft_wrap defaults to True
1322
1364
  base_app.add_alert(msg="Hello", prompt=None)
1323
- base_app.add_alert(msg="Hello", prompt="prompt> ")
1365
+ last_alert = base_app._alert_queue[-1]
1366
+ assert last_alert.msg == "Hello"
1367
+ assert last_alert.soft_wrap is True
1368
+ assert last_alert.prompt is None
1369
+
1370
+ # Test that soft_wrap can be set to True
1371
+ base_app.add_alert(msg="Hello", soft_wrap=True, prompt=None)
1372
+ last_alert = base_app._alert_queue[-1]
1373
+ assert last_alert.msg == "Hello"
1374
+ assert last_alert.soft_wrap is True
1375
+ assert last_alert.prompt is None
1376
+
1377
+ # Test that soft_wrap can be set to False
1378
+ base_app.add_alert(msg="Hello", soft_wrap=False, prompt=None)
1379
+ last_alert = base_app._alert_queue[-1]
1380
+ assert last_alert.msg == "Hello"
1381
+ assert last_alert.soft_wrap is False
1382
+ assert last_alert.prompt is None
1383
+
1384
+ # Test only prompt
1324
1385
  base_app.add_alert(msg=None, prompt="prompt> ")
1325
- assert len(base_app._alert_queue) == orig_num_alerts + 3
1386
+ last_alert = base_app._alert_queue[-1]
1387
+ assert last_alert.msg is None
1388
+ assert last_alert.soft_wrap is True
1389
+ assert last_alert.prompt == "prompt> "
1326
1390
 
1327
1391
 
1328
1392
  def test_visible_prompt() -> None:
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "4.0.1",
3
- "distance": 0,
4
- "node": "gaca7d06e65b6a08bb7290b2531fb612b95ee26c2",
5
- "dirty": false,
6
- "branch": "main",
7
- "node_date": "2026-07-07"
8
- }
@@ -1,182 +0,0 @@
1
- #!/usr/bin/env python
2
- """A simple example demonstrating an application that asynchronously prints alerts, updates the prompt
3
- and changes the window title.
4
- """
5
-
6
- import secrets
7
- import threading
8
- import time
9
-
10
- import cmd2
11
- from cmd2 import (
12
- Color,
13
- stylize,
14
- )
15
-
16
- ALERTS = [
17
- "Watch as this application prints alerts and updates the prompt",
18
- "This will only happen when the prompt is present",
19
- "Notice how it doesn't interfere with your typing or cursor location",
20
- "Go ahead and type some stuff and move the cursor throughout the line",
21
- "Keep typing...",
22
- "Move that cursor...",
23
- "Pretty seamless, eh?",
24
- "Feedback can also be given in the window title. Notice the alert count up there?",
25
- "You can stop and start the alerts by typing stop_alerts and start_alerts",
26
- "This demo will now continue to print alerts at random intervals",
27
- ]
28
-
29
-
30
- class AlerterApp(cmd2.Cmd):
31
- """An app that shows off async_alert() and async_update_prompt()."""
32
-
33
- def __init__(self) -> None:
34
- """Initializer."""
35
- super().__init__()
36
-
37
- self.prompt = "(APR)> "
38
-
39
- # The thread that will asynchronously alert the user of events
40
- self._stop_event = threading.Event()
41
- self._add_alert_thread = threading.Thread()
42
- self._alert_count = 0
43
- self._next_alert_time = 0.0
44
-
45
- # Create some hooks to handle the starting and stopping of our thread
46
- self.register_preloop_hook(self._preloop_hook)
47
- self.register_postloop_hook(self._postloop_hook)
48
-
49
- # Create an instance of SystemRandom
50
- self._secure_generator = secrets.SystemRandom()
51
-
52
- def _preloop_hook(self) -> None:
53
- """Start the alerter thread."""
54
- self._stop_event.clear()
55
- self._add_alert_thread = threading.Thread(name="alerter", target=self._add_alerts_func)
56
- self._add_alert_thread.start()
57
-
58
- def _postloop_hook(self) -> None:
59
- """Stops the alerter thread."""
60
- self._stop_event.set()
61
- if self._add_alert_thread.is_alive():
62
- self._add_alert_thread.join()
63
-
64
- def do_start_alerts(self, _: cmd2.Statement) -> None:
65
- """Starts the alerter thread."""
66
- if self._add_alert_thread.is_alive():
67
- print("The alert thread is already started")
68
- else:
69
- self._stop_event.clear()
70
- self._add_alert_thread = threading.Thread(name="alerter", target=self._add_alerts_func)
71
- self._add_alert_thread.start()
72
-
73
- def do_stop_alerts(self, _: cmd2.Statement) -> None:
74
- """Stops the alerter thread."""
75
- self._stop_event.set()
76
- if self._add_alert_thread.is_alive():
77
- self._add_alert_thread.join()
78
- else:
79
- print("The alert thread is already stopped")
80
-
81
- def _get_alerts(self) -> list[str]:
82
- """Reports alerts
83
- :return: the list of alerts.
84
- """
85
- cur_time = time.monotonic()
86
- if cur_time < self._next_alert_time:
87
- return []
88
-
89
- alerts = []
90
-
91
- if self._alert_count < len(ALERTS):
92
- alerts.append(ALERTS[self._alert_count])
93
- self._alert_count += 1
94
- self._next_alert_time = cur_time + 4
95
-
96
- else:
97
- rand_num = self._secure_generator.randint(1, 20)
98
- if rand_num > 2:
99
- return []
100
-
101
- for _ in range(rand_num):
102
- self._alert_count += 1
103
- alerts.append(f"Alert {self._alert_count}")
104
-
105
- self._next_alert_time = 0
106
-
107
- return alerts
108
-
109
- def _build_alert_str(self) -> str:
110
- """Combines alerts into one string that can be printed to the terminal
111
- :return: the alert string.
112
- """
113
- alert_str = ""
114
- alerts = self._get_alerts()
115
-
116
- longest_alert = max(ALERTS, key=len)
117
- num_asterisks = len(longest_alert) + 8
118
-
119
- for i, cur_alert in enumerate(alerts):
120
- # Use padding to center the alert
121
- padding = " " * int((num_asterisks - len(cur_alert)) / 2)
122
-
123
- if i > 0:
124
- alert_str += "\n"
125
- alert_str += "*" * num_asterisks + "\n"
126
- alert_str += padding + cur_alert + padding + "\n"
127
- alert_str += "*" * num_asterisks + "\n"
128
-
129
- return alert_str
130
-
131
- def _build_colored_prompt(self) -> str:
132
- """Randomly builds a colored prompt
133
- :return: the new prompt.
134
- """
135
- rand_num = self._secure_generator.randint(1, 6)
136
-
137
- status_color = Color.DEFAULT
138
-
139
- if rand_num == 1:
140
- status_color = Color.BRIGHT_RED
141
- elif rand_num == 2:
142
- status_color = Color.BRIGHT_YELLOW
143
- elif rand_num == 3:
144
- status_color = Color.CYAN
145
- elif rand_num == 4:
146
- status_color = Color.BRIGHT_GREEN
147
- elif rand_num == 5:
148
- status_color = Color.BRIGHT_BLUE
149
-
150
- return stylize(self.visible_prompt, style=status_color)
151
-
152
- def _add_alerts_func(self) -> None:
153
- """Prints alerts and updates the prompt any time the prompt is showing."""
154
- self._alert_count = 0
155
- self._next_alert_time = 0
156
-
157
- while not self._stop_event.is_set():
158
- # Get any alerts that need to be printed
159
- alert_str = self._build_alert_str()
160
-
161
- # Build a new prompt
162
- new_prompt = self._build_colored_prompt()
163
-
164
- # Check if we have alerts to print
165
- if alert_str:
166
- self.add_alert(msg=alert_str, prompt=new_prompt)
167
- new_title = f"Alerts Printed: {self._alert_count}"
168
- self.set_window_title(new_title)
169
-
170
- # Otherwise check if the prompt needs to be updated or refreshed
171
- elif self.prompt != new_prompt:
172
- self.add_alert(prompt=new_prompt)
173
-
174
- self._stop_event.wait(0.5)
175
-
176
-
177
- if __name__ == "__main__":
178
- import sys
179
-
180
- app = AlerterApp()
181
- app.set_window_title("Asynchronous Printer Test")
182
- sys.exit(app.cmdloop())
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes