diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 00000000..eb4f0c64 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,33 @@ +# 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: + 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 7aaeeb6b..7865fcc6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -21,18 +21,17 @@ jobs: - ["macos", "macos-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.8", "py38"] + - ["3.9", "py39"] + - ["3.10", "py310"] + - ["3.11", "py311"] + - ["3.12", "py312"] + - ["3.13", "py313"] + - ["3.11", "coverage"] exclude: - - { os: ["macos", "macos-latest"], config: ["3.11", "release-check"] } - - { os: ["macos", "macos-latest"], config: ["3.11", "lint"] } - - { os: ["macos", "macos-latest"], config: ["3.11", "coverage"] } + - { os: ["macos", "macos-latest"], config: ["3.11", "release-check"] } + - { os: ["macos", "macos-latest"], config: ["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 @@ -43,6 +42,7 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.config[0] }} + allow-prereleases: true - name: Pip cache uses: actions/cache@v4 with: diff --git a/.meta.toml b/.meta.toml index 220b6118..07702e90 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 = "c412f00f" +commit-id = "baf6089f" [python] with-pypy = false diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..8d0156c9 --- /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: "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/CHANGES.rst b/CHANGES.rst index 79329fe2..a89880a0 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,8 @@ Changelog 7.2 (unreleased) ---------------- +- Add support for Python 3.13. + - Drop support for Python 3.7. diff --git a/MANIFEST.in b/MANIFEST.in index ba206149..b029e347 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/buildout.cfg b/buildout.cfg index 00e7510c..a4d2cbda 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -8,7 +8,7 @@ parts = [versions] Products.ZCatalog = -RestrictedPython = >= 5.1 + [interpreter] recipe = zc.recipe.egg diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..3687d376 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,21 @@ +# +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/zope-product + +[build-system] +requires = ["setuptools < 74"] +build-backend = "setuptools.build_meta" + +[tool.coverage.run] +branch = true +source = ["Products.ZCatalog"] + +[tool.coverage.report] +fail_under = 85 +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 1eaa4dc9..f50d321b 100644 --- a/setup.py +++ b/setup.py @@ -49,6 +49,7 @@ "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 :: Internet :: WWW/HTTP :: Indexing/Search", ], diff --git a/src/Products/PluginIndexes/BooleanIndex/tests.py b/src/Products/PluginIndexes/BooleanIndex/tests.py index 3ac8a16d..49d12b7a 100644 --- a/src/Products/PluginIndexes/BooleanIndex/tests.py +++ b/src/Products/PluginIndexes/BooleanIndex/tests.py @@ -36,22 +36,22 @@ def test_index_true(self): index = self._makeOne() obj = Dummy(1, True) index._index_object(obj.id, obj, attr='truth') - self.assertTrue(1 in index._unindex) - self.assertFalse(1 in index._index) + self.assertIn(1, index._unindex) + self.assertNotIn(1, index._index) def test_index_false(self): index = self._makeOne() obj = Dummy(1, False) index._index_object(obj.id, obj, attr='truth') - self.assertTrue(1 in index._unindex) - self.assertFalse(1 in index._index) + self.assertIn(1, index._unindex) + self.assertNotIn(1, index._index) def test_index_missing_attribute(self): index = self._makeOne() obj = Dummy(1, True) index._index_object(obj.id, obj, attr='missing') - self.assertFalse(1 in index._unindex) - self.assertFalse(1 in index._index) + self.assertNotIn(1, index._unindex) + self.assertNotIn(1, index._index) def test_search_true(self): index = self._makeOne() diff --git a/src/Products/PluginIndexes/DateIndex/tests.py b/src/Products/PluginIndexes/DateIndex/tests.py index c09b0f22..3419cf73 100644 --- a/src/Products/PluginIndexes/DateIndex/tests.py +++ b/src/Products/PluginIndexes/DateIndex/tests.py @@ -161,7 +161,7 @@ def checkApply(): self.assertEqual(len(result), len(expectedValues), f'{req}: {result} | {expectedValues}') for k, v in expectedValues: - self.assertTrue(k in result) + self.assertIn(k, result) cache = index.getRequestCache() cache.clear() @@ -221,16 +221,16 @@ def test_empty(self): self.assertEqual(len(index), 0) self.assertEqual(len(index.referencedObjects()), 0) - self.assertTrue(index.getEntryForObject(1234) is None) + self.assertIsNone(index.getEntryForObject(1234)) marker = [] - self.assertTrue(index.getEntryForObject(1234, marker) is marker) + self.assertIs(index.getEntryForObject(1234, marker), marker) index.unindex_object(1234) # shouldn't throw self.assertTrue(index.hasUniqueValuesFor('date')) self.assertFalse(index.hasUniqueValuesFor('foo')) self.assertEqual(len(list(index.uniqueValues('date'))), 0) - self.assertTrue(index._apply_index({'zed': 12345}) is None) + self.assertIsNone(index._apply_index({'zed': 12345})) self._checkApply(index, {'date': DateTime(0)}, []) @@ -260,9 +260,9 @@ def test_retrieval(self): # One empty self.assertEqual(len(index.referencedObjects()), len(values) - 1) - self.assertTrue(index.getEntryForObject(1234) is None) + self.assertIsNone(index.getEntryForObject(1234)) marker = [] - self.assertTrue(index.getEntryForObject(1234, marker) is marker) + self.assertIs(index.getEntryForObject(1234, marker), marker) index.unindex_object(1234) # shouldn't throw for k, v in values: @@ -272,7 +272,7 @@ def test_retrieval(self): self.assertEqual( len(list(index.uniqueValues('date'))), len(values) - 2) - self.assertTrue(index._apply_index({'bar': 123}) is None) + self.assertIsNone(index._apply_index({'bar': 123})) self._checkApply(index, {'date': DateTime(0)}, values[1:2]) @@ -334,7 +334,7 @@ def test_removal(self): self._checkApply(index, {'date': 1072742900}, [values[7]]) index.index_object(7, None) - self.assertFalse(7 in index.documentToKeyMap().keys()) + self.assertNotIn(7, index.documentToKeyMap().keys()) def test_getCounter(self): from DateTime import DateTime diff --git a/src/Products/PluginIndexes/DateRangeIndex/tests.py b/src/Products/PluginIndexes/DateRangeIndex/tests.py index 9f4ccd9e..29cf3c3f 100644 --- a/src/Products/PluginIndexes/DateRangeIndex/tests.py +++ b/src/Products/PluginIndexes/DateRangeIndex/tests.py @@ -140,12 +140,12 @@ def test_interfaces(self): def test_empty(self): empty = self._makeOne('empty') - self.assertTrue(empty.getEntryForObject(1234) is None) + self.assertIsNone(empty.getEntryForObject(1234)) empty.unindex_object(1234) # shouldn't throw self.assertFalse(list(empty.uniqueValues('foo'))) self.assertFalse(list(empty.uniqueValues('foo', withLengths=True))) - self.assertTrue(empty._apply_index({'zed': 12345}) is None) + self.assertIsNone(empty._apply_index({'zed': 12345})) self._checkApply(empty, {'empty': 12345}, []) @@ -193,14 +193,14 @@ def test_floor_date(self): floor = index.floor_value - 1 bad = Dummy('bad', floor, None) index.index_object(0, bad) - self.assertTrue(0 in index._always.keys()) + self.assertIn(0, index._always.keys()) def test_ceiling_date(self): index = self._makeOne('work', 'start', 'stop') ceiling = index.ceiling_value + 1 bad = Dummy('bad', None, ceiling) index.index_object(1, bad) - self.assertTrue(1 in index._always.keys()) + self.assertIn(1, index._always.keys()) def test_datetime(self): from Products.PluginIndexes.DateIndex.tests import _getEastern diff --git a/src/Products/PluginIndexes/FieldIndex/tests.py b/src/Products/PluginIndexes/FieldIndex/tests.py index 4919fe45..e9620515 100644 --- a/src/Products/PluginIndexes/FieldIndex/tests.py +++ b/src/Products/PluginIndexes/FieldIndex/tests.py @@ -116,7 +116,7 @@ def checkApply(): assert len(result) == len(expectedValues), \ f'{list(result)} | {expectedValues}' for k, v in expectedValues: - self.assertTrue(k in result) + self.assertIn(k, result) index = self._index @@ -214,7 +214,7 @@ def testPopulated(self): def testNone(self): # Make sure None is ignored. self._index.index_object(10, Dummy(None)) - self.assertFalse(None in self._index.uniqueValues('foo')) + self.assertNotIn(None, self._index.uniqueValues('foo')) self._checkApply({'foo': None}, []) def testReindexNone(self): diff --git a/src/Products/PluginIndexes/PathIndex/tests.py b/src/Products/PluginIndexes/PathIndex/tests.py index 962256fe..e7bb6e7e 100644 --- a/src/Products/PluginIndexes/PathIndex/tests.py +++ b/src/Products/PluginIndexes/PathIndex/tests.py @@ -132,7 +132,7 @@ def test_getEntryForObject_miss_no_default(self): def test_getEntryForObject_miss_w_default(self): index = self._makeOne() default = object() - self.assertTrue(index.getEntryForObject(1234, default) is default) + self.assertIs(index.getEntryForObject(1234, default), default) def test_getEntryForObject_hit(self): index = self._makeOne() diff --git a/src/Products/PluginIndexes/UUIDIndex/tests.py b/src/Products/PluginIndexes/UUIDIndex/tests.py index 90784f46..dad41871 100644 --- a/src/Products/PluginIndexes/UUIDIndex/tests.py +++ b/src/Products/PluginIndexes/UUIDIndex/tests.py @@ -91,7 +91,7 @@ def checkApply(): self.assertEqual(used, ('foo', )) self.assertEqual(len(result), len(expectedValues)) for k, v in expectedValues: - self.assertTrue(k in result) + self.assertIn(k, result) index = self._index @@ -134,7 +134,7 @@ def test_populated(self): values = self._values self.assertEqual(len(self._index), len(values)) self.assertEqual(self._index.indexSize(), len(values)) - self.assertTrue(self._index.getEntryForObject(10) is None) + self.assertIsNone(self._index.getEntryForObject(10)) self._checkApply({'foo': 'not'}, []) self._index.unindex_object(10) # nothrow @@ -156,7 +156,7 @@ def test_populated(self): def test_none(self): # Make sure None is ignored. self._index.index_object(10, Dummy(None)) - self.assertFalse(None in self._index.uniqueValues('foo')) + self.assertNotIn(None, self._index.uniqueValues('foo')) self._checkApply({'foo': None}, []) def test_reindex(self): diff --git a/src/Products/ZCTextIndex/tests/testLexicon.py b/src/Products/ZCTextIndex/tests/testLexicon.py index 9695ed8c..c00eaa26 100644 --- a/src/Products/ZCTextIndex/tests/testLexicon.py +++ b/src/Products/ZCTextIndex/tests/testLexicon.py @@ -133,7 +133,7 @@ def process_post_glob(self, lst): wids = lexicon.sourceToWordIds('cats and dogs') wids = lexicon.termToWordIds('dogs') self.assertEqual(len(wids), 1) - self.assertTrue(wids[0] > 0) + self.assertGreater(wids[0], 0) def testMissingTermToWordIdsWithProcess_post_glob(self): """This test is for added process_post_glob""" @@ -156,7 +156,7 @@ def testOnePipelineElement(self): wids = lexicon.sourceToWordIds('cats and dogs') wids = lexicon.termToWordIds('fish') self.assertEqual(len(wids), 1) - self.assertTrue(wids[0] > 0) + self.assertGreater(wids[0], 0) def testSplitterAdaptorFold(self): from Products.ZCTextIndex.Lexicon import CaseNormalizer @@ -189,7 +189,7 @@ def testTwoElementPipeline(self): wids = lexicon.sourceToWordIds('cats and dogs') wids = lexicon.termToWordIds('hsif') self.assertEqual(len(wids), 1) - self.assertTrue(wids[0] > 0) + self.assertGreater(wids[0], 0) def testThreeElementPipeline(self): from Products.ZCTextIndex.Lexicon import Splitter @@ -202,7 +202,7 @@ def testThreeElementPipeline(self): wids = lexicon.sourceToWordIds('cats and dogs') wids = lexicon.termToWordIds('hsif') self.assertEqual(len(wids), 1) - self.assertTrue(wids[0] > 0) + self.assertGreater(wids[0], 0) def testSplitterLocaleAwareness(self): import locale diff --git a/src/Products/ZCatalog/tests/test_brains.py b/src/Products/ZCatalog/tests/test_brains.py index 8c6e653b..1635c568 100644 --- a/src/Products/ZCatalog/tests/test_brains.py +++ b/src/Products/ZCatalog/tests/test_brains.py @@ -162,8 +162,10 @@ def testGetObjectHappy(self): self.root.REQUEST = request self.assertEqual(b.getPath(), '/happy') self.assertEqual(b.getObject().REQUEST, request) - self.assertTrue(aq_base(b.getObject()) is - aq_base(self.cat.getobject(1))) + self.assertIs( + aq_base(b.getObject()), + aq_base(self.cat.getobject(1)) + ) def testGetObjectHappy_catalog_as_utility(self): request = DummyRequest() @@ -172,8 +174,10 @@ def testGetObjectHappy_catalog_as_utility(self): setRequest(request) self.assertEqual(b.getPath(), '/happy') self.assertEqual(b.getObject().REQUEST, request) - self.assertTrue(aq_base(b.getObject()) is - aq_base(self.cat.getobject(1))) + self.assertIs( + aq_base(b.getObject()), + aq_base(self.cat.getobject(1)) + ) clearRequest() def testGetObjectPropagatesConflictErrors(self): diff --git a/src/Products/ZCatalog/tests/test_catalog.py b/src/Products/ZCatalog/tests/test_catalog.py index 8b7c2a0c..4c19f31a 100644 --- a/src/Products/ZCatalog/tests/test_catalog.py +++ b/src/Products/ZCatalog/tests/test_catalog.py @@ -234,7 +234,7 @@ def _make_one(self, extra=None): def test_clear(self): catalog = self._make_one() - self.assertTrue(len(catalog) > 0) + self.assertGreater(len(catalog), 0) catalog.clear() self.assertEqual(catalog._length(), 0) self.assertEqual(len(catalog), 0) @@ -351,7 +351,7 @@ def extra(catalog): col1 = FieldIndex('col1') catalog.addIndex('col1', col1) catalog = self._make_one(extra=extra) - self.assertTrue(len(catalog) > 0) + self.assertGreater(len(catalog), 0) all_data = catalog({}) self.assertEqual(len(all_data), 0) @@ -737,7 +737,7 @@ def test_sort_on_two_reverse(self): sort_order='reverse') self.assertEqual(len(a), upper) for x in range(upper - 1): - self.assertTrue(a[x].num > a[x + 1].num) + self.assertGreater(a[x].num, a[x + 1].num) def test_sort_on_two_reverse_with_limit(self): catalog = self._make_one() @@ -768,7 +768,7 @@ def test_sort_on_two_reverse_neither(self): sort_order=('', '')) self.assertEqual(len(a), upper) for x in range(upper - 1): - self.assertTrue(a[x].num < a[x + 1].num) + self.assertLess(a[x].num, a[x + 1].num) def test_sort_on_two_reverse_first(self): catalog = self._make_one() @@ -777,7 +777,7 @@ def test_sort_on_two_reverse_first(self): sort_order=('reverse', '')) self.assertEqual(len(a), upper) for x in range(upper - 1): - self.assertTrue(a[x].num < a[x + 1].num) + self.assertLess(a[x].num, a[x + 1].num) def test_sort_on_two_reverse_second(self): catalog = self._make_one() @@ -786,7 +786,7 @@ def test_sort_on_two_reverse_second(self): sort_order=('', 'reverse')) self.assertEqual(len(a), upper) for x in range(upper - 1): - self.assertTrue(a[x].num > a[x + 1].num) + self.assertGreater(a[x].num, a[x + 1].num) def test_sort_on_two_reverse_both(self): catalog = self._make_one() @@ -795,7 +795,7 @@ def test_sort_on_two_reverse_both(self): sort_order=('reverse', 'reverse')) self.assertEqual(len(a), upper) for x in range(upper - 1): - self.assertTrue(a[x].num > a[x + 1].num) + self.assertGreater(a[x].num, a[x + 1].num) def test_sort_on_two_reverse_too_many(self): catalog = self._make_one() @@ -804,14 +804,14 @@ def test_sort_on_two_reverse_too_many(self): sort_order=('', '', 'reverse', '')) self.assertEqual(len(a), upper) for x in range(upper - 1): - self.assertTrue(a[x].num < a[x + 1].num) + self.assertLess(a[x].num, a[x + 1].num) def test_sort_on_two_small_limit(self): catalog = self._make_one() a = catalog(sort_on=('att1', 'num'), att1='att1', sort_limit=10) self.assertEqual(len(a), 10) for x in range(9): - self.assertTrue(a[x].num < a[x + 1].num) + self.assertLess(a[x].num, a[x + 1].num) def test_sort_on_two_small_limit_reverse(self): catalog = self._make_one() @@ -819,7 +819,7 @@ def test_sort_on_two_small_limit_reverse(self): sort_limit=10, sort_order='reverse') self.assertEqual(len(a), 10) for x in range(9): - self.assertTrue(a[x].num > a[x + 1].num) + self.assertGreater(a[x].num, a[x + 1].num) def test_sort_on_two_big_limit(self): catalog = self._make_one() @@ -827,7 +827,7 @@ def test_sort_on_two_big_limit(self): sort_limit=self.upper * 3) self.assertEqual(len(a), 100) for x in range(99): - self.assertTrue(a[x].num < a[x + 1].num) + self.assertLess(a[x].num, a[x + 1].num) def test_sort_on_two_big_limit_reverse(self): catalog = self._make_one() @@ -835,7 +835,7 @@ def test_sort_on_two_big_limit_reverse(self): sort_limit=self.upper * 3, sort_order='reverse') self.assertEqual(len(a), 100) for x in range(99): - self.assertTrue(a[x].num > a[x + 1].num) + self.assertGreater(a[x].num, a[x + 1].num) def test_sort_on_three(self): def extra(catalog): @@ -856,7 +856,7 @@ def extra(catalog): sort_order='reverse') self.assertEqual(len(a), self.upper) for x in range(self.upper - 1): - self.assertTrue(a[x].num > a[x + 1].num) + self.assertGreater(a[x].num, a[x + 1].num) def test_sort_on_three_reverse_last(self): def extra(catalog): @@ -867,7 +867,7 @@ def extra(catalog): sort_order=('', '', 'reverse')) self.assertEqual(len(a), self.upper) for x in range(self.upper - 1): - self.assertTrue(a[x].num > a[x + 1].num) + self.assertGreater(a[x].num, a[x + 1].num) def test_sort_on_three_small_limit(self): def extra(catalog): @@ -878,7 +878,7 @@ def extra(catalog): sort_limit=10) self.assertEqual(len(a), 10) for x in range(9): - self.assertTrue(a[x].num < a[x + 1].num) + self.assertLess(a[x].num, a[x + 1].num) def test_sort_on_three_big_limit(self): def extra(catalog): @@ -889,7 +889,7 @@ def extra(catalog): sort_limit=self.upper * 3) self.assertEqual(len(a), 100) for x in range(99): - self.assertTrue(a[x].num < a[x + 1].num) + self.assertLess(a[x].num, a[x + 1].num) class TestUnCatalog(unittest.TestCase): diff --git a/src/Products/ZCatalog/tests/test_zcatalog.py b/src/Products/ZCatalog/tests/test_zcatalog.py index 34ce16e9..b783b46d 100644 --- a/src/Products/ZCatalog/tests/test_zcatalog.py +++ b/src/Products/ZCatalog/tests/test_zcatalog.py @@ -322,8 +322,11 @@ def test_catalogView(self): # provide `ZopeVersion` catalog.ZopeVersion = lambda *arg, **kw: 4 vr = catalog.manage_catalogView() - self.assertTrue("There are no objects in the Catalog." not in vr, - "catalogView wrongly reports `no objects`") + self.assertNotIn( + "There are no objects in the Catalog.", + vr, + "catalogView wrongly reports `no objects`" + ) class TestAddDelColumnIndex(ZCatalogBase, unittest.TestCase): @@ -416,7 +419,7 @@ def test_getObject_restricted_raises_Unauthorized(self): pickySecurityManager = PickySecurityManager(['fold']) setSecurityManager(pickySecurityManager) ob = brain.getObject() - self.assertFalse(ob is None) + self.assertIsNotNone(ob) self.assertEqual(ob.getId(), 'ob') # Now test _unrestrictedGetObject diff --git a/src/Products/ZCatalog/tests/test_zodb.py b/src/Products/ZCatalog/tests/test_zodb.py index 12d257cc..d47afe86 100644 --- a/src/Products/ZCatalog/tests/test_zodb.py +++ b/src/Products/ZCatalog/tests/test_zodb.py @@ -116,7 +116,7 @@ def test_unmaintained_search(self): ignore, cache_size_limit = self._get_zodb_info(catalog) # ZODB connection cache grows out of size limit and eats memory actual_size = self._actual_cache_size(catalog) - self.assertTrue(actual_size >= cache_size_limit * 1.1) + self.assertGreaterEqual(actual_size, cache_size_limit * 1.1) def test_maintained_search(self): # run big query with cache maintenance @@ -125,4 +125,4 @@ def test_maintained_search(self): ignore, cache_size_limit = self._get_zodb_info(catalog) # ZODB connection cache stays within its size limit actual_size = self._actual_cache_size(catalog) - self.assertTrue(actual_size <= cache_size_limit + threshold) + self.assertLessEqual(actual_size, cache_size_limit + threshold) diff --git a/tox.ini b/tox.ini index a533a013..8f811ae6 100644 --- a/tox.ini +++ b/tox.ini @@ -10,13 +10,14 @@ envlist = py310 py311 py312 + py313 coverage [testenv] skip_install = true deps = - setuptools < 69 - zc.buildout >= 3.0.1 + setuptools < 74 + zc.buildout >= 3.1 wheel > 0.37 cffi >= 1.17.0rc1 # The universal2 environment is for Python on macOS built with @@ -34,18 +35,40 @@ deps = universal2: MarkupSafe universal2: zope.testrunner 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 >= 3.1 + wheel > 0.37 + cffi >= 1.17.0rc1 + # The universal2 environment is for Python on macOS built with + # universal2 mode instead of architecture-specific. These dependencies + # must be installed separately, zc.buildout is incompatible with the + # universal2 mode and cannot install them itself. + universal2: zope.interface + universal2: zope.security + universal2: zope.container + universal2: Persistence + universal2: Acquisition + universal2: AccessControl + universal2: zodbpickle + universal2: charset_normalizer + universal2: MarkupSafe + universal2: zope.testrunner + + [testenv:release-check] description = ensure that the distribution is ready to release basepython = python3 skip_install = true deps = + setuptools < 74 twine build check-manifest @@ -59,29 +82,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 @@ -91,28 +99,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=85 - -[coverage:run] -branch = True -source = Products.ZCatalog - -[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