From 56146e0cedf10bcd0b8efd6e92e2f87fdde8381f Mon Sep 17 00:00:00 2001 From: Michael Howitz Date: Thu, 16 Jan 2025 09:01:15 +0100 Subject: [PATCH 1/7] Drop support for Python 3.7, 3.8. --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e0b13bb..66e5f27 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,8 @@ Change log ================ +- Drop support for Python 3.7, 3.8. + 5.1 (2024-05-03) ================ From d70ca0b41e6a54a8968d2164ce62121e919a1737 Mon Sep 17 00:00:00 2001 From: Michael Howitz Date: Thu, 16 Jan 2025 09:01:16 +0100 Subject: [PATCH 2/7] Add support for Python 3.13. --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 66e5f27..0d2c49d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,8 @@ Change log ================ +- Add support for Python 3.13. + - Drop support for Python 3.7, 3.8. 5.1 (2024-05-03) From 4bae1fb1ef240c50e27a673e58a08fe7869137a8 Mon Sep 17 00:00:00 2001 From: Michael Howitz Date: Thu, 16 Jan 2025 09:03:06 +0100 Subject: [PATCH 3/7] Configuring for buildout-recipe --- .coveragerc | 29 ---------------------------- .github/workflows/pre-commit.yml | 33 ++++++++++++++++++++++++++++++++ .github/workflows/tests.yml | 21 +++++++++++--------- .meta.toml | 2 +- .pre-commit-config.yaml | 28 +++++++++++++++++++++++++++ MANIFEST.in | 2 +- pyproject.toml | 27 ++++++++++++++++++++++++++ tox.ini | 33 ++++++++++++++------------------ 8 files changed, 116 insertions(+), 59 deletions(-) delete mode 100644 .coveragerc create mode 100644 .github/workflows/pre-commit.yml create mode 100644 .pre-commit-config.yaml create mode 100644 pyproject.toml diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 1fef466..0000000 --- a/.coveragerc +++ /dev/null @@ -1,29 +0,0 @@ -# Generated from: -# https://github.com/zopefoundation/meta/tree/master/config/buildout-recipe -[run] -source = zdaemon -branch = true -parallel = true -data_file = $COVERAGE_HOME.coverage -omit = */__main__.py - -[paths] -source = - src/ - .tox/*/lib/python*/site-packages/ - .tox/pypy*/site-packages/ - -[report] -precision = 2 -exclude_lines = - except ImportError: - if __name__ == '__main__': - pragma: no cover - pragma: nocover - raise AssertionError - raise NotImplementedError - raise unittest.Skip - self.fail\( - -[html] -directory = parts/htmlcov diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000..aa4a089 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,33 @@ +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/buildout-recipe +name: pre-commit + +on: + pull_request: + push: + branches: + - master + # Allow to run this workflow manually from the Actions tab + workflow_dispatch: + +env: + FORCE_COLOR: 1 + +jobs: + pre-commit: + name: linting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.x + - uses: pre-commit/action@v3.0.1 + with: + extra_args: --all-files --show-diff-on-failure + env: + PRE_COMMIT_COLOR: always + - uses: pre-commit-ci/lite-action@v1.1.0 + if: always() + with: + msg: Apply pre-commit code formatting diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5c9e554..cec145e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,16 +20,14 @@ jobs: - ["ubuntu", "ubuntu-latest"] config: # [Python version, tox env] - - ["3.9", "release-check"] - - ["3.9", "lint"] - - ["3.7", "py37"] - - ["3.8", "py38"] - - ["3.9", "py39"] - - ["3.10", "py310"] - - ["3.11", "py311"] - - ["3.12", "py312"] + - ["3.11", "release-check"] + - ["3.9", "py39"] + - ["3.10", "py310"] + - ["3.11", "py311"] + - ["3.12", "py312"] + - ["3.13", "py313"] - ["pypy-3.10", "pypy3"] - - ["3.9", "coverage"] + - ["3.11", "coverage"] runs-on: ${{ matrix.os[1] }} if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name @@ -40,6 +38,7 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.config[0] }} + allow-prereleases: true - name: Pip cache uses: actions/cache@v4 with: @@ -53,7 +52,11 @@ jobs: python -m pip install --upgrade pip pip install tox - name: Test + if: ${{ !startsWith(runner.os, 'Mac') }} run: tox -e ${{ matrix.config[1] }} + - name: Test (macOS) + if: ${{ startsWith(runner.os, 'Mac') }} + run: tox -e ${{ matrix.config[1] }}-universal2 - name: Coverage if: matrix.config[1] == 'coverage' run: | diff --git a/.meta.toml b/.meta.toml index 4e9a70c..e62fc1c 100644 --- a/.meta.toml +++ b/.meta.toml @@ -2,7 +2,7 @@ # https://github.com/zopefoundation/meta/tree/master/config/buildout-recipe [meta] template = "buildout-recipe" -commit-id = "7713fd86" +commit-id = "a3a78df1" [python] with-pypy = true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..315e541 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,28 @@ +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/buildout-recipe +minimum_pre_commit_version: '3.6' +repos: + - repo: https://github.com/pycqa/isort + rev: "5.13.2" + hooks: + - id: isort + - repo: https://github.com/hhatto/autopep8 + rev: "v2.3.1" + hooks: + - id: autopep8 + args: [--in-place, --aggressive, --aggressive] + - repo: https://github.com/asottile/pyupgrade + rev: v3.19.1 + hooks: + - id: pyupgrade + args: [--py39-plus] + - repo: https://github.com/isidentical/teyit + rev: 0.4.3 + hooks: + - id: teyit + - repo: https://github.com/PyCQA/flake8 + rev: "7.1.1" + hooks: + - id: flake8 + additional_dependencies: + - flake8-debugger == 4.1.2 diff --git a/MANIFEST.in b/MANIFEST.in index 86edcc8..0a127ac 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,7 +5,7 @@ include *.rst include *.txt include buildout.cfg include tox.ini -include .coveragerc +include .pre-commit-config.yaml recursive-include src *.py recursive-include src *.conf diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..54f11f8 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,27 @@ +# +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/buildout-recipe + +[build-system] +requires = ["setuptools <= 75.6.0"] +build-backend = "setuptools.build_meta" + +[tool.coverage.run] +branch = true +source = ["zdaemon"] +parallel = true +data_file = "$COVERAGE_HOME.coverage" +omit = "*/__main__.py" + +[tool.coverage.report] +fail_under = 79 +precision = 2 +ignore_errors = true +show_missing = true +exclude_lines = ["pragma: no cover", "pragma: nocover", "except ImportError:", "raise NotImplementedError", "if __name__ == '__main__':", "self.fail", "raise AssertionError", "raise unittest.Skip"] + +[tool.coverage.html] +directory = "parts/htmlcov" + +[tool.coverage.paths] +source = ["src/", ".tox/*/lib/python*/site-packages/", ".tox/pypy*/site-packages/"] diff --git a/tox.ini b/tox.ini index f8f4187..b20999b 100644 --- a/tox.ini +++ b/tox.ini @@ -5,30 +5,34 @@ minversion = 3.18 envlist = release-check lint - py37 - py38 py39 py310 py311 py312 + py313 pypy3 coverage [testenv] usedevelop = true deps = -setenv = - py312: VIRTUALENV_PIP=23.1.2 - py312: PIP_REQUIRE_VIRTUALENV=0 + setuptools <= 75.6.0 commands = zope-testrunner --test-path=src {posargs:-vc} extras = test + +[testenv:setuptools-latest] +basepython = python3 +deps = + git+https://github.com/pypa/setuptools.git\#egg=setuptools + [testenv:release-check] description = ensure that the distribution is ready to release basepython = python3 skip_install = true deps = + setuptools <= 75.6.0 twine build check-manifest @@ -42,23 +46,14 @@ commands = twine check dist/* [testenv:lint] +description = This env runs all linters configured in .pre-commit-config.yaml basepython = python3 skip_install = true deps = - isort - flake8 -commands = - isort --check-only --diff {toxinidir}/src {toxinidir}/setup.py - flake8 src setup.py - -[testenv:isort-apply] -basepython = python3 -skip_install = true + pre-commit commands_pre = -deps = - isort commands = - isort {toxinidir}/src {toxinidir}/setup.py [] + pre-commit run --all-files --show-diff-on-failure [testenv:coverage] basepython = python3 @@ -68,11 +63,11 @@ setenv = COVERAGE_PROCESS_START={toxinidir}/.coveragerc COVERAGE_HOME={toxinidir}/ deps = - coverage + coverage[toml] commands = mkdir -p {toxinidir}/parts/htmlcov coverage erase coverage run -m zope.testrunner --test-path=src {posargs:-vc} coverage combine coverage html - coverage report -m --fail-under=79 + coverage report From a47be476ff9a4d9b6336cf8b8d3b1d6ef4f80f19 Mon Sep 17 00:00:00 2001 From: Michael Howitz Date: Thu, 16 Jan 2025 09:06:48 +0100 Subject: [PATCH 4/7] Update Python version support. --- pyproject.toml | 4 ++-- setup.py | 5 ++--- src/zdaemon/tests/tests.py | 4 ++-- src/zdaemon/tests/testzdoptions.py | 2 +- src/zdaemon/tests/testzdrun.py | 11 +++++++---- src/zdaemon/zdctl.py | 2 +- src/zdaemon/zdrun.py | 23 ++++++++++++----------- tox.ini | 2 +- 8 files changed, 28 insertions(+), 25 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 54f11f8..b168376 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,4 +1,4 @@ -# +# # Generated from: # https://github.com/zopefoundation/meta/tree/master/config/buildout-recipe @@ -11,7 +11,7 @@ branch = true source = ["zdaemon"] parallel = true data_file = "$COVERAGE_HOME.coverage" -omit = "*/__main__.py" +omit = ["*/__main__.py"] [tool.coverage.report] fail_under = 79 diff --git a/setup.py b/setup.py index 524ff66..3b65b01 100644 --- a/setup.py +++ b/setup.py @@ -60,12 +60,11 @@ def read(*rnames): 'License :: OSI Approved :: Zope Public License', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Operating System :: POSIX', @@ -74,7 +73,7 @@ def read(*rnames): zip_safe=False, entry_points=entry_points, include_package_data=True, - python_requires='>=3.7', + python_requires='>=3.9', install_requires=[ "ZConfig", "setuptools" diff --git a/src/zdaemon/tests/tests.py b/src/zdaemon/tests/tests.py index eae04f5..abaeda3 100644 --- a/src/zdaemon/tests/tests.py +++ b/src/zdaemon/tests/tests.py @@ -40,7 +40,7 @@ pkg_resources.Requirement.parse('zdaemon')).location zconfig_loc = pkg_resources.working_set.find( pkg_resources.Requirement.parse('ZConfig')).location -except (ImportError, AttributeError): +except (ModuleNotFoundError, AttributeError): zdaemon_loc = os.path.dirname(os.path.dirname(zdaemon.__file__)) zconfig_loc = os.path.dirname(os.path.dirname(ZConfig.__file__)) @@ -535,7 +535,7 @@ def checkenv(match): try: import coverage -except ImportError: +except ModuleNotFoundError: pass else: coverage.process_startup() diff --git a/src/zdaemon/tests/testzdoptions.py b/src/zdaemon/tests/testzdoptions.py index 5495179..27e8151 100644 --- a/src/zdaemon/tests/testzdoptions.py +++ b/src/zdaemon/tests/testzdoptions.py @@ -225,7 +225,7 @@ def test_handler_side_effect(self): L = [] options.add("setting", None, "a:", "append=", handler=L.append) options.realize(["-a2", "--append", "3"]) - self.assertTrue(options.setting is None) + self.assertIsNone(options.setting) self.assertEqual(L, ["2", "3"]) def test_handler_with_bad_value(self): diff --git a/src/zdaemon/tests/testzdrun.py b/src/zdaemon/tests/testzdrun.py index 3780110..5a85b64 100644 --- a/src/zdaemon/tests/testzdrun.py +++ b/src/zdaemon/tests/testzdrun.py @@ -70,7 +70,7 @@ def tearDown(self): signal.signal(sig, signal.SIG_DFL) try: os.unlink(self.zdsock) - except os.error: + except OSError: pass output = self.new_stdout.getvalue() self.assertEqual(self.expect, output) @@ -234,8 +234,11 @@ def testRunIgnoresParentSignals(self): # Make sure the child is still responsive. response = send_action('status\n', zdrun_socket, raise_on_error=True) - self.assertTrue(b'\n' in response, - 'no newline in response: ' + repr(response)) + self.assertIn( + b'\n', + response, + 'no newline in response: ' + repr(response) + ) # Kill the process. send_action('stop\n', zdrun_socket) finally: @@ -252,7 +255,7 @@ def testRunIgnoresParentSignals(self): for fname in os.listdir(tmp): try: os.unlink(os.path.join(tmp, fname)) - except os.error: + except OSError: pass os.rmdir(tmp) diff --git a/src/zdaemon/zdctl.py b/src/zdaemon/zdctl.py index 1426eae..f5ec237 100755 --- a/src/zdaemon/zdctl.py +++ b/src/zdaemon/zdctl.py @@ -415,7 +415,7 @@ def do_kill(self, arg): print("kill(%d, %d)" % (self.zd_pid, sig)) try: os.kill(self.zd_pid, sig) - except os.error as msg: + except OSError as msg: print("Error:", msg) else: print("signal %s sent to process %d" % (signame, self.zd_pid)) diff --git a/src/zdaemon/zdrun.py b/src/zdaemon/zdrun.py index 434aa14..c661535 100755 --- a/src/zdaemon/zdrun.py +++ b/src/zdaemon/zdrun.py @@ -131,7 +131,7 @@ def _set_filename(self, program): filename = program try: st = os.stat(filename) - except os.error: + except OSError: self.options.usage("can't stat program %r" % program) else: path = get_path() @@ -139,7 +139,7 @@ def _set_filename(self, program): filename = os.path.join(dir, program) try: st = os.stat(filename) - except os.error: + except OSError: continue mode = st[ST_MODE] if mode & 0o111: @@ -189,7 +189,7 @@ def spawn(self): self.lasttime = time.time() try: pid = os.fork() - except os.error: + except OSError: return 0 if pid != 0: # Parent @@ -210,11 +210,11 @@ def spawn(self): for i in range(3, 100): try: os.close(i) - except os.error: + except OSError: pass try: os.execv(self.filename, self.args) - except os.error as err: + except OSError as err: sys.stderr.write("can't exec %r: %s\n" % (self.filename, err)) sys.stderr.flush() # just in case @@ -232,7 +232,7 @@ def kill(self, sig): return "no subprocess running" try: os.kill(self.pid, sig) - except os.error as msg: + except OSError as msg: return str(msg) return None @@ -268,7 +268,7 @@ def run(self): finally: try: os.unlink(self.options.sockname) - except os.error: + except OSError: pass mastersocket = None @@ -286,7 +286,7 @@ def opensocket(self): try: os.link(tempname, sockname) break - except os.error: + except OSError: # Lock contention, or stale socket. self.checkopen() # Stale socket -- delete, sleep, and try again. @@ -311,7 +311,7 @@ def opensocket(self): def unlink_quietly(self, filename): try: os.unlink(filename) - except os.error: + except OSError: pass def checkopen(self): @@ -347,7 +347,7 @@ def sigexit(self, sig, frame): def sigchild(self, sig, frame): try: pid, sts = os.waitpid(-1, os.WNOHANG) - except os.error: + except OSError: return if pid: if pid == self.proc.pid: @@ -396,7 +396,7 @@ def daemonize(self): if self.options.directory: try: os.chdir(self.options.directory) - except os.error as err: + except OSError as err: self.logger.warn("can't chdir into %r: %s" % (self.options.directory, err)) else: @@ -767,6 +767,7 @@ def get_path(): class _ChildExits(dict): """map ``pid`` to exit status or ``None``.""" + def fetch(self, pid): """fetch and reset status for *pid*.""" st = self.get(pid) diff --git a/tox.ini b/tox.ini index b20999b..11fe600 100644 --- a/tox.ini +++ b/tox.ini @@ -60,7 +60,7 @@ basepython = python3 allowlist_externals = mkdir setenv = - COVERAGE_PROCESS_START={toxinidir}/.coveragerc + COVERAGE_PROCESS_START={toxinidir}/pyproject.toml COVERAGE_HOME={toxinidir}/ deps = coverage[toml] From d6fed3a2eed881d6381c5c359cebab612f8776e6 Mon Sep 17 00:00:00 2001 From: Michael Howitz Date: Fri, 17 Jan 2025 09:10:21 +0100 Subject: [PATCH 5/7] Configuring for buildout-recipe --- .meta.toml | 4 ++-- pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.meta.toml b/.meta.toml index e62fc1c..0191a99 100644 --- a/.meta.toml +++ b/.meta.toml @@ -18,12 +18,12 @@ fail-under = 79 [coverage-run] additional-config = [ "data_file = $COVERAGE_HOME.coverage", - "omit = */__main__.py", + "omit = ['*/__main__.py']", ] [tox] coverage-setenv = [ - "COVERAGE_PROCESS_START={toxinidir}/.coveragerc", + "COVERAGE_PROCESS_START={toxinidir}/pyproject.toml", "COVERAGE_HOME={toxinidir}/", ] use-flake8 = true diff --git a/pyproject.toml b/pyproject.toml index b168376..9307921 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ branch = true source = ["zdaemon"] parallel = true data_file = "$COVERAGE_HOME.coverage" -omit = ["*/__main__.py"] +omit = "['*/__main__.py']" [tool.coverage.report] fail_under = 79 From 511727018516992d6fa66359a0accb336f089bef Mon Sep 17 00:00:00 2001 From: Michael Howitz Date: Fri, 17 Jan 2025 09:34:36 +0100 Subject: [PATCH 6/7] Configuring for buildout-recipe --- .meta.toml | 6 ++++-- pyproject.toml | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.meta.toml b/.meta.toml index 0191a99..a0f7887 100644 --- a/.meta.toml +++ b/.meta.toml @@ -18,8 +18,10 @@ fail-under = 79 [coverage-run] additional-config = [ "data_file = $COVERAGE_HOME.coverage", - "omit = ['*/__main__.py']", - ] +] +omit = [ + "*/__main__.py", +] [tox] coverage-setenv = [ diff --git a/pyproject.toml b/pyproject.toml index 9307921..6d7b8b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,9 @@ branch = true source = ["zdaemon"] parallel = true data_file = "$COVERAGE_HOME.coverage" -omit = "['*/__main__.py']" +omit = [ + "*/__main__.py", +] [tool.coverage.report] fail_under = 79 From faf2c5614b6dddd7850bbcd2e2dc8066bf66e5bf Mon Sep 17 00:00:00 2001 From: Michael Howitz Date: Fri, 17 Jan 2025 09:41:13 +0100 Subject: [PATCH 7/7] Trigger CI --- CHANGES.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 0d2c49d..8aa6acf 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,7 +5,6 @@ Change log 5.2 (unreleased) ================ - - Add support for Python 3.13. - Drop support for Python 3.7, 3.8.