diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 6e9c3bd7..015ecb62 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -56,18 +56,14 @@ jobs: run: | . ./env/bin/activate pip install tox==${{ env.TOX_VERSION }} - - name: Prevent 0.0.0 releases I - run: | - . ./env/bin/activate - tox -e version.py - cat px/version.py - ! grep '0.0.0' px/version.py - name: Run tox to create the dist wheel run: | . ./env/bin/activate tox - - name: Prevent 0.0.0 releases II + - name: Prevent 0.0.0 releases run: | + cat px/version.py + ! grep '0.0.0' px/version.py ls dist/ ! ls dist/ | grep '0.0.0' diff --git a/.github/workflows/macos-ci.yml b/.github/workflows/macos-ci.yml index dd69bf97..dbb4b226 100644 --- a/.github/workflows/macos-ci.yml +++ b/.github/workflows/macos-ci.yml @@ -42,4 +42,4 @@ jobs: # The point is to run as little as possible on macOS because macOS runs are # slow and expensive, look for "multiplier" on this page: # https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions - tox -e version.py,installtest,pytest,package,test-package,test-wheel + tox -e installtest,pytest,package,test-package,test-wheel diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index 2eb4dd75..00000000 --- a/.pylintrc +++ /dev/null @@ -1,4 +0,0 @@ -# Disable all categories since we're running ruff instead, but VSCode insists on -# invoking pylint all the time -[MESSAGES CONTROL] -disable=E,W,R,C,F diff --git a/README.rst b/README.rst index 3c47e8ef..470dd27c 100644 --- a/README.rst +++ b/README.rst @@ -250,7 +250,7 @@ Development Prerequisites ------------- -* A Python 3.6+ version +* A Python 3.9+ version * `tox`_ Building and Running diff --git a/devbin/ptop-in-docker.sh b/devbin/ptop-in-docker.sh index b3034a61..02641a65 100755 --- a/devbin/ptop-in-docker.sh +++ b/devbin/ptop-in-docker.sh @@ -3,7 +3,7 @@ set -euo pipefail DOCKERFILE=" -FROM python:3.6-alpine +FROM python:3.9-alpine RUN apk add sudo py3-tox shellcheck bash git unzip lsof gcc python3-dev procps acct musl-dev zip RUN echo 'root ALL=(ALL:ALL) ALL' > /etc/sudoers diff --git a/devbin/test_clone_and_build.py b/devbin/test_clone_and_build.py index 14792c52..96f7bf69 100755 --- a/devbin/test_clone_and_build.py +++ b/devbin/test_clone_and_build.py @@ -5,9 +5,9 @@ """ import os -import sys import shutil import subprocess +import sys import tempfile # Copy everything to a temporary directory @@ -38,9 +38,6 @@ ) # Build the clone -print("Building sources using setup.py...") -subprocess.run(["python3", "setup.py", "build"], check=True) - print("Building sources using python -m build...") FAKE_VERSION = "99.99.99" subprocess.run(["git", "config", "user.email", "you@example.com"], check=True) diff --git a/devbin/tox-in-docker.sh b/devbin/tox-in-docker.sh index 4fa118d8..2f424f75 100755 --- a/devbin/tox-in-docker.sh +++ b/devbin/tox-in-docker.sh @@ -3,7 +3,7 @@ set -euo pipefail DOCKERFILE=" -FROM python:3.6-alpine +FROM python:3.9-alpine RUN apk add sudo py3-tox shellcheck bash git zip unzip lsof gcc python3-dev procps acct musl-dev RUN echo 'root ALL=(ALL:ALL) ALL' > /etc/sudoers diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index 12bdfc76..00000000 --- a/mypy.ini +++ /dev/null @@ -1,14 +0,0 @@ -[mypy] -check_untyped_defs = True - -disallow_any_generics = True -disallow_any_unimported = True -disallow_subclassing_any = True -disallow_untyped_decorators = True - -strict_equality = True - -warn_incomplete_stub = True -warn_redundant_casts = True -warn_return_any = True -warn_unused_ignores = True diff --git a/px/px.py b/px/px.py index 943adcc0..750daccb 100644 --- a/px/px.py +++ b/px/px.py @@ -139,9 +139,9 @@ def handleLogMessages(messages: Optional[str]) -> None: # even if we don't use it. And this will make test avoidance fail to avoid # px.py tests every time you make a new commit (because committing recreates # version.py). - from . import version + from .version import __version__ - sys.stderr.write("px version: " + version.VERSION + "\n") + sys.stderr.write("px version: " + __version__ + "\n") sys.stderr.write("\n") sys.stderr.write("Python version: " + sys.version + "\n") @@ -168,9 +168,9 @@ def _main(argv: List[str]) -> None: # NOTE: If we "import version" at the top of this file, we will depend on it even if # we don't use it. And this will make test avoidance fail to avoid px.py tests every # time you make a new commit (because committing recreates version.py). - from . import version + from .version import __version__ - print(version.VERSION) + print(__version__) return with_pager: Optional[bool] = None diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..6cf6d4e0 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,57 @@ +[project] +name = "pxpx" +description = "ps and top for Human Beings" +readme = "README.rst" +license = "MIT" +authors = [{ name = "Johan Walles", email = "johan.walles@gmail.com" }] +requires-python = ">=3.9" +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Intended Audience :: System Administrators", + "Operating System :: MacOS", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3", + "Topic :: System :: Monitoring", + "Topic :: System :: Systems Administration", + "Topic :: Utilities", +] +dynamic = ["version"] + +[project.urls] +Homepage = "https://github.com/walles/px" + +[project.scripts] +px = "px.px:main" +ptop = "px.px:main" +pxtree = "px.px:main" + +[tool.setuptools.data-files] +"share/man/man1" = ["doc/*.1"] + +[tool.setuptools.packages.find] +include = ["px*"] + +[tool.setuptools_scm] +version_file = "px/version.py" +fallback_version = "0.0.0+unknown" + +[tool.mypy] +check_untyped_defs = true +disallow_any_generics = true +disallow_any_unimported = true +disallow_subclassing_any = true +disallow_untyped_decorators = true +strict_equality = true +warn_incomplete_stub = true +warn_redundant_casts = true +warn_return_any = true +warn_unused_ignores = true + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = ["-v"] + +[build-system] +requires = ["setuptools>=77", "setuptools-scm>=8"] +build-backend = "setuptools.build_meta" diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index fc6b1410..00000000 --- a/setup.cfg +++ /dev/null @@ -1,9 +0,0 @@ -[aliases] -test=pytest - -[flake8] -max-line-length = 100 - -[tool:pytest] -testpaths = tests -addopts = -v diff --git a/setup.py b/setup.py deleted file mode 100755 index bb02f52a..00000000 --- a/setup.py +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env python3 - -import filecmp -import os -import re -import shutil -import subprocess -import tempfile - -from setuptools import setup - -VERSIONFILE = "px/version.py" - - -def write_version(version: str, replace: bool = False) -> None: - """ - Write the given version to px/version.py. - - If px/version.py is missing, the version will always be written. - - If px/version.py is present, the version will only be written if replace is True. - """ - with tempfile.NamedTemporaryFile(suffix=".py", delete=False) as tmp: - tmp.write( - b"# NOTE: Auto generated by update_version_py() in setup.py, no touchie!\n" - ) - tmp.write(b'VERSION = "%s"\n' % bytearray(version, "utf_8")) - - # Flushing is required for filecmp.cmp() to work (below) - tmp.flush() - - if not os.path.isfile(VERSIONFILE): - # No version file found - print(f"Creating {VERSIONFILE} with version {version}") - shutil.move(tmp.name, VERSIONFILE) - elif replace and not filecmp.cmp(tmp.name, VERSIONFILE): - print(f"Updating {VERSIONFILE} to new version {version}") - shutil.move(tmp.name, VERSIONFILE) - else: - # VERSIONFILE was already up to date. If we touch it in this case, - # it will have its file timestamp updated, which will force the slow - # px_integration_test.py tests to get rerun. - # - # Just clean up our tempfile and be merry. - print(f"Not touching existing {VERSIONFILE}") - os.remove(tmp.name) - - -def update_version_py() -> str: - """ - Update px/version.py with the current git version. - - Returns the version number. - """ - git_result = subprocess.run(["git", "describe", "--dirty"], capture_output=True) - if git_result.returncode == 0: - write_version(git_result.stdout.decode("utf-8").strip(), True) - else: - # Don't overwrite any existing version since we just made this one up - write_version("0.0.0", False) - - from px import version - - return version.VERSION - - -if __name__ == "__main__": - version_for_setuptools = update_version_py() - - if not re.match(r"^[0-9]+\.[0-9]+\.[0-9]+$", version_for_setuptools): - # Setuptools requires a nice version number - version_for_setuptools = "0.0.0" - - with open( - os.path.join(os.path.dirname(__file__), "README.rst"), encoding="utf-8" - ) as fp: - LONG_DESCRIPTION = fp.read() - - setup( - name="pxpx", - version=version_for_setuptools, - description="ps and top for Human Beings", - long_description=LONG_DESCRIPTION, - long_description_content_type="text/x-rst", - author="Johan Walles", - author_email="johan.walles@gmail.com", - url="https://github.com/walles/px", - license="MIT", - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Environment :: Console", - "Intended Audience :: System Administrators", - "License :: OSI Approved :: MIT License", - "Operating System :: MacOS", - "Operating System :: POSIX :: Linux", - "Programming Language :: Python :: 3", - "Topic :: System :: Monitoring", - "Topic :: System :: Systems Administration", - "Topic :: Utilities", - ], - packages=["px"], - # See: http://setuptools.readthedocs.io/en/latest/setuptools.html#setting-the-zip-safe-flag - zip_safe=True, - setup_requires=[ - "pytest-runner", - ], - entry_points={ - "console_scripts": [ - "px = px.px:main", - "ptop = px.px:main", - "pxtree = px.px:main", - ], - }, - # Note that we're by design *not* installing man pages here. - # Using "data_files=" only puts the man pages in the egg file, - # and installing that egg doesn't put them on the destination - # system. - # - # After trying to figure this out for a bit, my conclusion is - # that "pip install" simply isn't meant for installing any man - # pages. - # - # /johan.walles@gmail.com 2018aug27 - ) diff --git a/tox.ini b/tox.ini index 44c94573..f4356e71 100644 --- a/tox.ini +++ b/tox.ini @@ -9,10 +9,8 @@ build_version = 1.2.2.post1 mypy_version = 1.15.0 ruff_version = 0.10.0 pytest_version = 8.3.5 -setuptools_version = 76.0.0 envlist= - version.py ruff-format mypy ruff-check @@ -24,42 +22,33 @@ envlist= test-build-from-clean-clone [testenv] -skip_install = true basepython = python3 allowlist_externals = /bin/bash -[testenv:version.py] -deps = - setuptools == {[tox]setuptools_version} -commands = - # This keeps px/version.py up to date - /bin/bash -c 'python3 -c "import setup; setup.update_version_py()"' - [testenv:ruff-format] +skip_install = true deps = ruff=={[tox]ruff_version} # Format locally, check in CI and fail on not-formatted code -commands = /bin/bash -c 'if [ "{env:CI:}" ] ; then CHECK="--check --diff" ; fi ; ruff format $CHECK ./*.py ./*/*.py' +commands = /bin/bash -c 'if [ "{env:CI:}" ] ; then CHECK="--check --diff" ; fi ; ruff format $CHECK' [testenv:mypy] # NOTE: In theory mypy should probably depend on ruff-format to get line numbers # in any error messages right. But since mypy tends to finish last, and being a # bit off isn't the end of the world, let's not depend on ruff-format for now # and hope nobody notices. -depends = version.py - deps = mypy=={[tox]mypy_version} pytest=={[tox]pytest_version} - types-setuptools==67.7.0.1 # Matches what was on Johan's laptop 2023-05-08 types-python-dateutil==2.8.19 commands = - /bin/bash -c 'mypy --pretty ./*.py ./*/*.py' + /bin/bash -c 'mypy --pretty .' [testenv:ruff-check] +skip_install = true # Depend on ruff-format to not complain about formatting errors -depends = version.py, ruff-format +depends = ruff-format deps = ruff=={[tox]ruff_version} @@ -67,9 +56,10 @@ deps = # Auto-fix locally but not in CI commands = - /bin/bash -c 'FIX="--fix" ; if [ "{env:CI:}" ] ; then FIX="--no-fix" ; fi ; ruff check $FIX ./*.py ./*/*.py' + /bin/bash -c 'FIX="--fix" ; if [ "{env:CI:}" ] ; then FIX="--no-fix" ; fi ; ruff check $FIX' [testenv:shellcheck] +skip_install = true commands = /bin/bash -c 'shellcheck ./*.sh ./*/*.sh' @@ -79,7 +69,7 @@ commands = {toxinidir}/tests/installtest.sh [testenv:pytest] -depends = version.py, ruff-format +depends = ruff-format deps = pytest == {[tox]pytest_version} pytest-avoidance == 0.3.0 @@ -89,7 +79,7 @@ commands = [testenv:package] # Create {toxinidir}/px.pex -depends = version.py, ruff-format +depends = ruff-format allowlist_externals = {toxinidir}/devbin/make-executable-zip.sh deps = # Used by the make-executable-zip.sh script @@ -108,16 +98,15 @@ commands = # Run pex and ensure it doesn't fail python -Werror {toxinidir}/px.pex # Test version string vs git - /bin/bash -x -c 'test "$("{toxinidir}/px.pex" --version)" = "$(git describe --dirty)"' + /bin/bash -x -c '[[ "$("{toxinidir}/px.pex" --version)" =~ ^"$(git describe | cut -f1,2 -d.)" ]]' [testenv:test-wheel] # Test installing using pip -depends = version.py, ruff-format +depends = ruff-format allowlist_externals = /bin/bash /bin/rm deps = - setuptools == {[tox]setuptools_version} build == {[tox]build_version} commands = # clean up build/ and dist/ folders @@ -130,10 +119,9 @@ commands = # Verify we can run the px we just installed using pip px tox # Test version string vs git - /bin/bash -x -c 'test "$(px --version)" = "$(git describe --dirty)"' + /bin/bash -x -c '[[ "$(px --version)" =~ ^"$(git describe | cut -f1,2 -d.)" ]]' [testenv:test-build-from-clean-clone] deps = - setuptools == {[tox]setuptools_version} build == {[tox]build_version} commands = python3 {toxinidir}/devbin/test_clone_and_build.py