From 4779653c918d294211823365675fd0e5de105a58 Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Fri, 30 Aug 2024 06:30:33 +0000 Subject: [PATCH] Apply latest meta/config templates (#155) * - apply latest meta/config templates * - use setuptools 73 and buildout 3.1 --- .github/workflows/pre-commit.yml | 33 +++++ .github/workflows/tests.yml | 87 ++---------- .meta.toml | 2 +- .pre-commit-config.yaml | 28 ++++ MANIFEST.in | 1 + src/AccessControl/SecurityInfo.py | 4 +- src/AccessControl/ZopeGuards.py | 2 +- .../tests/testClassSecurityInfo.py | 10 +- src/AccessControl/tests/testModuleSecurity.py | 4 +- src/AccessControl/tests/testPermissionRole.py | 10 +- .../tests/testSecurityManager.py | 50 +++---- src/AccessControl/tests/testZopeGuards.py | 2 +- src/AccessControl/tests/test_tainted.py | 132 ++++++++++-------- src/AccessControl/tests/test_userfolder.py | 2 +- src/AccessControl/users.py | 2 +- tox.ini | 19 +-- 16 files changed, 197 insertions(+), 191 deletions(-) create mode 100644 .github/workflows/pre-commit.yml create mode 100644 .pre-commit-config.yaml 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 c23686b..d53d28f 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 @@ -153,12 +153,12 @@ jobs: run: | pip install -U pip pip install -U --pre cffi - 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 pip install cffi - name: Build AccessControl (macOS x86_64) @@ -322,7 +322,7 @@ jobs: if: matrix.python-version == '3.13' run: | pip install -U --pre cffi - 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 @@ -332,18 +332,18 @@ jobs: unzip -n dist/AccessControl-*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 AccessControl 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/AccessControl-*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: | @@ -374,80 +374,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 AccessControl wheel - uses: actions/download-artifact@v4 - with: - name: AccessControl-${{ runner.os }}-${{ matrix.python-version }}.whl - path: dist/ - - name: Install AccessControl - run: | - pip install -U pip - pip install -U wheel - pip install -U `ls dist/AccessControl-*`[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: @@ -528,6 +461,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 64ad8d1..15cb862 100644 --- a/.meta.toml +++ b/.meta.toml @@ -2,7 +2,7 @@ # https://github.com/zopefoundation/meta/tree/master/config/c-code [meta] template = "c-code" -commit-id = "13719585" +commit-id = "a1e05e74" [python] with-windows = true 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 1644588..3b23c67 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/AccessControl/SecurityInfo.py b/src/AccessControl/SecurityInfo.py index a025d8a..128a215 100644 --- a/src/AccessControl/SecurityInfo.py +++ b/src/AccessControl/SecurityInfo.py @@ -140,7 +140,7 @@ def protected(self, permission_name): # the decorator returned is remembered in a set and will # remove itself upon call. self.apply will check for an empty # set and raise an AssertionError otherwise. - key = "'{}':{}".format(permission_name, id(lambda x: x)) + key = f"'{permission_name}':{id(lambda x: x)}" def decor(func): self.declareProtected(permission_name, func.__name__) # NOQA: D001 @@ -148,7 +148,7 @@ def decor(func): return func # make sure our key algo creates unique-enough keys if key in self._unused_protected_decorators: - raise KeyError("Duplicate key: {}".format(key)) + raise KeyError(f"Duplicate key: {key}") self._unused_protected_decorators.add(key) return decor diff --git a/src/AccessControl/ZopeGuards.py b/src/AccessControl/ZopeGuards.py index 73515f6..380391c 100644 --- a/src/AccessControl/ZopeGuards.py +++ b/src/AccessControl/ZopeGuards.py @@ -491,7 +491,7 @@ def load_module(module, mname, mnameparts, validate, globals, locals): if mname is None: mname = nextname else: - mname = '{}.{}'.format(mname, nextname) + mname = f'{mname}.{nextname}' # import (if not already imported) and check for MSI nextmodule = secureModule(mname, globals, locals) if nextmodule is None: # not allowed diff --git a/src/AccessControl/tests/testClassSecurityInfo.py b/src/AccessControl/tests/testClassSecurityInfo.py index 3f625d7..807ff92 100644 --- a/src/AccessControl/tests/testClassSecurityInfo.py +++ b/src/AccessControl/tests/testClassSecurityInfo.py @@ -102,10 +102,10 @@ def protected_new(self, REQUEST=None): self.assertEqual(object.private__roles__, ()) imPermissionRole = [r for r in object.protected__roles__ if not r.endswith('_Permission')] - self.assertTrue(len(imPermissionRole) == 4) + self.assertEqual(len(imPermissionRole), 4) for item in ('Manager', 'Role A', 'Role B', 'Role C'): - self.assertTrue(item in imPermissionRole) + self.assertIn(item, imPermissionRole) # functions exist, i.e. decorators returned them self.assertEqual(object.public_new.__name__, 'public_new') @@ -117,10 +117,10 @@ def protected_new(self, REQUEST=None): self.assertEqual(object.private_new__roles__, ()) imPermissionRole = [r for r in object.protected_new__roles__ if not r.endswith('_Permission')] - self.assertTrue(len(imPermissionRole) == 4) + self.assertEqual(len(imPermissionRole), 4) for item in ('Manager', 'Role A', 'Role B', 'Role C'): - self.assertTrue(item in imPermissionRole) + self.assertIn(item, imPermissionRole) # Make sure that a permission defined without accompanying method # is still reflected in __ac_permissions__ @@ -160,7 +160,7 @@ def protected(self, REQUEST=None): # Do class initialization. exc = self.assertRaises(AssertionError, InitializeClass, Test) - self.assertTrue('has 2 non-decorator' in str(exc)) + self.assertIn('has 2 non-decorator', str(exc)) def test_aq_context_in_decorators(self): from Acquisition import Implicit diff --git a/src/AccessControl/tests/testModuleSecurity.py b/src/AccessControl/tests/testModuleSecurity.py index c6627e8..dd314de 100644 --- a/src/AccessControl/tests/testModuleSecurity.py +++ b/src/AccessControl/tests/testModuleSecurity.py @@ -98,12 +98,12 @@ def test_failed_import_keeps_MSI(self): from AccessControl.SecurityInfo import _moduleSecurity as MS from AccessControl.ZopeGuards import guarded_import MSI('AccessControl.tests.nonesuch').declarePublic('pub') # NOQA: D001 - self.assertTrue('AccessControl.tests.nonesuch' in MS) + self.assertIn('AccessControl.tests.nonesuch', MS) self.assertRaises(ImportError, guarded_import, 'AccessControl.tests.nonesuch', ()) - self.assertTrue('AccessControl.tests.nonesuch' in MS) + self.assertIn('AccessControl.tests.nonesuch', MS) def test_level_default(self): self.assertAuth('AccessControl.tests.public_module', (), diff --git a/src/AccessControl/tests/testPermissionRole.py b/src/AccessControl/tests/testPermissionRole.py index 725e16b..8052b8e 100644 --- a/src/AccessControl/tests/testPermissionRole.py +++ b/src/AccessControl/tests/testPermissionRole.py @@ -80,7 +80,7 @@ def assertPRoles(ob, permission, expect): expected[r] = 1 if got == expected: # Dict compare does the Right Thing. same = 1 - assert same, 'Expected roles: {!r}, got: {!r}'.format(expect, roles) + assert same, f'Expected roles: {expect!r}, got: {roles!r}' class PermissionRoleTests (unittest.TestCase): @@ -123,7 +123,7 @@ def testAppDefaults(self): def testPermissionRoleSupportsGetattr(self): a = PermissionRole('a') - self.assertTrue(getattr(a, '__roles__') == ('Manager', )) - self.assertTrue(getattr(a, '_d') == ('Manager', )) - self.assertTrue(getattr(a, '__name__') == 'a') - self.assertTrue(getattr(a, '_p') == '_a_Permission') + self.assertEqual(getattr(a, '__roles__'), ('Manager', )) + self.assertEqual(getattr(a, '_d'), ('Manager', )) + self.assertEqual(getattr(a, '__name__'), 'a') + self.assertEqual(getattr(a, '_p'), '_a_Permission') diff --git a/src/AccessControl/tests/testSecurityManager.py b/src/AccessControl/tests/testSecurityManager.py index a1ae9cd..5a86d5d 100644 --- a/src/AccessControl/tests/testSecurityManager.py +++ b/src/AccessControl/tests/testSecurityManager.py @@ -69,7 +69,7 @@ def _makeOne(self, thread_id, context): def test_getUser(self): context = DummyContext() mgr = self._makeOne(_THREAD_ID, context) - self.assertTrue(mgr.getUser() is context.user) + self.assertIs(mgr.getUser(), context.user) def test_calledByExecutable_no_stack(self): context = DummyContext() @@ -89,7 +89,7 @@ def test_addContext_no_custom_policy(self): original_policy = mgr._policy executableObject = object() mgr.addContext(executableObject) - self.assertTrue(mgr._policy is original_policy) + self.assertIs(mgr._policy, original_policy) def test_addContext_with_custom_policy(self): context = DummyContext() @@ -97,7 +97,7 @@ def test_addContext_with_custom_policy(self): new_policy = DummyPolicy() executableObject = ExecutableObject(new_policy) mgr.addContext(executableObject) - self.assertTrue(mgr._policy is new_policy) + self.assertIs(mgr._policy, new_policy) def test_addContext_with_custom_policy_then_none(self): context = DummyContext() @@ -107,7 +107,7 @@ def test_addContext_with_custom_policy_then_none(self): executableObject = ExecutableObject(new_policy) mgr.addContext(executableObject) mgr.addContext(object()) - self.assertTrue(mgr._policy is original_policy) + self.assertIs(mgr._policy, original_policy) def test_removeContext_pops_items_above_EO(self): context = DummyContext() @@ -121,8 +121,8 @@ def test_removeContext_pops_items_above_EO(self): mgr.removeContext(GAMMA) self.assertEqual(len(context.stack), 2) - self.assertTrue(context.stack[0] is ALPHA) - self.assertTrue(context.stack[1] is BETA) + self.assertIs(context.stack[0], ALPHA) + self.assertIs(context.stack[1], BETA) def test_removeContext_last_EO_restores_default_policy(self): context = DummyContext() @@ -131,7 +131,7 @@ def test_removeContext_last_EO_restores_default_policy(self): top = object() context.stack.append(top) mgr.removeContext(top) - self.assertTrue(mgr._policy is original_policy) + self.assertIs(mgr._policy, original_policy) def test_removeContext_with_top_having_custom_policy(self): context = DummyContext() @@ -141,7 +141,7 @@ def test_removeContext_with_top_having_custom_policy(self): top = object() context.stack.append(top) mgr.removeContext(top) - self.assertTrue(mgr._policy is new_policy) + self.assertIs(mgr._policy, new_policy) def test_removeContext_with_top_having_no_custom_policy(self): context = DummyContext() @@ -153,7 +153,7 @@ def test_removeContext_with_top_having_no_custom_policy(self): top = object() context.stack.append(top) mgr.removeContext(executableObject) - self.assertTrue(mgr._policy is original_policy) + self.assertIs(mgr._policy, original_policy) def test_checkPermission_delegates_to_policy(self): context = DummyContext() @@ -162,10 +162,10 @@ def test_checkPermission_delegates_to_policy(self): mgr = self._makeOne(_THREAD_ID, context) new_policy = mgr._policy = DummyPolicy() result = mgr.checkPermission(PERMISSION, TARGET) - self.assertTrue(result is DummyPolicy.CHECK_PERMISSION_RESULT) - self.assertTrue(new_policy.CHECK_PERMISSION_ARGS[0] is PERMISSION) - self.assertTrue(new_policy.CHECK_PERMISSION_ARGS[1] is TARGET) - self.assertTrue(new_policy.CHECK_PERMISSION_ARGS[2] is context) + self.assertIs(result, DummyPolicy.CHECK_PERMISSION_RESULT) + self.assertIs(new_policy.CHECK_PERMISSION_ARGS[0], PERMISSION) + self.assertIs(new_policy.CHECK_PERMISSION_ARGS[1], TARGET) + self.assertIs(new_policy.CHECK_PERMISSION_ARGS[2], context) def test_validate_without_roles_delegates_to_policy(self): context = DummyContext() @@ -184,11 +184,11 @@ def test_validate_without_roles_delegates_to_policy(self): self.assertTrue(result) self.assertEqual(len(new_policy.VALIDATE_ARGS), 5) - self.assertTrue(new_policy.VALIDATE_ARGS[0] is ACCESSED) - self.assertTrue(new_policy.VALIDATE_ARGS[1] is CONTAINER) + self.assertIs(new_policy.VALIDATE_ARGS[0], ACCESSED) + self.assertIs(new_policy.VALIDATE_ARGS[1], CONTAINER) self.assertEqual(new_policy.VALIDATE_ARGS[2], NAME) - self.assertTrue(new_policy.VALIDATE_ARGS[3] is VALUE) - self.assertTrue(new_policy.VALIDATE_ARGS[4] is context) + self.assertIs(new_policy.VALIDATE_ARGS[3], VALUE) + self.assertIs(new_policy.VALIDATE_ARGS[4], context) def test_validate_with_roles_delegates_to_policy(self): context = DummyContext() @@ -209,11 +209,11 @@ def test_validate_with_roles_delegates_to_policy(self): self.assertTrue(result) self.assertEqual(len(new_policy.VALIDATE_ARGS), 6) - self.assertTrue(new_policy.VALIDATE_ARGS[0] is ACCESSED) - self.assertTrue(new_policy.VALIDATE_ARGS[1] is CONTAINER) + self.assertIs(new_policy.VALIDATE_ARGS[0], ACCESSED) + self.assertIs(new_policy.VALIDATE_ARGS[1], CONTAINER) self.assertEqual(new_policy.VALIDATE_ARGS[2], NAME) - self.assertTrue(new_policy.VALIDATE_ARGS[3] is VALUE) - self.assertTrue(new_policy.VALIDATE_ARGS[4] is context) + self.assertIs(new_policy.VALIDATE_ARGS[3], VALUE) + self.assertIs(new_policy.VALIDATE_ARGS[4], context) self.assertEqual(new_policy.VALIDATE_ARGS[5], ROLES) def test_DTMLValidate_delegates_to_policy_validate(self): @@ -235,11 +235,11 @@ def test_DTMLValidate_delegates_to_policy_validate(self): self.assertTrue(result) self.assertEqual(len(new_policy.VALIDATE_ARGS), 5) - self.assertTrue(new_policy.VALIDATE_ARGS[0] is ACCESSED) - self.assertTrue(new_policy.VALIDATE_ARGS[1] is CONTAINER) + self.assertIs(new_policy.VALIDATE_ARGS[0], ACCESSED) + self.assertIs(new_policy.VALIDATE_ARGS[1], CONTAINER) self.assertEqual(new_policy.VALIDATE_ARGS[2], NAME) - self.assertTrue(new_policy.VALIDATE_ARGS[3] is VALUE) - self.assertTrue(new_policy.VALIDATE_ARGS[4] is context) + self.assertIs(new_policy.VALIDATE_ARGS[3], VALUE) + self.assertIs(new_policy.VALIDATE_ARGS[4], context) class PythonSecurityManagerTests(SecurityManagerTestBase, diff --git a/src/AccessControl/tests/testZopeGuards.py b/src/AccessControl/tests/testZopeGuards.py index 50eeca9..5dde02e 100644 --- a/src/AccessControl/tests/testZopeGuards.py +++ b/src/AccessControl/tests/testZopeGuards.py @@ -217,7 +217,7 @@ def test_get_simple(self): def test_get_default(self): from AccessControl.ZopeGuards import get_dict_get get = get_dict_get({'foo': 'bar'}, 'get') - self.assertTrue(get('baz') is None) + self.assertIsNone(get('baz')) self.assertEqual(get('baz', 'splat'), 'splat') def test_get_validates(self): diff --git a/src/AccessControl/tests/test_tainted.py b/src/AccessControl/tests/test_tainted.py index ec5fbfe..e6c6817 100644 --- a/src/AccessControl/tests/test_tainted.py +++ b/src/AccessControl/tests/test_tainted.py @@ -56,9 +56,9 @@ def testEqual(self): self.assertEqual(self.tainted, self.unquoted) def testCmp(self): - self.assertTrue(self.tainted == self.unquoted) - self.assertTrue(self.tainted < 'a') - self.assertTrue(self.tainted > '.') + self.assertEqual(self.tainted, self.unquoted) + self.assertLess(self.tainted, 'a') + self.assertGreater(self.tainted, '.') def testHash(self): hash = {} @@ -70,41 +70,45 @@ def testLen(self): self.assertEqual(len(self.tainted), len(self.unquoted)) def testGetItem(self): - self.assertTrue(isinstance(self.tainted[0], self._getClass())) + self.assertIsInstance(self.tainted[0], self._getClass()) self.assertEqual(self.tainted[0], '<') - self.assertFalse(isinstance(self.tainted[-1], self._getClass())) + self.assertNotIsInstance(self.tainted[-1], self._getClass()) self.assertEqual(self.tainted[-1], '>') def testGetSlice(self): - self.assertTrue(isinstance(self.tainted[0:1], self._getClass())) + self.assertIsInstance(self.tainted[0:1], self._getClass()) self.assertEqual(self.tainted[0:1], '<') - self.assertFalse(isinstance(self.tainted[1:], self._getClass())) + self.assertNotIsInstance(self.tainted[1:], self._getClass()) self.assertEqual(self.tainted[1:], self.unquoted[1:]) CONCAT = 'test' def testConcat(self): - self.assertTrue(isinstance(self.tainted + self.CONCAT, - self._getClass())) + self.assertIsInstance( + self.tainted + self.CONCAT, + self._getClass() + ) self.assertEqual(self.tainted + self.CONCAT, self.unquoted + self.CONCAT) - self.assertTrue(isinstance(self.CONCAT + self.tainted, - self._getClass())) + self.assertIsInstance( + self.CONCAT + self.tainted, + self._getClass() + ) self.assertEqual(self.CONCAT + self.tainted, self.CONCAT + self.unquoted) def testMultiply(self): - self.assertTrue(isinstance(2 * self.tainted, self._getClass())) + self.assertIsInstance(2 * self.tainted, self._getClass()) self.assertEqual(2 * self.tainted, 2 * self.unquoted) - self.assertTrue(isinstance(self.tainted * 2, self._getClass())) + self.assertIsInstance(self.tainted * 2, self._getClass()) self.assertEqual(self.tainted * 2, self.unquoted * 2) def testInterpolate(self): tainted = self._getClass()('<%s>') - self.assertTrue(isinstance(tainted % 'foo', self._getClass())) + self.assertIsInstance(tainted % 'foo', self._getClass()) self.assertEqual(tainted % 'foo', '') tainted = self._getClass()('<%s attr="%s">') - self.assertTrue(isinstance(tainted % ('foo', 'bar'), self._getClass())) + self.assertIsInstance(tainted % ('foo', 'bar'), self._getClass()) self.assertEqual(tainted % ('foo', 'bar'), '') def testStringMethods(self): @@ -118,21 +122,21 @@ def testStringMethods(self): v = getattr(tainted, f)() self.assertEqual(v, getattr(unquoted, f)()) if f in returnsTainted: - self.assertTrue(isinstance(v, self._getClass())) + self.assertIsInstance(v, self._getClass()) else: - self.assertFalse(isinstance(v, self._getClass())) + self.assertNotIsInstance(v, self._getClass()) optArg = "lstrip rstrip strip".split() for f in optArg: v = getattr(tainted, f)(" ") self.assertEqual(v, getattr(unquoted, f)(" ")) - self.assertTrue(isinstance(v, self._getClass())) + self.assertIsInstance(v, self._getClass()) justify = "center ljust rjust".split() for f in justify: v = getattr(tainted, f)(30) self.assertEqual(v, getattr(unquoted, f)(30)) - self.assertTrue(isinstance(v, self._getClass())) + self.assertIsInstance(v, self._getClass()) searches = "find index rfind rindex endswith startswith".split() searchraises = "index rindex".split() @@ -146,35 +150,41 @@ def testStringMethods(self): unquoted.count('test', 1, -1)) self.assertEqual(tainted.encode(), unquoted.encode()) - self.assertTrue(isinstance(tainted.encode(), self._getClass())) + self.assertIsInstance(tainted.encode(), self._getClass()) self.assertEqual(tainted.expandtabs(10), unquoted.expandtabs(10)) - self.assertTrue(isinstance(tainted.expandtabs(), self._getClass())) + self.assertIsInstance(tainted.expandtabs(), self._getClass()) self.assertEqual(tainted.replace('test', 'spam'), unquoted.replace('test', 'spam')) - self.assertTrue(isinstance(tainted.replace('test', '<'), - self._getClass())) - self.assertFalse(isinstance(tainted.replace('test', 'spam'), - self._getClass())) + self.assertIsInstance( + tainted.replace('test', '<'), + self._getClass() + ) + self.assertNotIsInstance( + tainted.replace('test', 'spam'), + self._getClass() + ) self.assertEqual(tainted.split(), unquoted.split()) for part in self._getClass()('< < <').split(): - self.assertTrue(isinstance(part, self._getClass())) + self.assertIsInstance(part, self._getClass()) for part in tainted.split(): - self.assertFalse(isinstance(part, self._getClass())) + self.assertNotIsInstance(part, self._getClass()) multiline = 'test\n' lines = self._getClass()(multiline).split() self.assertEqual(lines, multiline.split()) - self.assertTrue(isinstance(lines[1], self._getClass())) - self.assertFalse(isinstance(lines[0], self._getClass())) + self.assertIsInstance(lines[1], self._getClass()) + self.assertNotIsInstance(lines[0], self._getClass()) transtable = ''.join(map(chr, range(256))) self.assertEqual(tainted.translate(transtable), unquoted.translate(transtable)) - self.assertTrue(isinstance(self._getClass()('<').translate(transtable), - self._getClass())) + self.assertIsInstance( + self._getClass()('<').translate(transtable), + self._getClass() + ) def testQuoted(self): self.assertEqual(self.tainted.quoted(), self.quoted) @@ -192,35 +202,37 @@ def _getClass(self): return TaintedBytes def testCmp(self): - self.assertTrue(self.tainted == self.unquoted) self.assertEqual(self.tainted, self.unquoted) - self.assertTrue(self.tainted < b'a') - self.assertTrue(self.tainted > b'.') + self.assertEqual(self.tainted, self.unquoted) + self.assertLess(self.tainted, b'a') + self.assertGreater(self.tainted, b'.') CONCAT = b'test' def testGetItem(self): - self.assertTrue(isinstance(self.tainted[0], self._getClass())) + self.assertIsInstance(self.tainted[0], self._getClass()) self.assertEqual(self.tainted[0], self._getClass()(b'<')) - self.assertFalse(isinstance(self.tainted[-1], self._getClass())) + self.assertNotIsInstance(self.tainted[-1], self._getClass()) self.assertEqual(self.tainted[-1], 62) def testStr(self): self.assertEqual(str(self.tainted), self.unquoted.decode('utf8')) def testGetSlice(self): - self.assertTrue(isinstance(self.tainted[0:1], self._getClass())) + self.assertIsInstance(self.tainted[0:1], self._getClass()) self.assertEqual(self.tainted[0:1], b'<') - self.assertFalse(isinstance(self.tainted[1:], self._getClass())) + self.assertNotIsInstance(self.tainted[1:], self._getClass()) self.assertEqual(self.tainted[1:], self.unquoted[1:]) def testInterpolate(self): tainted = self._getClass()(b'<%s>') - self.assertTrue(isinstance(tainted % b'foo', self._getClass())) + self.assertIsInstance(tainted % b'foo', self._getClass()) self.assertEqual(tainted % b'foo', b'') tainted = self._getClass()(b'<%s attr="%s">') - self.assertTrue(isinstance(tainted % (b'foo', b'bar'), - self._getClass())) + self.assertIsInstance( + tainted % (b'foo', b'bar'), + self._getClass() + ) self.assertEqual(tainted % (b'foo', b'bar'), b'') def testStringMethods(self): @@ -234,21 +246,21 @@ def testStringMethods(self): v = getattr(tainted, f)() self.assertEqual(v, getattr(unquoted, f)()) if f in returnsTainted: - self.assertTrue(isinstance(v, self._getClass())) + self.assertIsInstance(v, self._getClass()) else: - self.assertFalse(isinstance(v, self._getClass())) + self.assertNotIsInstance(v, self._getClass()) optArg = "lstrip rstrip strip".split() for f in optArg: v = getattr(tainted, f)(b" ") self.assertEqual(v, getattr(unquoted, f)(b" ")) - self.assertTrue(isinstance(v, self._getClass())) + self.assertIsInstance(v, self._getClass()) justify = "center ljust rjust".split() for f in justify: v = getattr(tainted, f)(30) self.assertEqual(v, getattr(unquoted, f)(30)) - self.assertTrue(isinstance(v, self._getClass())) + self.assertIsInstance(v, self._getClass()) searches = "find index rfind rindex endswith startswith".split() searchraises = "index rindex".split() @@ -263,36 +275,42 @@ def testStringMethods(self): self.assertEqual(tainted.decode(), unquoted.decode()) from AccessControl.tainted import TaintedString - self.assertTrue(isinstance(tainted.decode(), TaintedString)) + self.assertIsInstance(tainted.decode(), TaintedString) self.assertEqual(tainted.expandtabs(10), unquoted.expandtabs(10)) - self.assertTrue(isinstance(tainted.expandtabs(), self._getClass())) + self.assertIsInstance(tainted.expandtabs(), self._getClass()) self.assertEqual(tainted.replace(b'test', b'spam'), unquoted.replace(b'test', b'spam')) - self.assertTrue(isinstance(tainted.replace(b'test', b'<'), - self._getClass())) - self.assertFalse(isinstance(tainted.replace(b'test', b'spam'), - self._getClass())) + self.assertIsInstance( + tainted.replace(b'test', b'<'), + self._getClass() + ) + self.assertNotIsInstance( + tainted.replace(b'test', b'spam'), + self._getClass() + ) self.assertEqual(tainted.split(), unquoted.split()) for part in self._getClass()(b'< < <').split(): - self.assertTrue(isinstance(part, self._getClass())) + self.assertIsInstance(part, self._getClass()) for part in tainted.split(): - self.assertFalse(isinstance(part, self._getClass())) + self.assertNotIsInstance(part, self._getClass()) multiline = b'test\n' lines = self._getClass()(multiline).split() self.assertEqual(lines, multiline.split()) - self.assertTrue(isinstance(lines[1], self._getClass())) - self.assertFalse(isinstance(lines[0], self._getClass())) + self.assertIsInstance(lines[1], self._getClass()) + self.assertNotIsInstance(lines[0], self._getClass()) transtable = bytes(range(256)) self.assertEqual(tainted.translate(transtable), unquoted.translate(transtable)) kls = self._getClass()(b'<') - self.assertTrue(isinstance(kls.translate(transtable), - self._getClass())) + self.assertIsInstance( + kls.translate(transtable), + self._getClass() + ) def testConstructor(self): from AccessControl.tainted import TaintedBytes diff --git a/src/AccessControl/tests/test_userfolder.py b/src/AccessControl/tests/test_userfolder.py index ad04b66..9909973 100644 --- a/src/AccessControl/tests/test_userfolder.py +++ b/src/AccessControl/tests/test_userfolder.py @@ -104,7 +104,7 @@ def testIdentify(self): def testGetRoles(self): uf = self._makeOne() user = uf.getUser('user1') - self.assertTrue('role1' in user.getRoles()) + self.assertIn('role1', user.getRoles()) def testMaxListUsers(self): # create a folder-ish thing which contains a roleManager, diff --git a/src/AccessControl/users.py b/src/AccessControl/users.py index 8ce2edb..ca62e9f 100644 --- a/src/AccessControl/users.py +++ b/src/AccessControl/users.py @@ -251,7 +251,7 @@ def __str__(self): return self.getUserName() def __repr__(self): - return '<{} {!r}>'.format(self.__class__.__name__, self.getUserName()) + return f'<{self.__class__.__name__} {self.getUserName()!r}>' class SimpleUser(BasicUser): diff --git a/tox.ini b/tox.ini index 617db14..fcd248f 100644 --- a/tox.ini +++ b/tox.ini @@ -13,10 +13,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 @@ -40,6 +39,7 @@ commands = coverage run -m zope.testrunner --test-path=src {posargs:-vc} coverage html -i coverage report -i -m --fail-under=80 + [testenv:release-check] description = ensure that the distribution is ready to release basepython = python3 @@ -58,20 +58,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