diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000..ebf8872 --- /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@v5 + - uses: actions/setup-python@v6 + 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 15d6aaf..3e81aa8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,52 +12,51 @@ on: jobs: build: + permissions: + contents: read + pull-requests: write strategy: # We want to see all failures: fail-fast: false matrix: os: - - ["ubuntu", "ubuntu-20.04"] + - ["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.9", "docs"] - - ["3.9", "coverage"] + - ["3.11", "release-check"] + - ["3.9", "py39"] + - ["3.10", "py310"] + - ["3.11", "py311"] + - ["3.12", "py312"] + - ["3.13", "py313"] + - ["3.11", "docs"] + - ["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@v3 - - name: Set up Python - uses: actions/setup-python@v4 + - uses: actions/checkout@v5 with: - python-version: ${{ matrix.config[0] }} - - name: Pip cache - uses: actions/cache@v3 + persist-credentials: false + - name: Install uv + caching + uses: astral-sh/setup-uv@v6 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.config[0] }} + github-token: ${{ secrets.GITHUB_TOKEN }} - name: Test - run: tox -e ${{ matrix.config[1] }} + if: ${{ !startsWith(runner.os, 'Mac') }} + run: uvx --with tox-uv tox -e ${{ matrix.config[1] }} + - name: Test (macOS) + if: ${{ startsWith(runner.os, 'Mac') }} + 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 e7dbe82..fb5360a 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 = "acd8d239" +commit-id = "96dd7c5d" [python] with-windows = false diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..c956c91 --- /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.1.0" + 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.20.0 + 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.3.0" + hooks: + - id: flake8 + additional_dependencies: + - flake8-debugger == 4.1.2 diff --git a/CHANGES.rst b/CHANGES.rst index 366def7..f8e6bc7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,12 @@ Change log 4.3 (unreleased) ---------------- +- Drop support for Python 3.7 and 3.8. + +- Add support for Python 3.13. + +- Update package management files from latest ``zope.meta`` templates. + 4.2 (2023-12-28) ---------------- diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index da489e0..bbf4194 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,7 @@ + # Contributing to dataflake projects The projects under the dataflake GitHub organization are open source and diff --git a/MANIFEST.in b/MANIFEST.in index 26a79ff..3c65fe8 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 docs *.py recursive-include docs *.rst diff --git a/buildout.cfg b/buildout.cfg index 56b6639..adc42a8 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -1,6 +1,6 @@ [buildout] extends = - https://zopefoundation.github.io/Zope/releases/master/versions-prod.cfg + https://zopefoundation.github.io/Zope/releases/master/versions.cfg develop = . parts = test @@ -8,5 +8,4 @@ parts = [test] recipe = zc.recipe.testrunner -defaults = ['-cv'] eggs = Products.mcdutils diff --git a/docs/conf.py b/docs/conf.py index 45ea7a5..e1deb63 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -5,13 +5,12 @@ import datetime import os -import pkginfo import sys +from importlib.metadata import distribution -parent = os.path.dirname(os.path.dirname(__file__)) -parent_dir = os.path.abspath(parent) -pkg_info = pkginfo.Develop(parent_dir) -pkg_version = pkg_info.version or '' + +sys.path.append(os.path.abspath('../src')) +rqmt = distribution('Products.mcdutils') year = datetime.datetime.now().year # -- Project information ----------------------------------------------------- @@ -22,9 +21,9 @@ author = 'Tres Seaver and contributors' # The short X.Y version. -version = pkg_version.replace('.dev0', '') +version = '%s.%s' % tuple(map(int, rqmt.version.split('.')[:2])) # The full version, including alpha/beta/rc tags. -release = pkg_version +release = rqmt.version # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration @@ -37,5 +36,5 @@ # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output -html_theme = 'sphinx_rtd_theme' +html_theme = 'furo' html_static_path = ['_static'] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..2742094 --- /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 >= 78.1.1,< 81", + "wheel", +] +build-backend = "setuptools.build_meta" + +[tool.coverage.run] +branch = true +source = ["Products.mcdutils"] + +[tool.coverage.report] +fail_under = 84 +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.cfg b/setup.cfg index 587ae1b..caa2d19 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,5 @@ # Generated from: # https://github.com/zopefoundation/meta/tree/master/config/zope-product -[bdist_wheel] -universal = 0 [flake8] doctests = 1 diff --git a/setup.py b/setup.py index 4dbf34d..1285afd 100644 --- a/setup.py +++ b/setup.py @@ -25,23 +25,23 @@ def _read(name): description=('A Zope product with memcached-backed ZCache and ' 'Zope session implementations.'), long_description=_read('README.rst') + '\n\n' + _read('CHANGES.rst'), + long_description_content_type='text/x-rst', classifiers=[ - 'Development Status :: 6 - Mature', - 'Environment :: Web Environment', - 'Framework :: Zope', - 'Framework :: Zope :: 5', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: Zope Public License', - 'Operating System :: OS Independent', - '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', - 'Topic :: Internet :: WWW/HTTP :: Session', + 'Development Status :: 6 - Mature', + 'Environment :: Web Environment', + 'Framework :: Zope', + 'Framework :: Zope :: 5', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Zope Public License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + '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', + 'Topic :: Internet :: WWW/HTTP :: Session', ], keywords='zope session memcache memcached Products', author='Tres Seaver and contributors', @@ -50,27 +50,26 @@ def _read(name): maintainer_email='jens@dataflake.org', url='https://mcdutils.readthedocs.io', project_urls={ - 'Documentation': 'https://mcdutils.readthedocs.io', - 'Issue Tracker': ('https://github.com/dataflake/Products.mcdutils' - '/issues'), - 'Sources': 'https://github.com/dataflake/Products.mcdutils', + 'Documentation': 'https://mcdutils.readthedocs.io', + 'Issue Tracker': ('https://github.com/dataflake/Products.mcdutils' + '/issues'), + 'Sources': 'https://github.com/dataflake/Products.mcdutils', }, - license='ZPL 2.1', + license='ZPL-2.1', packages=find_packages('src'), package_dir={'': 'src'}, include_package_data=True, namespace_packages=['Products'], zip_safe=False, - python_requires='>=3.7', + python_requires='>=3.9', install_requires=[ - 'setuptools', - 'python-memcached', - 'Zope >= 5', - ], + 'setuptools', + 'python-memcached', + 'Zope >= 5', + ], extras_require={ - 'docs': ['sphinx', - 'repoze.sphinx.autointerface', - 'sphinx-rtd-theme', - 'pkginfo'], - }, + 'docs': ['sphinx', + 'repoze.sphinx.autointerface', + 'furo'], + }, ) diff --git a/src/Products/mcdutils/ftests/test_proxy.py b/src/Products/mcdutils/ftests/test_proxy.py index 5a6cb48..16d0637 100644 --- a/src/Products/mcdutils/ftests/test_proxy.py +++ b/src/Products/mcdutils/ftests/test_proxy.py @@ -30,7 +30,7 @@ def test_writing_to_mapping_no_memcache(self): sdc = self._makeOne() mapping = sdc.new_or_existing('foobar') - self.assertTrue(isinstance(mapping, MemCacheMapping)) + self.assertIsInstance(mapping, MemCacheMapping) self.assertFalse(mapping._p_changed) self.assertFalse(mapping._p_joined) mapping['abc'] = 1345 @@ -45,7 +45,7 @@ def test_writing_to_mapping_with_memcache(self): sdc = self._makeOne() sdc._get_proxy().servers = ('localhost:11211',) mapping = sdc.new_or_existing('foobar') - self.assertTrue(isinstance(mapping, MemCacheMapping)) + self.assertIsInstance(mapping, MemCacheMapping) self.assertFalse(mapping._p_changed) self.assertTrue(mapping._p_joined) mapping['abc'] = 1345 diff --git a/src/Products/mcdutils/tests/test_sessiondata.py b/src/Products/mcdutils/tests/test_sessiondata.py index 88cf9b8..efd43fa 100644 --- a/src/Products/mcdutils/tests/test_sessiondata.py +++ b/src/Products/mcdutils/tests/test_sessiondata.py @@ -75,9 +75,9 @@ def test_new_or_existing_returns_txn_aware_mapping(self): from transaction.interfaces import IDataManager sdc = self._makeOne('mcsdc') created = sdc.new_or_existing('foobar') - self.assertTrue(isinstance(created, PersistentMapping)) + self.assertIsInstance(created, PersistentMapping) jar = created._p_jar - self.assertFalse(jar is None) + self.assertIsNotNone(jar) self.assertTrue(IDataManager.providedBy(jar)) def test_has_key_after_new_or_existing_returns_True(self): @@ -88,4 +88,4 @@ def test_has_key_after_new_or_existing_returns_True(self): def test_get_after_new_or_existing_returns_same(self): sdc = self._makeOne('mcsdc') created = sdc.new_or_existing('foobar') - self.assertTrue(sdc.get('foobar') is created) + self.assertIs(sdc.get('foobar'), created) diff --git a/src/Products/mcdutils/tests/test_zcache.py b/src/Products/mcdutils/tests/test_zcache.py index 0c70bd9..24d9892 100644 --- a/src/Products/mcdutils/tests/test_zcache.py +++ b/src/Products/mcdutils/tests/test_zcache.py @@ -129,7 +129,7 @@ def test_ZCache_set_simple(self): self.assertEqual(len(_cached), 2) key = '%s|||' % _DUMMY_PATH_STR - self.assertTrue(key in _cached[_DUMMY_PATH_STR]) + self.assertIn(key, _cached[_DUMMY_PATH_STR]) self.assertEqual(_cached[key], 'XYZZY') def test_ZCache_set_with_view_name(self): @@ -142,7 +142,7 @@ def test_ZCache_set_with_view_name(self): self.assertEqual(len(_cached), 2) key = '%s|v||' % _DUMMY_PATH_STR - self.assertTrue(key in _cached[_DUMMY_PATH_STR]) + self.assertIn(key, _cached[_DUMMY_PATH_STR]) self.assertEqual(_cached[key], 'XYZZY') def test_ZCache_set_replacing(self): @@ -160,10 +160,10 @@ def test_ZCache_set_replacing(self): self.assertEqual(len(_cached), 3) - self.assertTrue(key1 in _cached[_DUMMY_PATH_STR]) + self.assertIn(key1, _cached[_DUMMY_PATH_STR]) self.assertEqual(_cached[key1], 'GHIJKL') - self.assertTrue(key2 in _cached[_DUMMY_PATH_STR]) + self.assertIn(key2, _cached[_DUMMY_PATH_STR]) self.assertEqual(_cached[key2], 'XYZZY') diff --git a/tox.ini b/tox.ini index ea1963e..1444ee8 100644 --- a/tox.ini +++ b/tox.ini @@ -5,32 +5,41 @@ minversion = 3.18 envlist = release-check lint - py37 - py38 py39 py310 py311 py312 + py313 docs coverage [testenv] skip_install = true deps = - zc.buildout >= 3.0.1 - wheel > 0.37 + setuptools >= 78.1.1,< 81 + 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 >= 78.1.1,< 81 + wheel twine build check-manifest @@ -39,34 +48,19 @@ deps = commands_pre = commands = check-manifest - check-python-versions + check-python-versions --only setup.py,tox.ini,.github/workflows/tests.yml python -m build --sdist --no-isolation 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:docs] basepython = python3 @@ -85,27 +79,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=84 - -[coverage:run] -branch = True -source = Products.mcdutils - -[coverage:report] -precision = 2 -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