diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0d18a51..30bb649 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,29 +17,30 @@ jobs: fail-fast: false matrix: os: - - ["ubuntu", "ubuntu-20.04"] + - ["ubuntu", "ubuntu-latest"] config: # [Python version, tox env] - - ["3.9", "lint"] - - ["3.7", "py37"] + - ["3.11", "release-check"] + - ["3.11", "lint"] - ["3.8", "py38"] - ["3.9", "py39"] - ["3.10", "py310"] - ["3.11", "py311"] - - ["3.9", "docs"] - - ["3.9", "coverage"] + - ["3.12", "py312"] + - ["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 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.config[0] }} - name: Pip cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ matrix.config[0] }}-${{ hashFiles('setup.*', 'tox.ini') }} @@ -51,7 +52,11 @@ jobs: python -m pip install --upgrade pip pip install tox - name: Test + if: ${{ !startsWith(runner.os, 'Mac') }} run: tox -e ${{ matrix.config[1] }} + - name: Test (macOS) + if: ${{ startsWith(runner.os, 'Mac') }} + run: tox -e ${{ matrix.config[1] }}-universal2 - name: Coverage if: matrix.config[1] == 'coverage' run: | diff --git a/.meta.toml b/.meta.toml index d1e7c8f..20529b4 100644 --- a/.meta.toml +++ b/.meta.toml @@ -2,7 +2,7 @@ # https://github.com/zopefoundation/meta/tree/master/config/pure-python [meta] template = "pure-python" -commit-id = "b21fbbf2" +commit-id = "994c74d7" [python] with-windows = false diff --git a/CHANGES.rst b/CHANGES.rst index 7e21984..50c943b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,10 @@ Changelog 5.2 (unreleased) ---------------- +- Add support for Python 3.12. + +- Drop support for Python 3.7. + - Add basque (eu) translation. - Fix tests to run with ``lxml >= 5.3``, thus requiring at least that version. diff --git a/setup.cfg b/setup.cfg index 09d8284..770db3c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,5 @@ # Generated from: # https://github.com/zopefoundation/meta/tree/master/config/pure-python -[bdist_wheel] -universal = 0 [flake8] doctests = 1 diff --git a/setup.py b/setup.py index f950ae2..0eea88e 100644 --- a/setup.py +++ b/setup.py @@ -48,11 +48,11 @@ def read(*rnames): 'License :: OSI Approved :: Zope Public License', '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', 'Programming Language :: Python :: Implementation :: CPython', 'Natural Language :: English', 'Operating System :: OS Independent', @@ -64,7 +64,7 @@ def read(*rnames): include_package_data=True, package_dir={'': 'src'}, namespace_packages=['z3c'], - python_requires='>=3.7', + python_requires='>=3.8', extras_require=dict( extra=[ 'z3c.pt >= 2.1', diff --git a/src/z3c/form/action.py b/src/z3c/form/action.py index cd1d6a8..68311e8 100644 --- a/src/z3c/form/action.py +++ b/src/z3c/form/action.py @@ -30,7 +30,7 @@ def __init__(self, action): self.action = action def __repr__(self): - return '<{} for {!r}>'.format(self.__class__.__name__, self.action) + return f'<{self.__class__.__name__} for {self.action!r}>' @zope.interface.implementer(interfaces.IActionErrorEvent) @@ -104,7 +104,7 @@ def execute(self): return result def __repr__(self): - return '<{} {!r}>'.format(self.__class__.__name__, self.__name__) + return f'<{self.__class__.__name__} {self.__name__!r}>' @zope.interface.implementer(interfaces.IActionHandler) diff --git a/src/z3c/form/browser/tests.py b/src/z3c/form/browser/tests.py index faa224d..073b5e6 100644 --- a/src/z3c/form/browser/tests.py +++ b/src/z3c/form/browser/tests.py @@ -11,11 +11,6 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -""" -$Id$ -""" -__docformat__ = "reStructuredText" - import doctest import itertools import unittest @@ -28,7 +23,7 @@ try: import z3c.pt import z3c.ptcompat # noqa: F401 imported but unused -except ImportError: +except ModuleNotFoundError: Z3CPT_AVAILABLE = False diff --git a/src/z3c/form/browser/widget.py b/src/z3c/form/browser/widget.py index 75d5680..f900310 100644 --- a/src/z3c/form/browser/widget.py +++ b/src/z3c/form/browser/widget.py @@ -153,7 +153,6 @@ def addClass(self, klass: str): # make sure items are not repeated parts = self.klass.split() + klass.split() # Remove duplicates and keep order. - # Dictionaries are ordered in Python 3.7+ parts = list(dict.fromkeys(parts)) self.klass = " ".join(parts) @@ -224,14 +223,12 @@ class HTMLInputWidget(HTMLFormElement): @property def _html_attributes(self) -> list: attributes = super()._html_attributes - attributes.extend( - [ - "readonly", - "alt", - "accesskey", - "onselect", - ] - ) + attributes.extend([ + "readonly", + "alt", + "accesskey", + "onselect", + ]) return attributes @@ -247,14 +244,12 @@ class HTMLTextInputWidget(HTMLInputWidget): @property def _html_attributes(self) -> list: attributes = super()._html_attributes - attributes.extend( - [ - "size", - "maxlength", - "placeholder", - "autocapitalize", - ] - ) + attributes.extend([ + "size", + "maxlength", + "placeholder", + "autocapitalize", + ]) return attributes @@ -270,15 +265,13 @@ class HTMLTextAreaWidget(HTMLFormElement): @property def _html_attributes(self) -> list: attributes = super()._html_attributes - attributes.extend( - [ - "rows", - "cols", - "readonly", - "accesskey", - "onselect", - ] - ) + attributes.extend([ + "rows", + "cols", + "readonly", + "accesskey", + "onselect", + ]) return attributes @@ -291,12 +284,10 @@ class HTMLSelectWidget(HTMLFormElement): @property def _html_attributes(self) -> list: attributes = super()._html_attributes - attributes.extend( - [ - "multiple", - "size", - ] - ) + attributes.extend([ + "multiple", + "size", + ]) return attributes diff --git a/src/z3c/form/button.py b/src/z3c/form/button.py index b02ed71..c171377 100644 --- a/src/z3c/form/button.py +++ b/src/z3c/form/button.py @@ -167,7 +167,7 @@ def __call__(self, form, action): return self.func(form, action) def __repr__(self): - return '<{} for {!r}>'.format(self.__class__.__name__, self.button) + return f'<{self.__class__.__name__} for {self.button!r}>' def handler(button): diff --git a/src/z3c/form/button.rst b/src/z3c/form/button.rst index 1d5a16b..95b938a 100644 --- a/src/z3c/form/button.rst +++ b/src/z3c/form/button.rst @@ -442,7 +442,7 @@ First, you are able to add button managers: >>> bm2 = button.Buttons(button.Button('help', title='Help')) >>> bm1 + bm2 - Buttons([...]) + Buttons(...) >>> list(bm1 + bm2) ['apply', 'cancel', 'help'] diff --git a/src/z3c/form/contentprovider.rst b/src/z3c/form/contentprovider.rst index 68ada7c..bba286e 100644 --- a/src/z3c/form/contentprovider.rst +++ b/src/z3c/form/contentprovider.rst @@ -284,7 +284,7 @@ To enable form updating, all widget adapters must be registered:: >>> personForm.update() >>> personForm.widgets - FieldWidgetsAndProviders([...]) + FieldWidgetsAndProviders(...) Let's render the form:: diff --git a/src/z3c/form/datamanager.py b/src/z3c/form/datamanager.py index 84d963f..56bb86c 100644 --- a/src/z3c/form/datamanager.py +++ b/src/z3c/form/datamanager.py @@ -11,11 +11,7 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -"""Widget Framework Implementation - -$Id$ -""" -__docformat__ = "reStructuredText" +"""Widget Framework Implementation.""" import zope.component import zope.interface @@ -35,7 +31,7 @@ try: import persistent.mapping ALLOWED_DATA_CLASSES.append(persistent.mapping.PersistentMapping) -except ImportError: +except ModuleNotFoundError: pass diff --git a/src/z3c/form/field.py b/src/z3c/form/field.py index 8646417..91682aa 100644 --- a/src/z3c/form/field.py +++ b/src/z3c/form/field.py @@ -83,7 +83,7 @@ def __init__(self, field, name=None, prefix='', mode=None, interface=None, self.showDefault = showDefault def __repr__(self): - return '<{} {!r}>'.format(self.__class__.__name__, self.__name__) + return f'<{self.__class__.__name__} {self.__name__!r}>' @zope.interface.implementer(interfaces.IFields) diff --git a/src/z3c/form/field.rst b/src/z3c/form/field.rst index 0b6cc2e..23fabe7 100644 --- a/src/z3c/form/field.rst +++ b/src/z3c/form/field.rst @@ -529,7 +529,7 @@ When a widget is added to the widget manager, it is located: >>> lname.__name__ 'lastName' >>> lname.__parent__ - FieldWidgets([...]) + FieldWidgets(...) All widgets created by this widget manager are context aware: diff --git a/src/z3c/form/form.rst b/src/z3c/form/form.rst index 0629dc3..6eb936f 100644 --- a/src/z3c/form/form.rst +++ b/src/z3c/form/form.rst @@ -163,7 +163,7 @@ The widget manager is then stored in the ``widgets`` attribute as promised by the ``IForm`` interface: >>> addForm.widgets - FieldWidgets([...]) + FieldWidgets(...) The widget manager will have four widgets, one for each field: diff --git a/src/z3c/form/group.rst b/src/z3c/form/group.rst index ab854b5..d7769c6 100644 --- a/src/z3c/form/group.rst +++ b/src/z3c/form/group.rst @@ -191,7 +191,7 @@ Let's now submit the form, but forgetting to enter the address: >>> request = testing.TestRequest(form={ ... 'form.widgets.firstName': 'Stephan', ... 'form.widgets.lastName': 'Richter', - ... 'form.widgets.license': 'MA 40387', + ... 'form.widgets.license': 'MA 40487', ... 'form.widgets.model': 'BMW', ... 'form.widgets.make': '325', ... 'form.widgets.year': '2005', @@ -226,7 +226,7 @@ So what happens, if errors happen inside and outside a group? >>> request = testing.TestRequest(form={ ... 'form.widgets.firstName': 'Stephan', - ... 'form.widgets.license': 'MA 40387', + ... 'form.widgets.license': 'MA 40487', ... 'form.widgets.model': 'BMW', ... 'form.widgets.make': '325', ... 'form.widgets.year': '2005', @@ -271,7 +271,7 @@ Let's now successfully complete the add form. >>> request = testing.TestRequest(form={ ... 'form.widgets.firstName': 'Stephan', ... 'form.widgets.lastName': 'Richter', - ... 'form.widgets.license': 'MA 40387', + ... 'form.widgets.license': 'MA 40487', ... 'form.widgets.address': '10 Main St, Maynard, MA', ... 'form.widgets.model': 'BMW', ... 'form.widgets.make': '325', @@ -290,7 +290,7 @@ The object is now added to the container and all attributes should be set: >>> reg.lastName 'Richter' >>> reg.license - 'MA 40387' + 'MA 40487' >>> reg.address '10 Main St, Maynard, MA' >>> reg.model @@ -342,7 +342,7 @@ After updating the form, we can render the HTML: + value="MA 40487" />