diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml
new file mode 100644
index 0000000..19e7eb4
--- /dev/null
+++ b/.github/workflows/pre-commit.yml
@@ -0,0 +1,36 @@
+# Generated from:
+# https://github.com/zopefoundation/meta/tree/master/config/zope-product
+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:
+ permissions:
+ contents: read
+ pull-requests: write
+ 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@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd #v3.0.1
+ with:
+ extra_args: --all-files --show-diff-on-failure
+ env:
+ PRE_COMMIT_COLOR: always
+ - uses: pre-commit-ci/lite-action@5d6cc0eb514c891a40562a58a8e71576c5c7fb43 #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 2e30321..943e022 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -12,6 +12,9 @@ on:
jobs:
build:
+ permissions:
+ contents: read
+ pull-requests: write
strategy:
# We want to see all failures:
fail-fast: false
@@ -20,46 +23,39 @@ jobs:
- ["ubuntu", "ubuntu-latest"]
config:
# [Python version, tox env]
- - ["3.11", "release-check"]
- - ["3.11", "lint"]
- - ["3.8", "py38"]
- - ["3.9", "py39"]
- - ["3.10", "py310"]
- - ["3.11", "py311"]
- - ["3.12", "py312"]
- - ["3.11", "coverage"]
+ - ["3.11", "release-check"]
+ - ["3.9", "py39"]
+ - ["3.10", "py310"]
+ - ["3.11", "py311"]
+ - ["3.12", "py312"]
+ - ["3.13", "py313"]
+ - ["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
name: ${{ matrix.config[1] }}
steps:
- uses: actions/checkout@v4
- - name: Set up Python
- uses: actions/setup-python@v5
with:
- python-version: ${{ matrix.config[0] }}
- - name: Pip cache
- uses: actions/cache@v4
+ persist-credentials: false
+ - name: Install uv + caching
+ uses: astral-sh/setup-uv@v5
with:
- path: ~/.cache/pip
- key: ${{ runner.os }}-pip-${{ matrix.config[0] }}-${{ hashFiles('setup.*', 'tox.ini') }}
- restore-keys: |
- ${{ runner.os }}-pip-${{ matrix.config[0] }}-
- ${{ runner.os }}-pip-
- - name: Install dependencies
- run: |
- python -m pip install --upgrade pip
- pip install tox
+ enable-cache: true
+ cache-dependency-glob: |
+ setup.*
+ tox.ini
+ python-version: ${{ matrix.matrix.config[0] }}
+ github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Test
if: ${{ !startsWith(runner.os, 'Mac') }}
- run: tox -e ${{ matrix.config[1] }}
+ run: uvx --with tox-uv tox -e ${{ matrix.config[1] }}
- name: Test (macOS)
if: ${{ startsWith(runner.os, 'Mac') }}
- run: tox -e ${{ matrix.config[1] }}-universal2
+ run: uvx --with tox-uv tox -e ${{ matrix.config[1] }}-universal2
- name: Coverage
if: matrix.config[1] == 'coverage'
run: |
- pip install coveralls
- coveralls --service=github
+ uvx coveralls --service=github
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.meta.toml b/.meta.toml
index 82a8245..88f4442 100644
--- a/.meta.toml
+++ b/.meta.toml
@@ -2,7 +2,7 @@
# https://github.com/zopefoundation/meta/tree/master/config/zope-product
[meta]
template = "zope-product"
-commit-id = "b1221c3c"
+commit-id = "a0de4e93"
[python]
with-pypy = false
@@ -16,7 +16,7 @@ with-docs = false
use-flake8 = true
[coverage]
-fail-under = 70
+fail-under = 73
[coverage-run]
source = "src"
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..7f83638
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,28 @@
+# Generated from:
+# https://github.com/zopefoundation/meta/tree/master/config/zope-product
+minimum_pre_commit_version: '3.6'
+repos:
+ - repo: https://github.com/pycqa/isort
+ rev: "6.0.1"
+ hooks:
+ - id: isort
+ - repo: https://github.com/hhatto/autopep8
+ rev: "v2.3.2"
+ 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.2"
+ hooks:
+ - id: flake8
+ additional_dependencies:
+ - flake8-debugger == 4.1.2
diff --git a/CHANGES.rst b/CHANGES.rst
index 5148e15..f21cfb2 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -5,6 +5,10 @@ Changelog
----------------
+- Add support for Python 3.13.
+
+- Drop support for Python 3.8.
+
- Drop support for Python 3.7.
4.1 (2024-01-04)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b5c34d4..ce446a6 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,7 +1,7 @@
+-->
# Contributing to zopefoundation projects
The projects under the zopefoundation GitHub organization are open source and
diff --git a/MANIFEST.in b/MANIFEST.in
index c69cebb..e22e408 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -5,6 +5,7 @@ include *.rst
include *.txt
include buildout.cfg
include tox.ini
+include .pre-commit-config.yaml
recursive-include src *.py
recursive-include src *.dtml
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..bf33e5d
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,32 @@
+# Generated from:
+# https://github.com/zopefoundation/meta/tree/master/config/zope-product
+
+[build-system]
+requires = [
+ "setuptools == 75.8.2",
+ "wheel",
+]
+build-backend = "setuptools.build_meta"
+
+[tool.coverage.run]
+branch = true
+source = ["src"]
+
+[tool.coverage.report]
+fail_under = 73
+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"
diff --git a/setup.py b/setup.py
index 5e5723c..a5650b4 100644
--- a/setup.py
+++ b/setup.py
@@ -31,7 +31,7 @@
'/Products.ZSQLMethods/issues'),
'Sources': 'https://github.com/zopefoundation/Products.ZSQLMethods',
},
- license='ZPL 2.1',
+ license='ZPL-2.1',
description='SQL method support for Zope.',
author='Zope Foundation and Contributors',
author_email='zope-dev@zope.dev',
@@ -47,16 +47,16 @@
'License :: OSI Approved :: Zope Public License',
'Operating System :: OS Independent',
'Programming Language :: Python :: 3',
- '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',
'Topic :: Database',
'Topic :: Database :: Front-Ends',
],
- python_requires='>=3.8',
+ python_requires='>=3.9',
install_requires=[
'setuptools',
'Zope >= 4.2.1',
diff --git a/src/Shared/DC/ZRDB/Connection.py b/src/Shared/DC/ZRDB/Connection.py
index b942ef0..c066676 100644
--- a/src/Shared/DC/ZRDB/Connection.py
+++ b/src/Shared/DC/ZRDB/Connection.py
@@ -199,8 +199,8 @@ def connect(self, s):
except Exception:
t, v, tb = sys.exc_info()
raise BadRequest(
- 'Error connecting to DB.
\n'
- '\n' % (t, v)).with_traceback(tb)
+ 'Error connecting to DB.
\n'
+ '\n' % (t, v)).with_traceback(tb)
finally:
tb = None
self._v_connected = DateTime()
diff --git a/src/Shared/DC/ZRDB/tests/testArgs.py b/src/Shared/DC/ZRDB/tests/testArgs.py
index 9602d17..debfcc7 100644
--- a/src/Shared/DC/ZRDB/tests/testArgs.py
+++ b/src/Shared/DC/ZRDB/tests/testArgs.py
@@ -13,8 +13,6 @@
import pickle
from unittest import TestCase
-from unittest import TestSuite
-from unittest import makeSuite
class TestArgs(TestCase):
@@ -56,7 +54,7 @@ def test_dict_getters(self):
args = self._makeOne({'arg1': {'type': 'string', 'default': 'n/a'}},
['arg1'])
self.assertIn('arg1', args)
- self.assertTrue('arg1' in args)
+ self.assertIn('arg1', args)
self.assertDictEqual(args['arg1'],
{'default': 'n/a', 'type': 'string'})
self.assertEqual(args.keys(), ['arg1'])
@@ -81,7 +79,3 @@ def test__delitem__(self):
del args['arg2']
self.assertEqual(len(args), 0)
-
-
-def test_suite():
- return TestSuite((makeSuite(TestArgs),))
diff --git a/src/Shared/DC/ZRDB/tests/testResult.py b/src/Shared/DC/ZRDB/tests/testResult.py
index 743dc42..52ac850 100644
--- a/src/Shared/DC/ZRDB/tests/testResult.py
+++ b/src/Shared/DC/ZRDB/tests/testResult.py
@@ -1,7 +1,5 @@
from io import StringIO
from unittest import TestCase
-from unittest import TestSuite
-from unittest import makeSuite
from ExtensionClass import Base
@@ -40,7 +38,7 @@ def test_results(self):
self.assertEqual(row.bar, 4)
self.assertEqual(row.FOO, 3)
self.assertEqual(row.BAR, 4)
- self.assertTrue(isinstance(row, Brain))
+ self.assertIsInstance(row, Brain)
def test_rdb_file(self):
infile = StringIO("""\
@@ -67,8 +65,4 @@ def test_rdb_file(self):
self.assertEqual(row.bar, 4)
self.assertEqual(row.FOO, 3)
self.assertEqual(row.BAR, 4)
- self.assertTrue(isinstance(row, Brain))
-
-
-def test_suite():
- return TestSuite((makeSuite(TestResults),))
+ self.assertIsInstance(row, Brain)
diff --git a/src/Shared/DC/ZRDB/tests/testTHUNK.py b/src/Shared/DC/ZRDB/tests/testTHUNK.py
index 79fee5e..69b8530 100644
--- a/src/Shared/DC/ZRDB/tests/testTHUNK.py
+++ b/src/Shared/DC/ZRDB/tests/testTHUNK.py
@@ -12,8 +12,6 @@
##############################################################################
from unittest import TestCase
-from unittest import TestSuite
-from unittest import makeSuite
class TestTM(TestCase):
@@ -55,7 +53,3 @@ def test_sortKey(self):
tm.setSortKey([])
self.assertEqual(tm.sortKey(), '[]')
-
-
-def test_suite():
- return TestSuite((makeSuite(TestTM),))
diff --git a/src/Shared/DC/ZRDB/tests/testTM.py b/src/Shared/DC/ZRDB/tests/testTM.py
index 483cd54..de77a06 100644
--- a/src/Shared/DC/ZRDB/tests/testTM.py
+++ b/src/Shared/DC/ZRDB/tests/testTM.py
@@ -12,8 +12,6 @@
##############################################################################
from unittest import TestCase
-from unittest import TestSuite
-from unittest import makeSuite
class TestTM(TestCase):
@@ -55,7 +53,3 @@ def test_sortKey(self):
tm.setSortKey([])
self.assertEqual(tm.sortKey(), '[]')
-
-
-def test_suite():
- return TestSuite((makeSuite(TestTM),))
diff --git a/src/Shared/DC/ZRDB/tests/test_caching.py b/src/Shared/DC/ZRDB/tests/test_caching.py
index 16896e8..cb758d3 100644
--- a/src/Shared/DC/ZRDB/tests/test_caching.py
+++ b/src/Shared/DC/ZRDB/tests/test_caching.py
@@ -1,8 +1,6 @@
from pprint import pprint
from time import time
from unittest import TestCase
-from unittest import TestSuite
-from unittest import makeSuite
class DummyDB:
@@ -357,11 +355,3 @@ def test_cached_result_not_called_for_no_caching(self):
self.da.max_cache_ = 1
# check that we get an exception
self.assertRaises(TypeError, self.da)
-
-
-def test_suite():
- suite = TestSuite()
- suite.addTest(makeSuite(TestCaching))
- suite.addTest(makeSuite(TestCacheKeys))
- suite.addTest(makeSuite(TestFullChain))
- return suite
diff --git a/src/Shared/DC/ZRDB/tests/test_results.py b/src/Shared/DC/ZRDB/tests/test_results.py
index b7d3061..ecff83e 100644
--- a/src/Shared/DC/ZRDB/tests/test_results.py
+++ b/src/Shared/DC/ZRDB/tests/test_results.py
@@ -73,12 +73,12 @@ def test_getattr_and_aliases(self):
def test_suppliedbrain(self):
ob = self._makeOne((self.columns, self.data), brains=Brain)
row = ob[0]
- self.assertTrue(isinstance(row, Brain))
+ self.assertIsInstance(row, Brain)
def test_suppliedparent(self):
ob = self._makeOne((self.columns, self.data), parent=Parent)
row = ob[0]
- self.assertTrue(aq_parent(row) is Parent)
+ self.assertIs(aq_parent(row), Parent)
def test_tuples(self):
ob = self._makeOne((self.columns, self.data))
@@ -146,7 +146,7 @@ def test_record_of(self):
def test_record_hash(self):
ob = self._makeOne((self.columns, self.data))
row = ob[0]
- self.assertTrue(isinstance(hash(row), int))
+ self.assertIsInstance(hash(row), int)
def test_record_len(self):
ob = self._makeOne((self.columns, self.data))
diff --git a/tox.ini b/tox.ini
index 1670a3a..f5fcda4 100644
--- a/tox.ini
+++ b/tox.ini
@@ -5,32 +5,40 @@ minversion = 3.18
envlist =
release-check
lint
- py38
py39
py310
py311
py312
+ py313
coverage
[testenv]
skip_install = true
deps =
- setuptools < 69
- zc.buildout >= 3.0.1
- wheel > 0.37
+ setuptools == 75.8.2
+ zc.buildout
+ wheel
setenv =
- py312: VIRTUALENV_PIP=23.1.2
- py312: PIP_REQUIRE_VIRTUALENV=0
commands_pre =
{envbindir}/buildout -nc {toxinidir}/buildout.cfg buildout:directory={envdir} buildout:develop={toxinidir} install test
commands =
{envbindir}/test {posargs:-cv}
+[testenv:setuptools-latest]
+basepython = python3
+deps =
+ git+https://github.com/pypa/setuptools.git\#egg=setuptools
+ zc.buildout
+ wheel
+
+
[testenv:release-check]
description = ensure that the distribution is ready to release
basepython = python3
skip_install = true
deps =
+ setuptools == 75.8.2
+ wheel
twine
build
check-manifest
@@ -44,29 +52,14 @@ commands =
twine check dist/*
[testenv:lint]
+description = This env runs all linters configured in .pre-commit-config.yaml
basepython = python3
-commands_pre =
- mkdir -p {toxinidir}/parts/flake8
-allowlist_externals =
- mkdir
-commands =
- isort --check-only --diff {toxinidir}/src {toxinidir}/setup.py
- flake8 {toxinidir}/src {toxinidir}/setup.py
+skip_install = true
deps =
- flake8
- isort
- # Useful flake8 plugins that are Python and Plone specific:
- flake8-coding
- flake8-debugger
- mccabe
-
-[testenv:isort-apply]
-basepython = python3
+ 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
@@ -76,28 +69,9 @@ allowlist_externals =
mkdir
deps =
{[testenv]deps}
- coverage
+ coverage[toml]
commands =
mkdir -p {toxinidir}/parts/htmlcov
coverage run {envbindir}/test {posargs:-cv}
coverage html
- coverage report -m --fail-under=70
-
-[coverage:run]
-branch = True
-source = src
-
-[coverage:report]
-precision = 2
-ignore_errors = True
-exclude_lines =
- pragma: no cover
- pragma: nocover
- except ImportError:
- raise NotImplementedError
- if __name__ == '__main__':
- self.fail
- raise AssertionError
-
-[coverage:html]
-directory = parts/htmlcov
+ coverage report