diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000..325ae0e --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,33 @@ +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/c-code +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.0.2 + if: always() + with: + msg: Apply pre-commit code formatting diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b93c47a..790d468 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,7 +9,7 @@ # Original comment follows. ### ### -# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# This workflow will install Python dependencies, run tests with a variety of Python versions # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions ### @@ -81,7 +81,7 @@ jobs: # other people to test/debug), the strategy is to divide the process # into several different jobs. The first builds and saves the binary # wheels. It has dependent jobs that download and install the wheel - # to run tests, build docs, and perform linting. Building the + # to run tests, and build docs. Building the # manylinux wheels is an independent set of jobs. # # This division is time-saving for projects that take awhile to @@ -156,12 +156,12 @@ jobs: if: matrix.python-version == '3.13' run: | pip install -U pip - pip install -U "setuptools<69" wheel twine + pip install -U "setuptools<74" wheel twine - name: Install Build Dependencies if: matrix.python-version != '3.13' run: | pip install -U pip - pip install -U "setuptools<69" wheel twine + pip install -U "setuptools<74" wheel twine - name: Build Acquisition (macOS x86_64) if: > @@ -327,7 +327,7 @@ jobs: - name: Install Acquisition ${{ matrix.python-version }} if: matrix.python-version == '3.13' run: | - pip install -U wheel "setuptools<69" + pip install -U wheel "setuptools<74" # coverage might have a wheel on PyPI for a future python version which is # not ABI compatible with the current one, so build it from sdist: pip install -U --no-binary :all: coverage @@ -337,18 +337,18 @@ jobs: unzip -n dist/Acquisition-*whl -d src # Use "--pre" here because dependencies with support for this future # Python release may only be available as pre-releases - pip install --pre -U -e .[test] + pip install --pre -e .[test] - name: Install Acquisition if: matrix.python-version != '3.13' run: | - pip install -U wheel "setuptools<69" + pip install -U wheel "setuptools<74" pip install -U coverage pip install -U 'cffi; platform_python_implementation == "CPython"' # Unzip into src/ so that testrunner can find the .so files # when we ask it to load tests from that directory. This # might also save some build time? unzip -n dist/Acquisition-*whl -d src - pip install -U -e .[test] + pip install -e .[test] - name: Run tests with C extensions if: ${{ !startsWith(matrix.python-version, 'pypy') }} run: | @@ -379,80 +379,13 @@ jobs: with: parallel-finished: true - lint: - needs: build-package - runs-on: ${{ matrix.os }} - strategy: - matrix: - python-version: ["3.9"] - os: [ubuntu-latest] - - steps: - - name: checkout - uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - allow-prereleases: true - ### - # Caching. - # This actually *restores* a cache and schedules a cleanup action - # to save the cache. So it must come before the thing we want to use - # the cache. - ### - - name: Get pip cache dir (default) - id: pip-cache-default - if: ${{ !startsWith(runner.os, 'Windows') }} - run: | - echo "dir=$(pip cache dir)" >>$GITHUB_OUTPUT - - - name: Get pip cache dir (Windows) - id: pip-cache-windows - if: ${{ startsWith(runner.os, 'Windows') }} - run: | - echo "dir=$(pip cache dir)" >> $Env:GITHUB_OUTPUT - - - name: pip cache (default) - uses: actions/cache@v4 - if: ${{ !startsWith(runner.os, 'Windows') }} - with: - path: ${{ steps.pip-cache-default.outputs.dir }} - key: ${{ runner.os }}-pip-${{ matrix.python-version }} - restore-keys: | - ${{ runner.os }}-pip- - - - name: pip cache (Windows) - uses: actions/cache@v4 - if: ${{ startsWith(runner.os, 'Windows') }} - with: - path: ${{ steps.pip-cache-windows.outputs.dir }} - key: ${{ runner.os }}-pip-${{ matrix.python-version }} - restore-keys: | - ${{ runner.os }}-pip- - - - name: Download Acquisition wheel - uses: actions/download-artifact@v4 - with: - name: Acquisition-${{ runner.os }}-${{ matrix.python-version }}.whl - path: dist/ - - name: Install Acquisition - run: | - pip install -U pip - pip install -U wheel - pip install -U `ls dist/Acquisition-*`[test] - - name: Lint - run: | - pip install -U tox - tox -e lint - manylinux: runs-on: ubuntu-latest if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name # We use a regular Python matrix entry to share as much code as possible. strategy: matrix: - python-version: ["3.9"] + python-version: ["3.11"] image: [manylinux2014_x86_64, manylinux2014_i686, manylinux2014_aarch64] steps: @@ -533,6 +466,8 @@ jobs: name: manylinux_${{ matrix.image }}_wheels.zip - name: Restore pip cache permissions run: sudo chown -R $(whoami) ${{ steps.pip-cache-default.outputs.dir }} + - name: Prevent publishing wheels for unreleased Python versions + run: VER=$(echo '3.13' | tr -d .) && ls -al wheelhouse && sudo rm -f wheelhouse/*-cp${VER}*.whl && ls -al wheelhouse - name: Publish package to PyPI uses: pypa/gh-action-pypi-publish@release/v1 if: > diff --git a/.meta.toml b/.meta.toml index e39d3eb..35a69f3 100644 --- a/.meta.toml +++ b/.meta.toml @@ -2,13 +2,12 @@ # https://github.com/zopefoundation/meta/tree/master/config/c-code [meta] template = "c-code" -commit-id = "73f176ee" +commit-id = "a1e05e74" [python] with-windows = true with-pypy = true with-sphinx-doctests = false -with-appveyor = false with-future-python = true with-macos = false with-docs = false diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..b5d6386 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,28 @@ +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/c-code +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.17.0 + hooks: + - id: pyupgrade + args: [--py38-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 e17577b..7b4bedf 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 include .coveragerc recursive-include src *.py diff --git a/src/Acquisition/tests.py b/src/Acquisition/tests.py index 30838ae..fd0013e 100644 --- a/src/Acquisition/tests.py +++ b/src/Acquisition/tests.py @@ -1513,8 +1513,8 @@ def persistent_id(self, obj): pickler = Pickler(file, 1) pickler.dump(w) state = file.getvalue() - self.assertTrue(b'1234' in state) - self.assertFalse(b'class_and_oid' in state) + self.assertIn(b'1234', state) + self.assertNotIn(b'class_and_oid', state) def test_cant_persist_acquisition_wrappers_newstyle(self): try: @@ -1565,8 +1565,8 @@ def persistent_id(self, obj): pickler = Pickler(file, 1) pickler.dump(w) state = file.getvalue() - self.assertTrue(b'1234' in state) - self.assertFalse(b'class_and_oid' in state) + self.assertIn(b'1234', state) + self.assertNotIn(b'class_and_oid', state) class TestInterfaces(unittest.TestCase): @@ -1703,7 +1703,7 @@ def __del__(self, counter=counter): del a removed = gc.collect() if self.SUPPORTS_GC_THRESHOLD: - self.assertTrue(removed > 0) + self.assertGreater(removed, 0) self.assertEqual(counter[0], 1) def test_Wrapper_gc(self): @@ -1723,7 +1723,7 @@ def __del__(self, counter=counter): del a removed = gc.collect() if self.SUPPORTS_GC_THRESHOLD: - self.assertTrue(removed > 0) + self.assertGreater(removed, 0) self.assertEqual(counter[0], 1) @@ -2214,9 +2214,9 @@ class Impl2(Implicit): y = Impl2().__of__(x) x.__parent__ = y - self.assertTrue(x.__parent__.aq_base is y.aq_base) - self.assertTrue(aq_parent(x) is y) - self.assertTrue(x.__parent__.__parent__ is x) + self.assertIs(x.__parent__.aq_base, y.aq_base) + self.assertIs(aq_parent(x), y) + self.assertIs(x.__parent__.__parent__, x) self.assertEqual(x.hello, 'world') self.assertEqual(aq_acquire(x, 'hello'), 'world') @@ -2388,8 +2388,8 @@ class Impl(Implicit): del child_wrapper.__parent__ del child_wrapper.aq_parent - self.assertIs(child_wrapper.__parent__, None) - self.assertIs(child_wrapper.aq_parent, None) + self.assertIsNone(child_wrapper.__parent__) + self.assertIsNone(child_wrapper.aq_parent) self.assertFalse(hasattr(child_wrapper, 'a')) def test__cmp__is_called_on_wrapped_object(self): @@ -2768,7 +2768,7 @@ class Impl2(Implicit): x.__parent__ = y y.__parent__ = x - self.assertTrue(x.__parent__.__parent__ is x) + self.assertIs(x.__parent__.__parent__, x) self.assertEqual(aq_acquire(x, 'hello'), 'world') self.assertEqual(aq_acquire(x, 'only'), 'here') @@ -2794,11 +2794,13 @@ class Impl3(Implicit): b.__parent__ = c c.__parent__ = a - self.assertTrue(a.__parent__.__parent__ is c) - self.assertTrue( - aq_base(a.__parent__.__parent__.__parent__) is a) - self.assertTrue(b.__parent__.__parent__ is a) - self.assertTrue(c.__parent__.__parent__ is b) + self.assertIs(a.__parent__.__parent__, c) + self.assertIs( + aq_base(a.__parent__.__parent__.__parent__), + a + ) + self.assertIs(b.__parent__.__parent__, a) + self.assertIs(c.__parent__.__parent__, b) self.assertEqual(aq_acquire(a, 'hello'), 'world') self.assertEqual(aq_acquire(b, 'hello'), 'world') @@ -2864,7 +2866,7 @@ def test_unwrapped_falls_back_to_default(self): def test_w_unicode_attr_name(self): # See https://bugs.launchpad.net/acquisition/+bug/143358 found = aq_acquire(self.a.b.c, AQ_PARENT) - self.assertTrue(found.aq_self is self.a.b.aq_self) + self.assertIs(found.aq_self, self.a.b.aq_self) class TestCooperativeBase(unittest.TestCase): @@ -2928,7 +2930,7 @@ def get_flags(self): self.assertEqual(wrapped.get_flags(), wrapper.get_flags()) wrapper_dict = object.__getattribute__(wrapper, '__dict__') - self.assertFalse('_Persistent__flags' in wrapper_dict) + self.assertNotIn('_Persistent__flags', wrapper_dict) @unittest.skipIf(CAPI, 'Pure Python test.') def test_type_with_slots_reused(self): @@ -2946,7 +2948,7 @@ def get_flags(self): wrapper = Acquisition.ImplicitAcquisitionWrapper(wrapped, None) wrapper2 = Acquisition.ImplicitAcquisitionWrapper(wrapped, None) - self.assertTrue(type(wrapper) is type(wrapper2)) + self.assertIs(type(wrapper), type(wrapper2)) @unittest.skipIf(CAPI, 'Pure Python test.') def test_object_getattribute_in_rebound_method_with_dict(self): @@ -2970,7 +2972,7 @@ def get_flags(self): self.assertEqual(wrapped.get_flags(), wrapper.get_flags()) wrapper_dict = object.__getattribute__(wrapper, '__dict__') - self.assertTrue('_Persistent__flags' in wrapper_dict) + self.assertIn('_Persistent__flags', wrapper_dict) @unittest.skipIf(CAPI, 'Pure Python test.') def test_object_getattribute_in_rebound_method_with_slots_and_dict(self): @@ -3228,8 +3230,8 @@ def __contains__(self, item): base.derived = ReallyContains() - self.assertTrue(42 in base.derived) - self.assertFalse(24 in base.derived) + self.assertIn(42, base.derived) + self.assertNotIn(24, base.derived) # Iterable objects are NOT iterated # XXX: Is this a bug in the C code? Shouldn't it do @@ -3344,7 +3346,7 @@ class RichCmpNeverCalled(base_class): for name in rich_cmp_methods: getattr(operator, name)(base.derived, base.derived2) - self.assertFalse(base.derived2 == base.derived) + self.assertNotEqual(base.derived2, base.derived) self.assertEqual(base.derived, base.derived) def test_implicit_proxy_comporison(self): diff --git a/tox.ini b/tox.ini index 1fdad0c..c7a7bda 100644 --- a/tox.ini +++ b/tox.ini @@ -14,10 +14,9 @@ envlist = coverage [testenv] -usedevelop = true pip_pre = py313: true deps = - setuptools < 69 + setuptools < 74 setenv = pure: PURE_PYTHON=1 !pure-!pypy3: PURE_PYTHON=0 @@ -41,6 +40,7 @@ commands = coverage run -m zope.testrunner --test-path=src {posargs:-vc} coverage html -i coverage report -i -m --fail-under=96 + [testenv:release-check] description = ensure that the distribution is ready to release basepython = python3 @@ -59,20 +59,11 @@ 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