diff --git a/.editorconfig b/.editorconfig index 824b9c3..ce35108 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,7 +2,7 @@ # https://github.com/zopefoundation/meta/tree/master/config/zope-product # # EditorConfig Configuration file, for more details see: -# http://EditorConfig.org +# https://EditorConfig.org # EditorConfig is a convention description, that could be interpreted # by multiple editors to enforce common coding conventions for specific # file types diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000..cf37dd3 --- /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.13' + - 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 dfbf3c2..dab1e65 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,55 +12,57 @@ 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.10", "py310"] + - ["3.11", "py311"] + - ["3.12", "py312"] + - ["3.13", "py313"] + - ["3.14", "py314"] + - ["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 - 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 + persist-credentials: false + - name: Install additional dependencies run: | - python -m pip install --upgrade pip - pip install tox set -ex sudo apt update sudo apt install -y ldap-utils slapd libldap2-dev libsasl2-dev + - name: Install uv + caching + # astral/setup-uv@7.1.3 + uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 + with: + 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 aca3a48..d6d796a 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 = "9fcd3d67" [python] with-windows = false @@ -25,6 +25,7 @@ additional-rules = [ [check-manifest] additional-ignores = [ "docs/_build/html/_static/*", + "docs/_build/html/_static/scripts/*", ] [coverage] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..cdeaf6c --- /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: "7.0.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.21.0 + hooks: + - id: pyupgrade + args: [--py310-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/.readthedocs.yaml b/.readthedocs.yaml index 047163d..ecff5e7 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -11,11 +11,6 @@ build: os: ubuntu-22.04 tools: python: "3.11" - apt_packages: - - ldap-utils - - slapd - - libldap2-dev - - libsasl2-dev # Build documentation in the docs/ directory with Sphinx sphinx: diff --git a/CHANGES.rst b/CHANGES.rst index adc220b..d9d5b9a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,6 +7,10 @@ To see earlier changes please see HISTORY.txt. ---------------- +- Add support for Python 3.13, 3.14. + +- Drop support for Python 3.7, 3.8, 3.9. + 5.1 (2024-01-03) ---------------- - add support for Python 3.12, 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 83b3cdc..2301abf 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 bbfe3fa..08a14f2 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 diff --git a/docs/conf.py b/docs/conf.py index 128ff55..39fc956 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -5,26 +5,25 @@ 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 '' year = datetime.datetime.now().year +sys.path.append(os.path.abspath('../src')) +rqmt = distribution('Products.LDAPMultiPlugins') # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = 'Products.LDAPMultiPlugins' -copyright = '2005-%i, Jens Vagelpohl and Contributors' % year +copyright = f'2005-{year}, Jens Vagelpohl and Contributors' author = 'Jens Vagelpohl' # 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/docs/requirements.txt b/docs/requirements.txt index 59ed542..4956bd5 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1,3 @@ -sphinx>5 -.[docs] +sphinx +furo +repoze.sphinx.autointerface diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a33ceda --- /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.LDAPMultiPlugins"] + +[tool.coverage.report] +fail_under = 40 +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 07935d4..9ed406b 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 @@ -14,6 +12,7 @@ ignore = .meta.toml docs/_build/html/_sources/* docs/_build/html/_static/* + docs/_build/html/_static/scripts/* [isort] force_single_line = True diff --git a/setup.py b/setup.py index d59864a..f55f32c 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,6 @@ import os -from setuptools import find_packages from setuptools import setup @@ -26,59 +25,48 @@ def read(name): version='5.2.dev0', description='LDAP-backed plugins for the Zope PluggableAuthService', long_description=read('README.rst'), + long_description_content_type='text/x-rst', classifiers=[ - "Development Status :: 6 - Mature", - "Framework :: Zope", - "Framework :: Zope :: 5", - "Intended Audience :: Developers", - "License :: OSI Approved :: Zope Public License", - "Operating System :: OS Independent", - "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 :: Site Management", - "Topic :: Software Development", - "Topic :: System :: Systems Administration ::" - " Authentication/Directory :: LDAP", - ], + "Development Status :: 6 - Mature", + "Framework :: Zope", + "Framework :: Zope :: 5", + "Intended Audience :: Developers", + "License :: OSI Approved :: Zope Public License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Topic :: Internet :: WWW/HTTP :: Site Management", + "Topic :: Software Development", + "Topic :: System :: Systems Administration ::" + "Authentication/Directory :: LDAP", + ], keywords='web application server zope ldap', author="Jens Vagelpohl and contributors", author_email="jens@dataflake.org", url='https://github.com/dataflake/Products.LDAPMultiPlugins', project_urls={ - 'Documentation': 'https://productsldapmultiplugins.readthedocs.io/', - 'Issue Tracker': ('https://github.com/dataflake/' - 'Products.LDAPMultiPlugins/issues'), - 'Sources': 'https://github.com/dataflake/Products.LDAPMultiPlugins', + 'Documentation': 'https://productsldapmultiplugins.readthedocs.io/', + 'Issue Tracker': ('https://github.com/dataflake/' + 'Products.LDAPMultiPlugins/issues'), + 'Sources': 'https://github.com/dataflake/Products.LDAPMultiPlugins', }, - license="ZPL 2.1", - packages=find_packages('src'), - include_package_data=True, - namespace_packages=['Products'], - package_dir={'': 'src'}, - zip_safe=False, - python_requires='>=3.7', + license="ZPL-2.1", + python_requires='>=3.10', install_requires=[ - 'setuptools', - 'Zope >= 5', - 'Products.LDAPUserFolder', - 'Products.PluggableAuthService', - ], + 'Zope >= 5', + 'Products.LDAPUserFolder', + 'Products.PluggableAuthService', + ], extras_require={ - 'exportimport': ['Products.GenericSetup >= 2.0b1'], - 'docs': [ - 'Sphinx', - 'sphinx_rtd_theme', - 'repoze.sphinx.autointerface', - 'pkginfo' - ], + 'exportimport': ['Products.GenericSetup >= 2.0b1'], + 'docs': [ + 'Sphinx', + 'furo', + 'repoze.sphinx.autointerface', + ], }, - entry_points=""" - [zope2.initialize] - Products.LDAPMultiPlugins = Products.LDAPMultiPlugins:initialize - """ ) diff --git a/src/Products/LDAPMultiPlugins/ActiveDirectoryMultiPlugin.py b/src/Products/LDAPMultiPlugins/ActiveDirectoryMultiPlugin.py index 074dfb1..2243b26 100644 --- a/src/Products/LDAPMultiPlugins/ActiveDirectoryMultiPlugin.py +++ b/src/Products/LDAPMultiPlugins/ActiveDirectoryMultiPlugin.py @@ -106,7 +106,7 @@ class ActiveDirectoryMultiPlugin(LDAPPluginBase): {'id': 'group_class', 'type': 'string', 'mode': 'w'}, {'id': 'group_recurse', 'type': 'int', 'mode': 'w'}, {'id': 'group_recurse_depth', 'type': 'int', 'mode': 'w'}, - ) + ) groupid_attr = 'objectGUID' grouptitle_attr = 'cn' @@ -308,7 +308,7 @@ def enumerateUsers(self, id=None, login=None, exact_match=0, result.sort(key=lambda item: item.get(sort_by, '').lower()) if isinstance(max_results, int) and len(result) > max_results: - result = result[:max_results-1] + result = result[:max_results - 1] else: result = [] @@ -324,7 +324,7 @@ def enumerateUsers(self, id=None, login=None, exact_match=0, result.sort(key=lambda item: item.get(sort_by, '').lower()) if isinstance(max_results, int) and len(result) > max_results: - result = result[:max_results-1] + result = result[:max_results - 1] result = tuple(result) @@ -400,7 +400,7 @@ def enumerateGroups(self, id=None, exact_match=0, sort_by=None, results.sort(key=lambda item: item.get(sort_by, '').lower()) if isinstance(max_results, int) and len(results) > max_results: - results = results[:max_results+1] + results = results[:max_results + 1] results = tuple(results) diff --git a/src/Products/LDAPMultiPlugins/LDAPMultiPlugin.py b/src/Products/LDAPMultiPlugins/LDAPMultiPlugin.py index 1df6f14..8cd6caf 100644 --- a/src/Products/LDAPMultiPlugins/LDAPMultiPlugin.py +++ b/src/Products/LDAPMultiPlugins/LDAPMultiPlugin.py @@ -200,7 +200,7 @@ def enumerateUsers(self, id=None, login=None, exact_match=0, sort_by=None, result.sort(key=lambda item: item.get(sort_by, '').lower()) if isinstance(max_results, int) and len(result) > max_results: - result = result[:max_results-1] + result = result[:max_results - 1] result = tuple(result) self.ZCacheable_set(result, view_name=view_name, keywords=criteria) @@ -252,7 +252,7 @@ def enumerateGroups(self, id=None, exact_match=False, sort_by=None, return () if isinstance(max_results, int) and len(results) > max_results: - results = results[:max_results+1] + results = results[:max_results + 1] for rec in results: rec['pluginid'] = plugin_id diff --git a/src/Products/__init__.py b/src/Products/__init__.py deleted file mode 100644 index de40ea7..0000000 --- a/src/Products/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__import__('pkg_resources').declare_namespace(__name__) diff --git a/tox.ini b/tox.ini index 072c813..d86805f 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 + py314 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 pyproject.toml,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=40 - -[coverage:run] -branch = True -source = Products.LDAPMultiPlugins - -[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