diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 111112af..464b98c6 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -644,12 +644,12 @@ The one plugin we consider essential for PyCharm is [RyeCharm](https://plugins.jetbrains.com/plugin/25230-ryecharm). `RyeCharm` is an all-in-one PyCharm plugin for [Astral](https://astral.sh/)-backed Python tools: [uv](https://github.com/astral-sh/uv), [Ruff](https://github.com/astral-sh/ruff), and [ty](https://github.com/astral-sh/ty). NOTE: `ty` -support is provisional as that new type checker is in early alpha developement. +support is provisional as that new type checker is in early alpha development. #### VSCode Settings While **VSCode** is a phenomenal IDE for developing in Python, the out-of-the-box experience leaves -a lot to be desired. You will need to install a number of extenstions and tweak the default +a lot to be desired. You will need to install a number of extensions and tweak the default configuration for many of them in order to get an optimal developer experience. Recommended VSCode extensions: diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 2818bd38..6a060624 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -29,5 +29,5 @@ jobs: python-version: "3.14" - name: Install the project run: uv sync --group quality - - name: Run pre-commit - run: uv run pre-commit run -a --show-diff-on-failure + - name: Run prek + run: uv run prek run -a --show-diff-on-failure diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2dc629c6..68fa58fc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,9 +3,15 @@ repos: rev: "v6.0.0" hooks: - id: check-case-conflict + - id: check-executables-have-shebangs - id: check-merge-conflict + - id: check-symlinks - id: check-toml + - id: check-yaml + - id: detect-private-key - id: end-of-file-fixer + - id: fix-byte-order-marker + - id: mixed-line-ending - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit @@ -23,3 +29,13 @@ repos: additional_dependencies: - prettier@3.8.1 - prettier-plugin-toml@2.0.6 + + - repo: https://github.com/crate-ci/typos + rev: v1.44.0 + hooks: + - id: typos + exclude: | + (?x)^( + ruff.toml| + tests/.* + )$ diff --git a/.typos.toml b/.typos.toml new file mode 100644 index 00000000..1f807952 --- /dev/null +++ b/.typos.toml @@ -0,0 +1,5 @@ +[default.extend-words] +EXPLIoT = "EXPLIoT" +Counterfit = "Counterfit" +expliot = "expliot" +counterfit = "counterfit" diff --git a/Makefile b/Makefile index 914fc664..8319eb89 100644 --- a/Makefile +++ b/Makefile @@ -7,8 +7,8 @@ install: ## Install the virtual environment with dependencies @echo "🚀 Creating uv Python virtual environment" @uv python install 3.14 @uv sync --python=3.14 - @echo "🚀 Installing Git pre-commit hooks locally" - @uv run pre-commit install + @echo "🚀 Installing Git prek hooks locally" + @uv run prek install -f @echo "🚀 Installing Prettier using npm" @npm install -q --no-fund --include=dev @@ -16,8 +16,8 @@ install: ## Install the virtual environment with dependencies check: ## Run code quality tools. @echo "🚀 Checking lock file consistency with 'pyproject.toml'" @uv lock --locked - @echo "🚀 Linting code and documentation: Running pre-commit" - @uv run pre-commit run -a + @echo "🚀 Auto-formatting/Linting code and documentation: Running prek" + @uv run prek run -a @echo "🚀 Static type checking: Running mypy" @uv run mypy diff --git a/README.md b/README.md old mode 100755 new mode 100644 index 1fb1d286..a479d9ea --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ examples. - [cmd2 example applications](https://github.com/python-cmd2/cmd2/tree/main/examples) - Basic cmd2 examples to demonstrate how to use various features - [Advanced Examples](https://github.com/jayrod/cmd2-example-apps) - - More complex examples that demonstrate more featuers about how to put together a complete + - More complex examples that demonstrate more features about how to put together a complete application - [Cookiecutter](https://github.com/cookiecutter/cookiecutter) Templates from community - Basic cookiecutter template for cmd2 application : diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py index e9609795..623da830 100644 --- a/cmd2/argparse_custom.py +++ b/cmd2/argparse_custom.py @@ -397,14 +397,14 @@ def __init__( @property def choices_provider(self) -> ChoicesProviderUnbound[CmdOrSet]: - """Retreive the internal choices_provider function.""" + """Retrieve the internal choices_provider function.""" if self.is_completer: raise AttributeError("This instance is configured as a completer, not a choices_provider") return cast(ChoicesProviderUnbound[CmdOrSet], self.to_call) @property def completer(self) -> CompleterUnbound[CmdOrSet]: - """Retreive the internal completer function.""" + """Retrieve the internal completer function.""" if not self.is_completer: raise AttributeError("This instance is configured as a choices_provider, not a completer") return cast(CompleterUnbound[CmdOrSet], self.to_call) diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index a8f5ee52..8cb373a3 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -292,7 +292,7 @@ def remove(self, command_method: CommandFunc) -> None: @dataclass(kw_only=True) class AsyncAlert: - """Contents of an asynchonous alert which display while user is at prompt. + """Contents of an asynchronous alert which display while user is at prompt. :param msg: an optional message to be printed above the prompt. :param prompt: an optional string to dynamically replace the current prompt. @@ -608,7 +608,7 @@ def __init__( # Command parsers for this Cmd instance. self._command_parsers: _CommandParsers = _CommandParsers(self) - # Members related to printing asychronous alerts + # Members related to printing asynchronous alerts self._alert_queue: deque[AsyncAlert] = deque() self._alert_condition = threading.Condition() self._alert_allowed = False @@ -3508,7 +3508,7 @@ def _pre_prompt() -> None: self._alert_allowed = False def _cmdloop(self) -> None: - """Repeatedly issue a prompt, accept input, parse it, and dispatch to apporpriate commands. + """Repeatedly issue a prompt, accept input, parse it, and dispatch to appropriate commands. Parse an initial prefix off the received input and dispatch to action methods, passing them the remainder of the line as argument. diff --git a/docs/doc_conventions.md b/docs/doc_conventions.md index 9981b75b..022eb773 100644 --- a/docs/doc_conventions.md +++ b/docs/doc_conventions.md @@ -49,7 +49,7 @@ or [The Markdown Guide](https://www.markdownguide.org/) for a more complete refe Code blocks can be created in two ways: -- Indent the block - this will show as a monospace code block, but won't include highighting +- Indent the block - this will show as a monospace code block, but won't include highlighting - use the triple backticks followed by the code language, e.g. `python` and close with triple backticks diff --git a/examples/README.md b/examples/README.md index 43928cda..2727ac64 100644 --- a/examples/README.md +++ b/examples/README.md @@ -42,7 +42,7 @@ each: - Demonstrates usage of `@with_default_category` decorator to group and categorize commands and `CommandSet` use - [dynamic_commands.py](https://github.com/python-cmd2/cmd2/blob/main/examples/dynamic_commands.py) - - Shows how `do_*` commands can be dynamically created programatically at runtime + - Shows how `do_*` commands can be dynamically created programmatically at runtime - [environment.py](https://github.com/python-cmd2/cmd2/blob/main/examples/environment.py) - Shows how to create custom `cmd2.Settable` parameters which serve as internal environment variables @@ -63,7 +63,7 @@ each: - Shows how to use various `cmd2` application lifecycle hooks - [migrating.py](https://github.com/python-cmd2/cmd2/blob/main/examples/migrating.py) - A simple `cmd` application that you can migrate to `cmd2` by changing one line -- [modular_commands.py](https://github.com/python-cmd2/cmd2/blob/main/examples/modular_commands.py) +- [modular_commandsets.py](https://github.com/python-cmd2/cmd2/blob/main/examples/modular_commandsets.py) - Complex example demonstrating a variety of methods to load `CommandSets` using a mix of command decorators - [paged_output.py](https://github.com/python-cmd2/cmd2/blob/main/examples/paged_output.py) diff --git a/examples/command_sets.py b/examples/command_sets.py index ed51c6f4..a14cb80c 100755 --- a/examples/command_sets.py +++ b/examples/command_sets.py @@ -6,7 +6,7 @@ most commands trivial because the intent is to focus on the CommandSet feature set. The `AutoLoadCommandSet` is a basic command set which is loaded automatically at application startup and stays loaded until -application exit. Ths is the simplest case of simply modularizing command definitions to different classes and/or files. +application exit. This is the simplest case of simply modularizing command definitions to different classes and/or files. The `LoadableFruits` and `LoadableVegetables` CommandSets are dynamically loadable and un-loadable at runtime using the `load` and `unload` commands. This demonstrates the ability to load and unload CommandSets based on application state. Each of these @@ -102,7 +102,7 @@ def __init__(self) -> None: self.register_command_set(AutoLoadCommandSet()) - # Store the dyanmic CommandSet classes for ease of loading and unloading + # Store the dynamic CommandSet classes for ease of loading and unloading self._fruits = LoadableFruits() self._vegetables = LoadableVegetables() @@ -147,7 +147,7 @@ def do_unload(self, ns: argparse.Namespace) -> None: @with_argparser(cut_parser) @with_category(COMMANDSET_SUBCOMMAND) def do_cut(self, ns: argparse.Namespace) -> None: - """Intended to be used with dyanmically loaded subcommands specifically.""" + """Intended to be used with dynamically loaded subcommands specifically.""" handler = ns.cmd2_handler.get() if handler is not None: handler(ns) diff --git a/examples/custom_types.py b/examples/custom_types.py index ea8a4062..39bfeecf 100644 --- a/examples/custom_types.py +++ b/examples/custom_types.py @@ -51,7 +51,7 @@ def integer(value_str: str) -> int: def hexadecimal(value_str: str) -> int: - """Parse hexidecimal integer, with optional '0x' prefix.""" + """Parse hexadecimal integer, with optional '0x' prefix.""" return int(value_str, base=16) diff --git a/examples/modular_commands.py b/examples/modular_commandsets.py similarity index 100% rename from examples/modular_commands.py rename to examples/modular_commandsets.py diff --git a/pyproject.toml b/pyproject.toml index 20daa022..281032af 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ dev = [ "ipython>=8.23", "mkdocstrings[python]>=1", "mypy>=1.13", - "pre-commit>=3", + "prek>=0.3.5", "pytest>=8.1.1", "pytest-cov>=5", "pytest-mock>=3.14.1", @@ -58,7 +58,7 @@ docs = [ "setuptools_scm>=8", "zensical>=0.0.17", ] -quality = ["pre-commit>=3"] +quality = ["prek>=0.3.5"] test = [ "codecov>=2.1", "coverage>=7.11", diff --git a/ruff.toml b/ruff.toml index 7d5962b7..706aa072 100644 --- a/ruff.toml +++ b/ruff.toml @@ -154,7 +154,7 @@ mccabe.max-complexity = 49 # Ignore starting a process with a partial executable path (i.e. git) "scripts/validate_tag.py" = ["S607"] -# Ingore various rulesets in test directories +# Ignore various rulesets in test directories "{tests}/*.py" = [ "ANN", # Ignore all type annotation rules in test folders "ARG", # Ignore all unused argument warnings in test folders diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index 5cfd0d5e..7bd349a3 100644 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -2900,7 +2900,7 @@ def test_macro_usage_with_missing_args(base_app) -> None: assert "expects at least 2 arguments" in err[0] -def test_macro_usage_with_exta_args(base_app) -> None: +def test_macro_usage_with_extra_args(base_app) -> None: # Create the macro out, _err = run_cmd(base_app, 'macro create fake help {1}') assert out == normalize("Macro 'fake' created") diff --git a/tests/test_commandset.py b/tests/test_commandset.py index 686c7928..330928f2 100644 --- a/tests/test_commandset.py +++ b/tests/test_commandset.py @@ -159,7 +159,7 @@ def do_builtin(self, _) -> None: # Create a synonym to a command outside of this CommandSet with subcommands. # This will best test the synonym check in cmd2.Cmd._check_uninstallable() when - # we unresgister this CommandSet. + # we unregister this CommandSet. do_alias_synonym = cmd2.Cmd.do_alias cs = SynonymCommandSet("foo") diff --git a/tests/test_pt_utils.py b/tests/test_pt_utils.py index 2664848e..3051c971 100644 --- a/tests/test_pt_utils.py +++ b/tests/test_pt_utils.py @@ -474,7 +474,7 @@ def test_get_completions_add_opening_quote_and_return_results( def test_get_completions_allow_finalization( self, line, match, quote_char, end_of_line, expected, mock_cmd_app: MockCmd ) -> None: - """Test that get_completions corectly handles finalizing single matches.""" + """Test that get_completions correctly handles finalizing single matches.""" completer = pt_utils.Cmd2Completer(cast(Any, mock_cmd_app)) # Set up document