diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 06d4299b5..1175618ae 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,7 +65,7 @@ The tables below list all prerequisites along with the minimum required version #### Additional prerequisites to build cmd2 documentation | Prerequisite | Minimum Version | | ------------------------------------------- | --------------- | -| [sphinx](http://www.sphinx-doc.org) | `1.4.9` | +| [sphinx](http://www.sphinx-doc.org) | `2.0.0` | | [sphinx-rtd-theme](https://github.com/snide/sphinx_rtd_theme) | `0.1.9` | #### Optional prerequisites for enhanced unit test features @@ -211,7 +211,7 @@ pipenv install --dev To create a new virtualenv, using a specific version of Python you have installed (and on your PATH), use the --python VERSION flag, like so: ```sh -pipenv install --dev --python 3.7 +pipenv install --dev --python 3.8 ``` Then you can enter that virtual environment with: @@ -221,8 +221,8 @@ pipenv shell #### Create a new environment for cmd2 using Conda ```sh -$ conda create -n cmd2_py36 python=3.6 -$ conda activate cmd2_py36 +$ conda create -n cmd2_py37 python=3.7 +$ conda activate cmd2_py37 ``` #### Create a new environment for cmd using Virtualenv @@ -233,13 +233,13 @@ We recommend that you use [pyenv](https://github.com/pyenv/pyenv) to manage your pyenv versions # Install python version defined -pyenv install 3.6.3 +pyenv install 3.8.2 ``` With the Python version installed, you can set the virtualenv properly. ```sh $ cd ~/src/cmd2 -$ virtualenv -p $(pyenv root)/versions/3.6.3/ cmd_py36 +$ virtualenv -p $(pyenv root)/versions/3.8.2/ cmd_py38 $ source ~/src/cmd2/bin/activate ``` @@ -544,6 +544,33 @@ excellent support for debugging console applications. [PyCharm](https://www.jetbrains.com/pycharm/) is also quite good and has very nice [code inspection](https://www.jetbrains.com/help/pycharm/code-inspection.html) capabilities. +## Branching Strategy and Semantic Versioning + +Starting with version 1.0.0, `cmd2` has adopted [Semantic Versioning](https://semver.org). + +### Semantic Versioning Summary +Given a version number `MAJOR`.`MINOR`.`PATCH`, increment the: + +- `MAJOR` version when you make incompatible API changes, +- `MINOR` version when you add functionality in a backwards compatible manner, and +- `PATCH` version when you make backwards compatible bug fixes. + +### Branching Strategy + +We use the **master** branch for the upcoming `PATCH` release - i.e. if the current version +of `cmd2` is 1.0.2, then the **master** branch contains code which is planned for release +in 1.0.3. + +If work needs to be done for a `MAJOR` or `MINOR` release when we anticipate there will be +a `PATCH` release in-between, then a branch should be created named for the appropriate version +number for the work, e.g. if the current release of `cmd2` is 1.0.2 and a backwards-incompatible +change needs to be committed for an upcoming `MAJOR` release, then this work should be committed +to a **2.0.0** branch until such a time as we are ready to release version 2.0.0. + +Following this strategy, releases are always done from the **master** branch and `MAJOR` or `MINOR` +branches are merged to **master** immediately prior to doing a release. Once merged to **master**, the +other branches can be deleted. All releases are tagged so that they can be reproduced if necessary. + ## Publishing a new release Since 0.9.2, the process of publishing a new release of `cmd2` to [PyPi](https://pypi.org/) has been diff --git a/Pipfile b/Pipfile index 5d23fcf02..e03fc25d9 100644 --- a/Pipfile +++ b/Pipfile @@ -18,6 +18,7 @@ flake8 = "*" gnureadline = {version = "*",sys_platform = "== 'darwin'"} invoke = "*" ipython = "*" +isort = "*" mock = {version = "*",markers = "python_version < '3.6'"} plumbum = "*" pyreadline = {version = "*",sys_platform = "== 'win32'"} diff --git a/cmd2/ansi.py b/cmd2/ansi.py index a6c09413f..f172b87f3 100644 --- a/cmd2/ansi.py +++ b/cmd2/ansi.py @@ -6,10 +6,10 @@ import functools import re from enum import Enum -from typing import Any, IO, List, Union +from typing import IO, Any, List, Union import colorama -from colorama import Fore, Back, Style +from colorama import Back, Fore, Style from wcwidth import wcswidth # On Windows, filter ANSI escape codes out of text sent to stdout/stderr, and replace them with equivalent Win32 calls diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py index f61f5fd80..61f173ccf 100644 --- a/cmd2/argparse_completer.py +++ b/cmd2/argparse_completer.py @@ -14,10 +14,16 @@ from typing import Dict, List, Optional, Union from . import ansi, cmd2, constants -from .argparse_custom import ATTR_CHOICES_CALLABLE, generate_range_error -from .argparse_custom import ATTR_SUPPRESS_TAB_HINT, ATTR_DESCRIPTIVE_COMPLETION_HEADER, ATTR_NARGS_RANGE -from .argparse_custom import ChoicesCallable, CompletionItem -from .utils import basic_complete, CompletionError +from .argparse_custom import ( + ATTR_CHOICES_CALLABLE, + ATTR_DESCRIPTIVE_COMPLETION_HEADER, + ATTR_NARGS_RANGE, + ATTR_SUPPRESS_TAB_HINT, + ChoicesCallable, + CompletionItem, + generate_range_error, +) +from .utils import CompletionError, basic_complete # If no descriptive header is supplied, then this will be used instead DEFAULT_DESCRIPTIVE_HEADER = 'Description' diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py index f018f33f3..485f65c2a 100644 --- a/cmd2/argparse_custom.py +++ b/cmd2/argparse_custom.py @@ -203,7 +203,7 @@ def my_completer_method(self, text, line, begidx, endidx, arg_tokens) import re import sys # noinspection PyUnresolvedReferences,PyProtectedMember -from argparse import ZERO_OR_MORE, ONE_OR_MORE, ArgumentError, _ +from argparse import ONE_OR_MORE, ZERO_OR_MORE, ArgumentError, _ from typing import Callable, Optional, Tuple, Type, Union from . import ansi, constants diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index d14b4d990..49c181f11 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -42,17 +42,14 @@ from contextlib import redirect_stdout from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Tuple, Type, Union -from . import ansi -from . import constants -from . import plugin -from . import utils -from .argparse_custom import CompletionItem, DEFAULT_ARGUMENT_PARSER +from . import ansi, constants, plugin, utils +from .argparse_custom import DEFAULT_ARGUMENT_PARSER, CompletionItem from .clipboard import can_clip, get_paste_buffer, write_to_paste_buffer from .decorators import with_argparser from .exceptions import Cmd2ArgparseError, Cmd2ShlexError, EmbeddedConsoleExit, EmptyStatement, RedirectionError from .history import History, HistoryItem -from .parsing import StatementParser, Statement, Macro, MacroArg, shlex_split -from .rl_utils import rl_type, RlType, rl_get_point, rl_set_prompt, vt100_support, rl_make_safe_prompt, rl_warning +from .parsing import Macro, MacroArg, Statement, StatementParser, shlex_split +from .rl_utils import RlType, rl_get_point, rl_make_safe_prompt, rl_set_prompt, rl_type, rl_warning, vt100_support from .utils import CompletionError, Settable # Set up readline diff --git a/cmd2/history.py b/cmd2/history.py index 7b52aa163..60a071fb2 100644 --- a/cmd2/history.py +++ b/cmd2/history.py @@ -4,7 +4,6 @@ """ import re - from typing import List, Union import attr diff --git a/cmd2/parsing.py b/cmd2/parsing.py index 71582f1ae..a7ee74a1c 100755 --- a/cmd2/parsing.py +++ b/cmd2/parsing.py @@ -8,8 +8,7 @@ import attr -from . import constants -from . import utils +from . import constants, utils from .exceptions import Cmd2ShlexError diff --git a/cmd2/py_bridge.py b/cmd2/py_bridge.py index 0dc04ca67..38fef1428 100644 --- a/cmd2/py_bridge.py +++ b/cmd2/py_bridge.py @@ -5,10 +5,10 @@ """ import sys -from contextlib import redirect_stdout, redirect_stderr +from contextlib import redirect_stderr, redirect_stdout from typing import Optional -from .utils import namedtuple_with_defaults, StdSim +from .utils import StdSim, namedtuple_with_defaults class CommandResult(namedtuple_with_defaults('CommandResult', ['stdout', 'stderr', 'stop', 'data'])): diff --git a/cmd2/rl_utils.py b/cmd2/rl_utils.py index 4df733dbf..099d76b7a 100644 --- a/cmd2/rl_utils.py +++ b/cmd2/rl_utils.py @@ -2,8 +2,8 @@ """ Imports the proper readline for the platform and provides utility functions for it """ -from enum import Enum import sys +from enum import Enum # Prefer statically linked gnureadline if available (for macOS compatibility due to issues with libedit) try: diff --git a/docs/conf.py b/docs/conf.py index 9bb2e8553..6c6faf0d6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,10 +17,9 @@ add these directories to sys.path here. If the directory is relative to the documentation root, use os.path.abspath to make it absolute, like shown here. """ -from pkg_resources import get_distribution - # Import for custom theme from Read the Docs import sphinx_rtd_theme +from pkg_resources import get_distribution # -- General configuration ----------------------------------------------------- diff --git a/examples/alias_startup.py b/examples/alias_startup.py index 052d13674..3fa9ec5ae 100755 --- a/examples/alias_startup.py +++ b/examples/alias_startup.py @@ -5,6 +5,7 @@ 2) How to run an initialization script at startup """ import os + import cmd2 diff --git a/examples/argparse_completion.py b/examples/argparse_completion.py index bf2b2723d..e44533b39 100644 --- a/examples/argparse_completion.py +++ b/examples/argparse_completion.py @@ -6,8 +6,8 @@ import argparse from typing import Dict, List -from cmd2 import Cmd, Cmd2ArgumentParser, with_argparser, CompletionItem -from cmd2.utils import basic_complete, CompletionError +from cmd2 import Cmd, Cmd2ArgumentParser, CompletionItem, with_argparser +from cmd2.utils import CompletionError, basic_complete # Data source for argparse.choices food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato'] diff --git a/examples/async_printing.py b/examples/async_printing.py index 692ce7692..a136d8e20 100755 --- a/examples/async_printing.py +++ b/examples/async_printing.py @@ -10,7 +10,7 @@ from typing import List import cmd2 -from cmd2 import style, fg +from cmd2 import fg, style ALERTS = ["Watch as this application prints alerts and updates the prompt", "This will only happen when the prompt is present", diff --git a/examples/basic.py b/examples/basic.py index 5df3d1b56..2a1e9a121 100755 --- a/examples/basic.py +++ b/examples/basic.py @@ -9,7 +9,7 @@ 6) Shell-like capabilities """ import cmd2 -from cmd2 import style, fg, bg +from cmd2 import bg, fg, style class BasicApp(cmd2.Cmd): diff --git a/examples/colors.py b/examples/colors.py index 1466471b4..8f8e3c6ef 100755 --- a/examples/colors.py +++ b/examples/colors.py @@ -26,9 +26,10 @@ import argparse from typing import Any +from colorama import Back, Fore, Style + import cmd2 from cmd2 import ansi -from colorama import Fore, Back, Style class CmdLineApp(cmd2.Cmd): diff --git a/examples/custom_parser.py b/examples/custom_parser.py index 7c154176e..34c7bee29 100644 --- a/examples/custom_parser.py +++ b/examples/custom_parser.py @@ -4,8 +4,7 @@ """ import sys -from cmd2 import Cmd2ArgumentParser, set_default_argument_parser -from cmd2 import ansi +from cmd2 import Cmd2ArgumentParser, ansi, set_default_argument_parser # First define the parser diff --git a/examples/exit_code.py b/examples/exit_code.py index f4b190912..89ed86cd8 100755 --- a/examples/exit_code.py +++ b/examples/exit_code.py @@ -2,9 +2,10 @@ # coding=utf-8 """A simple example demonstrating the following how to emit a non-zero exit code in your cmd2 application. """ -import cmd2 from typing import List +import cmd2 + class ReplWithExitCode(cmd2.Cmd): """ Example cmd2 application where we can specify an exit code when existing.""" diff --git a/examples/hooks.py b/examples/hooks.py index 409c89790..f8079e58f 100755 --- a/examples/hooks.py +++ b/examples/hooks.py @@ -10,7 +10,6 @@ """ import re - from typing import List import cmd2 diff --git a/examples/initialization.py b/examples/initialization.py index 609255f17..50bb73aa1 100755 --- a/examples/initialization.py +++ b/examples/initialization.py @@ -13,7 +13,7 @@ 10) How to make custom attributes settable at runtime """ import cmd2 -from cmd2 import style, fg, bg +from cmd2 import bg, fg, style class BasicApp(cmd2.Cmd): diff --git a/examples/migrating.py b/examples/migrating.py index 3a25b8c8a..86a59ed6a 100755 --- a/examples/migrating.py +++ b/examples/migrating.py @@ -3,9 +3,8 @@ """ A sample application for cmd which can be used to show how to migrate to cmd2. """ -import random - import cmd +import random class CmdLineApp(cmd.Cmd): diff --git a/examples/override_parser.py b/examples/override_parser.py index eecb0e88f..b65483880 100755 --- a/examples/override_parser.py +++ b/examples/override_parser.py @@ -10,12 +10,14 @@ # See the code for custom_parser.py. It simply defines a parser and calls cmd2.set_default_argument_parser() # with the custom parser's type. import argparse -argparse.cmd2_parser_module = 'examples.custom_parser' # Next import stuff from cmd2. It will import your module just before the cmd2.Cmd class file is imported # and therefore override the parser class it uses on its commands. from cmd2 import cmd2 +argparse.cmd2_parser_module = 'examples.custom_parser' + + if __name__ == '__main__': import sys app = cmd2.Cmd(use_ipython=True, persistent_history_file='cmd2_history.dat') diff --git a/examples/plumbum_colors.py b/examples/plumbum_colors.py index 2887ef1f9..ed65f245c 100755 --- a/examples/plumbum_colors.py +++ b/examples/plumbum_colors.py @@ -27,9 +27,10 @@ """ import argparse +from plumbum.colors import bg, fg + import cmd2 from cmd2 import ansi -from plumbum.colors import fg, bg class FgColors(ansi.ColorBase): diff --git a/examples/scripts/arg_printer.py b/examples/scripts/arg_printer.py index 19f6dd3f8..f5f30c6da 100755 --- a/examples/scripts/arg_printer.py +++ b/examples/scripts/arg_printer.py @@ -2,6 +2,7 @@ # coding=utf-8 import os import sys + print("Running Python script {!r} which was called with {} arguments".format(os.path.basename(sys.argv[0]), len(sys.argv) - 1)) for i, arg in enumerate(sys.argv[1:]): diff --git a/examples/subcommands.py b/examples/subcommands.py index 4f569b1e7..dd69d97ea 100755 --- a/examples/subcommands.py +++ b/examples/subcommands.py @@ -7,6 +7,7 @@ and provides separate contextual help. """ import argparse + import cmd2 sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball'] diff --git a/examples/unicode_commands.py b/examples/unicode_commands.py index f8381e50e..0a7c5ac74 100755 --- a/examples/unicode_commands.py +++ b/examples/unicode_commands.py @@ -3,6 +3,7 @@ """A simple example demonstrating support for unicode command names. """ import math + import cmd2 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 000000000..f60e3aed5 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,17 @@ +[flake8] +exclude = .git,.idea,.pytest_cache,.tox,.venv,.vscode,build,cmd2.egg-info,dist,htmlcov,__pycache__,*.egg +max-line-length = 127 +max-complexity = 26 + +[isort] +line_length=127 +skip=cmd2/__init__.py +multi_line_output = 3 +include_trailing_comma = true +force_grid_wrap = 0 +use_parentheses = true + +[doc8] +ignore-path=docs/_build,.git,.idea,.pytest_cache,.tox,.venv,.vscode,build,cmd2,examples,tests,cmd2.egg-info,dist,htmlcov,__pycache__,*.egg +;max-line-length=99 +verbose=0 diff --git a/setup.py b/setup.py index 860459a8a..1da7f6c04 100755 --- a/setup.py +++ b/setup.py @@ -4,6 +4,7 @@ Setuptools setup file, used to install or test 'cmd2' """ import codecs + from setuptools import setup DESCRIPTION = "cmd2 - quickly build feature-rich and user-friendly interactive command line applications in Python" diff --git a/tasks.py b/tasks.py index ed0004a6a..f95a42c09 100644 --- a/tasks.py +++ b/tasks.py @@ -15,6 +15,7 @@ import invoke + # shared function def rmrf(items, verbose=True): "Silently remove a list of directories or files" diff --git a/tests/conftest.py b/tests/conftest.py index 9ee8da195..60074f5cb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,7 +3,7 @@ Cmd2 unit/functional testing """ import sys -from contextlib import redirect_stdout, redirect_stderr +from contextlib import redirect_stderr, redirect_stdout from typing import List, Optional, Union from unittest import mock @@ -12,7 +12,6 @@ import cmd2 from cmd2.utils import StdSim - # Prefer statically linked gnureadline if available (for macOS compatibility due to issues with libedit) try: import gnureadline as readline diff --git a/tests/pyscript/environment.py b/tests/pyscript/environment.py index 5e4d93d60..f1182c829 100644 --- a/tests/pyscript/environment.py +++ b/tests/pyscript/environment.py @@ -2,6 +2,7 @@ # Tests that cmd2 populates __name__, __file__, and sets sys.path[0] to our directory import os import sys + app.cmd_echo = True if __name__ != '__main__': diff --git a/tests/test_argparse.py b/tests/test_argparse.py index db6dea21d..daf434978 100644 --- a/tests/test_argparse.py +++ b/tests/test_argparse.py @@ -9,6 +9,7 @@ import pytest import cmd2 + from .conftest import run_cmd # Prefer statically linked gnureadline if available (for macOS compatibility due to issues with libedit) diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 9e635a42e..4313647b0 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -9,9 +9,10 @@ import pytest import cmd2 -from cmd2 import with_argparser, Cmd2ArgumentParser, CompletionItem +from cmd2 import Cmd2ArgumentParser, CompletionItem, with_argparser from cmd2.utils import CompletionError, StdSim, basic_complete -from .conftest import run_cmd, complete_tester + +from .conftest import complete_tester, run_cmd # Lists used in our tests (there is a mix of sorted and unsorted on purpose) static_int_choices_list = [-1, 1, -2, 2, 0, -12] diff --git a/tests/test_argparse_custom.py b/tests/test_argparse_custom.py index 92b2ecb4f..f4db12b6d 100644 --- a/tests/test_argparse_custom.py +++ b/tests/test_argparse_custom.py @@ -9,6 +9,7 @@ import cmd2 from cmd2 import Cmd2ArgumentParser, constants from cmd2.argparse_custom import generate_range_error + from .conftest import run_cmd diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index fdeea9aa4..33f75c9e9 100755 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -13,16 +13,27 @@ import pytest +import cmd2 +from cmd2 import COMMAND_NAME, ansi, clipboard, constants, exceptions, plugin, utils + +from .conftest import ( + HELP_HISTORY, + SHORTCUTS_TXT, + SHOW_LONG, + SHOW_TXT, + complete_tester, + normalize, + odd_file_names, + run_cmd, + verify_help_text, +) + # Python 3.5 had some regressions in the unitest.mock module, so use 3rd party mock if available try: import mock except ImportError: from unittest import mock -import cmd2 -from cmd2 import ansi, clipboard, constants, exceptions, plugin, utils, COMMAND_NAME -from .conftest import (run_cmd, normalize, verify_help_text, HELP_HISTORY, SHORTCUTS_TXT, SHOW_TXT, - SHOW_LONG, complete_tester, odd_file_names) def CreateOutsimApp(): c = cmd2.Cmd() diff --git a/tests/test_completion.py b/tests/test_completion.py index e13d87dea..a380d43af 100755 --- a/tests/test_completion.py +++ b/tests/test_completion.py @@ -22,6 +22,7 @@ import cmd2 from cmd2 import utils from examples.subcommands import SubcommandsExample + from .conftest import complete_tester, normalize, run_cmd # List of strings used with completion functions diff --git a/tests/test_history.py b/tests/test_history.py index 11f189f62..6fa16ad81 100755 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -3,22 +3,23 @@ """ Test history functions of cmd2 """ -import tempfile import os +import tempfile import pytest +import cmd2 # Python 3.5 had some regressions in the unitest.mock module, so use # 3rd party mock if available from cmd2.parsing import StatementParser +from .conftest import HELP_HISTORY, normalize, run_cmd + try: import mock except ImportError: from unittest import mock -import cmd2 -from .conftest import run_cmd, normalize, HELP_HISTORY # diff --git a/tests/test_parsing.py b/tests/test_parsing.py index 5f3633208..c2c242fe3 100755 --- a/tests/test_parsing.py +++ b/tests/test_parsing.py @@ -10,6 +10,7 @@ from cmd2 import constants, exceptions, utils from cmd2.parsing import StatementParser, shlex_split + @pytest.fixture def parser(): parser = StatementParser( diff --git a/tests/test_plugin.py b/tests/test_plugin.py index bb7753f0e..132361a67 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -8,14 +8,15 @@ import pytest +import cmd2 +from cmd2 import Cmd2ArgumentParser, exceptions, plugin, with_argparser + # Python 3.5 had some regressions in the unitest.mock module, so use 3rd party mock if available try: import mock except ImportError: from unittest import mock -import cmd2 -from cmd2 import exceptions, plugin, Cmd2ArgumentParser, with_argparser class Plugin: diff --git a/tests/test_run_pyscript.py b/tests/test_run_pyscript.py index 6110e3aca..8cfd85789 100644 --- a/tests/test_run_pyscript.py +++ b/tests/test_run_pyscript.py @@ -9,7 +9,8 @@ import pytest from cmd2 import plugin, utils -from .conftest import run_cmd, odd_file_names + +from .conftest import odd_file_names, run_cmd # Python 3.5 had some regressions in the unitest.mock module, so use 3rd party mock if available try: diff --git a/tests/test_table_creator.py b/tests/test_table_creator.py index 917ba5cc9..ed53efa66 100644 --- a/tests/test_table_creator.py +++ b/tests/test_table_creator.py @@ -6,8 +6,15 @@ import pytest from cmd2 import ansi -from cmd2.table_creator import (AlternatingTable, BorderedTable, Column, HorizontalAlignment, - SimpleTable, TableCreator, VerticalAlignment) +from cmd2.table_creator import ( + AlternatingTable, + BorderedTable, + Column, + HorizontalAlignment, + SimpleTable, + TableCreator, + VerticalAlignment, +) def test_column_creation(): diff --git a/tests/test_transcript.py b/tests/test_transcript.py index aa6f8c4ea..69389b7f2 100644 --- a/tests/test_transcript.py +++ b/tests/test_transcript.py @@ -5,18 +5,19 @@ """ import argparse import os -import sys -import re import random +import re +import sys import tempfile - from unittest import mock + import pytest import cmd2 -from .conftest import run_cmd, verify_help_text from cmd2 import transcript -from cmd2.utils import StdSim, Settable +from cmd2.utils import Settable, StdSim + +from .conftest import run_cmd, verify_help_text class CmdLineApp(cmd2.Cmd):