diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index c7113e4c..7def0a6c 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 6210f096..1e675473 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 00000000..ba1f4663 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,22 @@ +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 + - prettier-plugin-toml@2.0.1 diff --git a/.prettierignore b/.prettierignore index c0718806..d6e44c5a 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/.readthedocs.yaml b/.readthedocs.yaml index 248b9468..bb48e095 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -4,27 +4,23 @@ # 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: + install: + - pip install . + - pip install dependency-groups + - pip-install-dependency-groups docs diff --git a/CHANGELOG.md b/CHANGELOG.md index c9830035..bdf5f916 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 new file mode 100644 index 00000000..80b452c6 --- /dev/null +++ b/Makefile @@ -0,0 +1,67 @@ +# 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 + @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" + @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: 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: 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: publish-test +publish-test: validatetag build ## Test publishing a release to PyPI. + @echo "🚀 Publishing: Dry run." + @uvx twine upload --repository testpypi dist/* + +.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: + @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 1b7e2035..a89e1bcf 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 624459ac..6e0ba19e 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 fc817007..ebdc3d66 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 a62ccc38..34728dab 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,71 @@ 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"] +plugins = ["cmd2-ext-test"] +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 +289,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", "plugins"] [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 e298fd24..ddc36fdf 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.