From dec23d08b6d71a16ff85987612de8ea5a8d45971 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Thu, 16 Jan 2025 22:44:33 -0500 Subject: [PATCH 1/7] Added basic git pre-commit hook setup for auto-formatting. Also: - Added a simple Makefile for easily installing and using a uv-based local dev setup - Refactored pyproject.toml to use the recommended dependency-groups and minimum versions for all dependencies --- .github/CONTRIBUTING.md | 10 +++- .gitignore | 2 +- .pre-commit-config.yaml | 21 +++++++++ .prettierignore | 2 +- Makefile | 54 +++++++++++++++++++++ examples/scripts/nested.txt | 1 - examples/tmux_launch.sh | 2 +- examples/tmux_split.sh | 2 +- pyproject.toml | 94 +++++++++++++++++-------------------- readme_files/shout_out.csv | 2 +- 10 files changed, 132 insertions(+), 58 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 Makefile diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index c7113e4cd..7def0a6c4 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -88,7 +88,7 @@ $ pip freeze | grep pyperclip If your versions are lower than the prerequisite versions, you should update. -If you do not already have Python installed on your machine, we recommend using [uv](https://github.com/astral-sh/uv) +If you do not already have Python installed on your machine, we recommend using [uv](https://github.com/astral-sh/uv) for all of your Python needs because it is extremely fast, meets all Python installation and packaging needs, and works on all platforms (Windows, Mac, and Linux). You can install `uv` using instructions at the link above. @@ -221,7 +221,13 @@ To create a virtual environment and install everything needed for `cmd2` develop from a GitHub checkout: ```sh -uv venv +make install +``` + +To install the recommended Git pre-commit hooks for auto-formatting locally, do the following: + +```sh +uv run pre-commit run -a ``` To create a new virtualenv, using a specific version of Python you have installed, use the diff --git a/.gitignore b/.gitignore index 6210f0968..1e675473e 100644 --- a/.gitignore +++ b/.gitignore @@ -54,4 +54,4 @@ uv.lock # Node/npm used for installing Prettier locally to override the outdated version that is bundled with the VSCode extension node_modules/ package-lock.json -package.json \ No newline at end of file +package.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..cac9bf4ae --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,21 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: "v5.0.0" + hooks: + - id: check-case-conflict + - id: check-merge-conflict + - id: end-of-file-fixer + - id: trailing-whitespace + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: "v0.9.2" + hooks: + - id: ruff-format + args: [--config=pyproject.toml] + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: "v3.1.0" + hooks: + - id: prettier + additional_dependencies: + - prettier@3.4.2 diff --git a/.prettierignore b/.prettierignore index c07188067..d6e44c5a2 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,2 @@ # Markdown documentation files with non-standards syntax for mkdocstrings that Prettier should not auto-format -docs/features/initialization.md \ No newline at end of file +docs/features/initialization.md diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..6509c3d42 --- /dev/null +++ b/Makefile @@ -0,0 +1,54 @@ +# Simple Makefile for use with a uv-based development environment +.PHONY: install +install: ## Install the virtual environment + @echo "🚀 Creating virtual environment" + @uv sync + +.PHONY: check +check: ## Run code quality tools. + @echo "🚀 Checking lock file consistency with 'pyproject.toml'" + @uv lock --locked + @echo "🚀 Linting code: Running pre-commit" + @uv run pre-commit run -a + @echo "🚀 Static type checking: Running mypy" + @uv run mypy + +.PHONY: test +test: ## Test the code with pytest. + @echo "🚀 Testing code: Running pytest" + @uv run python -m pytest --cov --cov-config=pyproject.toml --cov-report=xml tests + +.PHONY: build +build: clean-build ## Build wheel file + @echo "🚀 Creating wheel file" + @uvx --from build pyproject-build --installer uv + +.PHONY: clean-build +clean-build: ## Clean build artifacts + @echo "🚀 Removing build artifacts" + @uv run python -c "import shutil; import os; shutil.rmtree('dist') if os.path.exists('dist') else None" + +.PHONY: publish +publish: ## Publish a release to PyPI. + @echo "🚀 Publishing: Dry run." + @uvx --from build pyproject-build --installer uv + @echo "🚀 Publishing." + @uvx twine upload --repository-url https://upload.pypi.org/legacy/ dist/* + +.PHONY: build-and-publish +build-and-publish: build publish ## Build and publish. + +.PHONY: docs-test +docs-test: ## Test if documentation can be built without warnings or errors + @uv run mkdocs build -s + +.PHONY: docs +docs: ## Build and serve the documentation + @uv run mkdocs serve + +.PHONY: help +help: + @uv run python -c "import re; \ + [[print(f'\033[36m{m[0]:<20}\033[0m {m[1]}') for m in re.findall(r'^([a-zA-Z_-]+):.*?## (.*)$$', open(makefile).read(), re.M)] for makefile in ('$(MAKEFILE_LIST)').strip().split()]" + +.DEFAULT_GOAL := help diff --git a/examples/scripts/nested.txt b/examples/scripts/nested.txt index 1b7e20358..a89e1bcfc 100644 --- a/examples/scripts/nested.txt +++ b/examples/scripts/nested.txt @@ -1,3 +1,2 @@ !echo "Doing a relative run script" _relative_run_script script.txt - diff --git a/examples/tmux_launch.sh b/examples/tmux_launch.sh index 624459acd..6e0ba19e4 100755 --- a/examples/tmux_launch.sh +++ b/examples/tmux_launch.sh @@ -33,4 +33,4 @@ if [ $# -eq 1 ] fi tmux new-session -s "tmux window demo" -n "$FIRST_COMMAND" "$FIRST_COMMAND ;read" \; \ - new-window -n "$SECOND_COMMAND" "$SECOND_COMMAND ; read" \; previous-window \ No newline at end of file + new-window -n "$SECOND_COMMAND" "$SECOND_COMMAND ; read" \; previous-window diff --git a/examples/tmux_split.sh b/examples/tmux_split.sh index fc8170073..ebdc3d66f 100755 --- a/examples/tmux_split.sh +++ b/examples/tmux_split.sh @@ -31,4 +31,4 @@ fi tmux new-session -s "tmux split pane demo" "$FIRST_COMMAND ; read" \; \ split-window "$SECOND_COMMAND ; read" \; \ - select-layout even-vertical \ No newline at end of file + select-layout even-vertical diff --git a/pyproject.toml b/pyproject.toml index a62ccc389..66760a8fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["build", "setuptools>=64", "setuptools-scm>=8"] +requires = ["build>=1.2.1", "setuptools>=64", "setuptools-scm>=8"] build-backend = "setuptools.build_meta" [project] @@ -28,58 +28,70 @@ classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", ] dependencies = [ - "gnureadline; platform_system == 'Darwin'", - "pyperclip", - "pyreadline3; platform_system == 'Windows'", - "wcwidth", + "gnureadline>=8; platform_system == 'Darwin'", + "pyperclip>=1.8", + "pyreadline3>=3.4; platform_system == 'Windows'", + "wcwidth>=0.2.10", ] -[project.optional-dependencies] -build = ["build", "setuptools", "setuptools-scm"] +[dependency-groups] +build = ["build>=1.2.1", "setuptools>=64", "setuptools-scm>=8"] dev = [ - "black", - "codecov", - "griffe-typingdoc", - "invoke", - "mkdocs-include-markdown-plugin", - "mkdocs-macros-plugin", - "mkdocs-material", - "mkdocstrings[python]", - "mypy", - "pytest", - "pytest-cov", - "pytest-mock", - "ruff", - "twine", + "black>=24", + "codecov>=2", + "griffe-typingdoc>=0.2", + "invoke>=2", + "mkdocs-include-markdown-plugin>=6", + "mkdocs-macros-plugin>=1", + "mkdocs-material>=8", + "mkdocstrings[python]>=0.26", + "mypy>=1.12", + "pre-commit>=2.20.0", + "pytest>=7", + "pytest-cov>=4", + "pytest-mock>=3.14", + "ruff>=0.9", + "twine>=6", ] docs = [ - "black", - "griffe-typingdoc", - "mkdocs-include-markdown-plugin", - "mkdocs-macros-plugin", - "mkdocs-material", - "mkdocstrings[python]", - "setuptools", - "setuptools_scm", + "black>=24", + "griffe-typingdoc>=0.2", + "mkdocs-include-markdown-plugin>=6", + "mkdocs-macros-plugin>=1", + "mkdocs-material>=8", + "mkdocstrings[python]>=0.26", + "setuptools>=64", + "setuptools_scm>=8", ] -test = ["codecov", "coverage", "pytest", "pytest-cov", "pytest-mock"] -validate = ["mypy", "ruff", "types-setuptools"] +test = [ + "codecov>=2", + "coverage>=7", + "pytest>=7", + "pytest-cov>=4", + "pytest-mock>=3.14", +] +validate = ["mypy>=1.12", "ruff>=0.9", "types-setuptools>=69"] [tool.mypy] disallow_incomplete_defs = true disallow_untyped_calls = true disallow_untyped_defs = true exclude = [ + "^.git/", + "^.venv/", "^build/", # .build directory "^docs/", # docs directory + "^dist/", "^examples/", # examples directory "^plugins/*", # plugins directory "^noxfile\\.py$", # nox config file "setup\\.py$", # any files named setup.py + "^site/", "^tasks\\.py$", # tasks.py invoke config file "^tests/", # tests directory "^tests_isolated/", # tests_isolated directory ] +files = ['.'] show_column_numbers = true show_error_codes = true show_error_context = true @@ -276,25 +288,7 @@ packages = ["cmd2"] [tool.setuptools_scm] [tool.uv] -dev-dependencies = [ - "black", - "build", - "cmd2-ext-test", - "codecov", - "griffe-typingdoc", - "invoke", - "mkdocs-include-markdown-plugin", - "mkdocs-macros-plugin", - "mkdocs-material", - "mkdocstrings[python]", - "mypy", - "pytest", - "pytest-cov", - "pytest-mock", - "ruff", - "ruff", - "twine", -] +default-groups = ["dev"] [tool.uv.sources] cmd2-ext-test = { path = "plugins/ext_test", editable = true } diff --git a/readme_files/shout_out.csv b/readme_files/shout_out.csv index e298fd24b..ddc36fdff 100644 --- a/readme_files/shout_out.csv +++ b/readme_files/shout_out.csv @@ -11,4 +11,4 @@ Application Name, Description Oldies but goodie,, [JSShell](https://github.com/Den1al/JSShell),An interactive multi-user web JavaScript shell. -[FLASHMINGO](https://github.com/fireeye/flashmingo),Automatic analysis of SWF files based on some heuristics. Extensible via plugins. \ No newline at end of file +[FLASHMINGO](https://github.com/fireeye/flashmingo),Automatic analysis of SWF files based on some heuristics. Extensible via plugins. From 03f3cc1964584c4e882eea8327351e7bf37f71a1 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Thu, 16 Jan 2025 22:55:28 -0500 Subject: [PATCH 2/7] Fix bug in pyproject.toml where cmd2-ext-test wasn't getting installed for test setup --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 66760a8fc..34728dab7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,6 +63,7 @@ docs = [ "setuptools>=64", "setuptools_scm>=8", ] +plugins = ["cmd2-ext-test"] test = [ "codecov>=2", "coverage>=7", @@ -288,7 +289,7 @@ packages = ["cmd2"] [tool.setuptools_scm] [tool.uv] -default-groups = ["dev"] +default-groups = ["dev", "plugins"] [tool.uv.sources] cmd2-ext-test = { path = "plugins/ext_test", editable = true } From 6bd3ef556e507e03340e3ae1fcf950e461a57d12 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Thu, 16 Jan 2025 22:57:58 -0500 Subject: [PATCH 3/7] Minor pre-commit tweak to support TOML formatting --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cac9bf4ae..ba1f46632 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,3 +19,4 @@ repos: - id: prettier additional_dependencies: - prettier@3.4.2 + - prettier-plugin-toml@2.0.1 From b58394aedb5884eaf31c6b765ecd387b9a1196c7 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Thu, 16 Jan 2025 23:17:20 -0500 Subject: [PATCH 4/7] Try to fix ReadTheDocs build since that doesn't yet natively support pyproject.toml dependency groups --- .readthedocs.yaml | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 248b94689..4362f1846 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -4,27 +4,27 @@ # Required version: 2 -# Set the OS, Python version and other tools you might need -build: - os: ubuntu-24.04 - tools: - python: "3" - -# Build documentation in the "docs/" directory with MkDocs -mkdocs: - configuration: mkdocs.yml - # Optionally build your docs in additional formats such as PDF and ePub # formats: # - pdf # - epub formats: all -# Optional but recommended, declare the Python requirements required to build your documentation -# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html -python: - install: - - method: pip - path: . - extra_requirements: - - docs +# Build documentation in the "docs/" directory with MkDocs +mkdocs: + configuration: mkdocs.yml + +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-24.04 + tools: + python: "3.13" + jobs: + create_environment: + - asdf plugin add uv + - asdf install uv latest + - asdf global uv latest + - uv venv + build: + html: + - NO_COLOR=1 uv run mkdocs build --strict --site-dir $READTHEDOCS_OUTPUT/html From 4a868a6f3eb9332be63fe357c8059f364537f535 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Thu, 16 Jan 2025 23:26:11 -0500 Subject: [PATCH 5/7] Try to fix RTD build by using pip to install dependency-groups --- .readthedocs.yaml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 4362f1846..bb48e0950 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -20,11 +20,7 @@ build: tools: python: "3.13" jobs: - create_environment: - - asdf plugin add uv - - asdf install uv latest - - asdf global uv latest - - uv venv - build: - html: - - NO_COLOR=1 uv run mkdocs build --strict --site-dir $READTHEDOCS_OUTPUT/html + install: + - pip install . + - pip install dependency-groups + - pip-install-dependency-groups docs From 60edfda5394b821c90a2d04c697422d36d8e0537 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Thu, 16 Jan 2025 23:36:38 -0500 Subject: [PATCH 6/7] Updated `make tests` comment to match `inv pytest` --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 6509c3d42..16cb441cc 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ check: ## Run code quality tools. test: ## Test the code with pytest. @echo "🚀 Testing code: Running pytest" @uv run python -m pytest --cov --cov-config=pyproject.toml --cov-report=xml tests + @uv run python -m pytest --cov --cov-config=pyproject.toml --cov-report=xml tests_isolated .PHONY: build build: clean-build ## Build wheel file From 841430d0db0fe39f8ee607f09e9e875b2a5572a3 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Fri, 17 Jan 2025 18:21:22 -0500 Subject: [PATCH 7/7] Add missing commands to Makefile and prepare for release --- CHANGELOG.md | 2 +- Makefile | 40 ++++++++++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9830035d..bdf5f9165 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 2.5.9 (TBD) +## 2.5.9 (January 17, 2025) - Bug Fixes - Fixed 'index out of range' error when passing no arguments to an argparse-based command function. diff --git a/Makefile b/Makefile index 16cb441cc..80b452c61 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,14 @@ test: ## Test the code with pytest. @uv run python -m pytest --cov --cov-config=pyproject.toml --cov-report=xml tests @uv run python -m pytest --cov --cov-config=pyproject.toml --cov-report=xml tests_isolated +.PHONY: docs-test +docs-test: ## Test if documentation can be built without warnings or errors + @uv run mkdocs build -s + +.PHONY: docs +docs: ## Build and serve the documentation + @uv run mkdocs serve + .PHONY: build build: clean-build ## Build wheel file @echo "🚀 Creating wheel file" @@ -29,23 +37,27 @@ clean-build: ## Clean build artifacts @echo "🚀 Removing build artifacts" @uv run python -c "import shutil; import os; shutil.rmtree('dist') if os.path.exists('dist') else None" -.PHONY: publish -publish: ## Publish a release to PyPI. - @echo "🚀 Publishing: Dry run." - @uvx --from build pyproject-build --installer uv - @echo "🚀 Publishing." - @uvx twine upload --repository-url https://upload.pypi.org/legacy/ dist/* +.PHONY: tag +tag: ## Add a Git tag and push it to origin with syntax: make tag TAG=tag_name + @echo "🚀 Creating git tag: ${TAG}" + @git tag -a ${TAG} + @echo "🚀 Pushing tag to origin: ${TAG}" + @git push origin ${TAG} -.PHONY: build-and-publish -build-and-publish: build publish ## Build and publish. +.PHONY: validate-tag +validate-tag: ## Check to make sure that a tag exists for the current HEAD and it looks like a valid version number + @echo "🚀 Validating version tag" + @uv run inv validatetag -.PHONY: docs-test -docs-test: ## Test if documentation can be built without warnings or errors - @uv run mkdocs build -s +.PHONY: publish-test +publish-test: validatetag build ## Test publishing a release to PyPI. + @echo "🚀 Publishing: Dry run." + @uvx twine upload --repository testpypi dist/* -.PHONY: docs -docs: ## Build and serve the documentation - @uv run mkdocs serve +.PHONY: publish +publish: validatetag build ## Publish a release to PyPI. + @echo "🚀 Publishing." + @uvx twine upload --repository-url https://upload.pypi.org/legacy/ dist/* .PHONY: help help: