Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-flask-restx for openSUSE:Factory checked in at 2026-04-09 16:09:38 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-flask-restx (Old) and /work/SRC/openSUSE:Factory/.python-flask-restx.new.21863 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-flask-restx" Thu Apr 9 16:09:38 2026 rev:15 rq:1345331 version:1.3.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-flask-restx/python-flask-restx.changes 2025-04-10 21:58:54.331740935 +0200 +++ /work/SRC/openSUSE:Factory/.python-flask-restx.new.21863/python-flask-restx.changes 2026-04-09 16:22:43.305519685 +0200 @@ -1,0 +2,19 @@ +Thu Apr 9 06:00:40 UTC 2026 - Steve Kowalik <[email protected]> + +- Update to 1.3.2: + * Fix for "EmailTest.test_invalid_values_check" unit test + * Set minimum Python version to 3.9 + * fix code block error + * Replace pytz with zoneinfo / datetime.timezone + * Fix testing for Flask 3.1.0 changes + * Move from jsonschema.RefResolver to the new referencing library + * A fix to allow nullable fields.nested for input validation + * Added a thread lock to guard first-time schema construction +- Drop patches, merged upstream: + * Fix-testing-with-flask.patch + * Replace-pytz-with-zoneinfo-datetime-timezone.patch +- Rebased flask-restx-importlib.patch, submit upstream. +- Add patch support-python-314.patch: + * Support Python 3.14 functools.partial() changes. + +------------------------------------------------------------------- Old: ---- 1.3.0.tar.gz Fix-testing-with-flask.patch Replace-pytz-with-zoneinfo-datetime-timezone.patch New: ---- 1.3.2.tar.gz support-python-314.patch ----------(Old B)---------- Old:- Drop patches, merged upstream: * Fix-testing-with-flask.patch * Replace-pytz-with-zoneinfo-datetime-timezone.patch Old: * Fix-testing-with-flask.patch * Replace-pytz-with-zoneinfo-datetime-timezone.patch - Rebased flask-restx-importlib.patch, submit upstream. ----------(Old E)---------- ----------(New B)---------- New:- Rebased flask-restx-importlib.patch, submit upstream. - Add patch support-python-314.patch: * Support Python 3.14 functools.partial() changes. ----------(New E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-flask-restx.spec ++++++ --- /var/tmp/diff_new_pack.f5Xd65/_old 2026-04-09 16:22:43.917544798 +0200 +++ /var/tmp/diff_new_pack.f5Xd65/_new 2026-04-09 16:22:43.921544962 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-flask-restx # -# Copyright (c) 2025 SUSE LLC +# Copyright (c) 2026 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,32 +17,30 @@ Name: python-flask-restx -Version: 1.3.0 +Version: 1.3.2 Release: 0 Summary: Framework for fast, easy and documented API development with Flask License: BSD-3-Clause URL: https://github.com/python-restx/flask-restx Source: https://github.com/python-restx/flask-restx/archive/%{version}.tar.gz -# PATCH-FIX-UPSTREAM https://github.com/python-restx/flask-restx/pull/622 -Patch0: Replace-pytz-with-zoneinfo-datetime-timezone.patch -Patch1: Fix-testing-with-flask.patch -Patch2: flask-restx-importlib.patch +# PATCH-FIX-UPSTREAM gh#python-restx/flask-restx#648 +Patch0: flask-restx-importlib.patch +# PATCH-FIX-UPSTREAM gh#python-restx/flask-restx#649 +Patch1: support-python-314.patch BuildRequires: %{python_module Faker} BuildRequires: %{python_module Flask} BuildRequires: %{python_module Werkzeug} BuildRequires: %{python_module aniso8601} -BuildRequires: %{python_module base >= 3.8} +BuildRequires: %{python_module base >= 3.9} BuildRequires: %{python_module blinker} -BuildRequires: %{python_module importlib_resources if %python-base < 3.9} BuildRequires: %{python_module jsonschema} BuildRequires: %{python_module pip} BuildRequires: %{python_module pytest-benchmark} BuildRequires: %{python_module pytest-flask} BuildRequires: %{python_module pytest-mock} BuildRequires: %{python_module pytest} -BuildRequires: %{python_module q} +BuildRequires: %{python_module referencing} BuildRequires: %{python_module setuptools} -BuildRequires: %{python_module tzlocal} BuildRequires: %{python_module wheel} BuildRequires: fdupes BuildRequires: python-rpm-macros @@ -50,9 +48,7 @@ Requires: python-Werkzeug Requires: python-aniso8601 Requires: python-jsonschema -%if %{python_version_nodots} < 39 -Requires: python-importlib_resources -%endif +Requires: python-referencing BuildArch: noarch %python_subpackages ++++++ 1.3.0.tar.gz -> 1.3.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/.github/workflows/release.yml new/flask-restx-1.3.2/.github/workflows/release.yml --- old/flask-restx-1.3.0/.github/workflows/release.yml 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/.github/workflows/release.yml 2025-09-23 22:33:47.000000000 +0200 @@ -7,10 +7,10 @@ build: runs-on: ubuntu-latest steps: - - name: Set up Python 3.8 + - name: Set up Python 3.9 uses: actions/setup-python@v1 with: - python-version: 3.8 + python-version: 3.9 - name: Checkout code uses: actions/checkout@v2 - name: Install dependencies diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/.github/workflows/test.yml new/flask-restx-1.3.2/.github/workflows/test.yml --- old/flask-restx-1.3.0/.github/workflows/test.yml 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/.github/workflows/test.yml 2025-09-23 22:33:47.000000000 +0200 @@ -15,7 +15,7 @@ strategy: fail-fast: false matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "pypy3.8", "3.12"] + python-version: ["3.9", "3.10", "3.11", "pypy3.9", "3.12"] flask: ["<3.0.0", ">=3.0.0"] steps: - name: Set up Python ${{ matrix.python-version }} @@ -41,10 +41,10 @@ runs-on: ubuntu-latest if: github.event_name == 'pull_request' steps: - - name: Set up Python 3.8 + - name: Set up Python 3.9 uses: actions/setup-python@v4 with: - python-version: "3.8" + python-version: "3.9" - name: Checkout ${{ github.base_ref }} uses: actions/checkout@v3 with: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/CHANGELOG.rst new/flask-restx-1.3.2/CHANGELOG.rst --- old/flask-restx-1.3.0/CHANGELOG.rst 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/CHANGELOG.rst 2025-09-23 22:33:47.000000000 +0200 @@ -25,6 +25,20 @@ some info. If you see your contribution missing info, please open a PR on the Changelog! +.. _section-1.3.1: +1.3.1 +----- +.. _bug_fixes-1.3.1 +Bug Fixes +~~~~~~~~~ + +:: + + * Add python version requirement on setup.py (#586) [jason-the-j] + * Add a thread lock to avoid concurrent schema construction. (#545) [peter-doggart] + * Fix Nested field schema generation for nullable fields. (#638) [peter-doggart] + * Fix reference resolution for definitions in schema. (#553) [peter-doggart] + .. _section-1.3.0: 1.3.0 ----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/README.rst new/flask-restx-1.3.2/README.rst --- old/flask-restx-1.3.0/README.rst 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/README.rst 2025-09-23 22:33:47.000000000 +0200 @@ -2,14 +2,14 @@ Flask RESTX =========== -.. image:: https://github.com/python-restx/flask-restx/workflows/Tests/badge.svg?tag=1.3.0&event=push +.. image:: https://github.com/python-restx/flask-restx/workflows/Tests/badge.svg?tag=1.3.2&event=push :target: https://github.com/python-restx/flask-restx/actions?query=workflow%3ATests :alt: Tests status .. image:: https://codecov.io/gh/python-restx/flask-restx/branch/master/graph/badge.svg :target: https://codecov.io/gh/python-restx/flask-restx :alt: Code coverage -.. image:: https://readthedocs.org/projects/flask-restx/badge/?version=1.3.0 - :target: https://flask-restx.readthedocs.io/en/1.3.0/ +.. image:: https://readthedocs.org/projects/flask-restx/badge/?version=1.3.2 + :target: https://flask-restx.readthedocs.io/en/1.3.2/ :alt: Documentation status .. image:: https://img.shields.io/pypi/l/flask-restx.svg :target: https://pypi.org/project/flask-restx @@ -38,7 +38,7 @@ Compatibility ============= -Flask-RESTX requires Python 3.8+. +Flask-RESTX requires Python 3.9+. On Flask Compatibility ====================== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/bumpr.rc new/flask-restx-1.3.2/bumpr.rc --- old/flask-restx-1.3.0/bumpr.rc 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/bumpr.rc 2025-09-23 22:33:47.000000000 +0200 @@ -4,7 +4,7 @@ commit = true tag = true push = true -tests = tox -e py38 +tests = tox -e py39 clean = inv clean files = diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/doc/index.rst new/flask-restx-1.3.2/doc/index.rst --- old/flask-restx-1.3.0/doc/index.rst 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/doc/index.rst 2025-09-23 22:33:47.000000000 +0200 @@ -33,7 +33,7 @@ Compatibility ============= -Flask-RESTX requires Python 3.8+. +Flask-RESTX requires Python 3.9+. Installation diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/doc/installation.rst new/flask-restx-1.3.2/doc/installation.rst --- old/flask-restx-1.3.0/doc/installation.rst 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/doc/installation.rst 2025-09-23 22:33:47.000000000 +0200 @@ -20,5 +20,5 @@ pip install -e .[dev,test] -Flask-RESTX requires Python version 3.8+. +Flask-RESTX requires Python version 3.9+. It's also working with PyPy and PyPy3. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/doc/scaling.rst new/flask-restx-1.3.2/doc/scaling.rst --- old/flask-restx-1.3.0/doc/scaling.rst 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/doc/scaling.rst 2025-09-23 22:33:47.000000000 +0200 @@ -243,7 +243,7 @@ from .apis.namespaceX import api as nsX blueprint = Blueprint('api', __name__, url_prefix='/api/1') - api = Api(blueprint + api = Api(blueprint, title='My Title', version='1.0', description='A description', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/examples/zoo_app/requirements.txt new/flask-restx-1.3.2/examples/zoo_app/requirements.txt --- old/flask-restx-1.3.0/examples/zoo_app/requirements.txt 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/examples/zoo_app/requirements.txt 2025-09-23 22:33:47.000000000 +0200 @@ -8,7 +8,6 @@ jsonschema==3.2.0 MarkupSafe==2.0.1 pyrsistent==0.17.3 -pytz==2021.1 six==1.16.0 Werkzeug==2.2.3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/flask_restx/__about__.py new/flask-restx-1.3.2/flask_restx/__about__.py --- old/flask-restx-1.3.0/flask_restx/__about__.py 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/flask_restx/__about__.py 2025-09-23 22:33:47.000000000 +0200 @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -__version__ = "1.3.0" +__version__ = "1.3.2" __description__ = ( "Fully featured framework for fast, easy and documented API development with Flask" ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/flask_restx/api.py new/flask-restx-1.3.2/flask_restx/api.py --- old/flask-restx-1.3.0/flask_restx/api.py 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/flask_restx/api.py 2025-09-23 22:33:47.000000000 +0200 @@ -2,6 +2,7 @@ import inspect from itertools import chain import logging +import threading import operator import re import sys @@ -16,7 +17,7 @@ from flask.signals import got_request_exception -from jsonschema import RefResolver +from referencing import Registry from werkzeug.utils import cached_property from werkzeug.datastructures import Headers @@ -133,7 +134,7 @@ format_checker=None, url_scheme=None, default_swagger_filename="swagger.json", - **kwargs + **kwargs, ): self.version = version self.title = title or "API" @@ -161,6 +162,7 @@ } ) self._schema = None + self._schema_lock = threading.Lock() self.models = {} self._refresolver = None self.format_checker = format_checker @@ -567,14 +569,17 @@ :returns dict: the schema as a serializable dict """ if not self._schema: - try: - self._schema = Swagger(self).as_dict() - except Exception: - # Log the source exception for debugging purpose - # and return an error message - msg = "Unable to render schema" - log.exception(msg) # This will provide a full traceback - return {"error": msg} + # Guard schema initialization to avoid concurrent construction on first access + with self._schema_lock: + if not self._schema: + try: + self._schema = Swagger(self).as_dict() + except Exception: + # Log the source exception for debugging purpose + # and return an error message + msg = "Unable to render schema" + log.exception(msg) # This will provide a full traceback + return {"error": msg} return self._schema @property @@ -825,7 +830,44 @@ @property def refresolver(self): if not self._refresolver: - self._refresolver = RefResolver.from_schema(self.__schema__) + # Create a registry that can resolve references within our schema + registry = Registry() + schema = self.__schema__ + + # If schema has definitions, register it + if "definitions" in schema: + schema_id = schema.get("$id", "http://localhost/schema.json") + registry = registry.with_resource(schema_id, schema) + else: + # If no definitions in schema, register all models individually + for name, model in self.models.items(): + model_schema = model.__schema__ + # Add $id to the model schema so it can be referenced + if "$id" not in model_schema: + model_schema = model_schema.copy() + model_schema["$id"] = ( + f"http://localhost/schema.json#/definitions/{name}" + ) + registry = registry.with_resource( + f"http://localhost/schema.json#/definitions/{name}", + model_schema, + ) + + # Also register the root schema with definitions + if self.models: + definitions = {} + for name, model in self.models.items(): + definitions[name] = model.__schema__ + + schema_with_definitions = { + "$id": "http://localhost/schema.json", + "definitions": definitions, + } + registry = registry.with_resource( + "http://localhost/schema.json", schema_with_definitions + ) + + self._refresolver = registry return self._refresolver @staticmethod @@ -861,7 +903,7 @@ "%s.%s" % (blueprint_setup.blueprint.name, endpoint), view_func, defaults=defaults, - **options + **options, ) def _deferred_blueprint_init(self, setup_state): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/flask_restx/fields.py new/flask-restx-1.3.2/flask_restx/fields.py --- old/flask-restx-1.3.0/flask_restx/fields.py 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/flask_restx/fields.py 2025-09-23 22:33:47.000000000 +0200 @@ -134,6 +134,9 @@ :param bool readonly: Is the field read only ? (for documentation purpose) :param example: An optional data example (for documentation purpose) :param callable mask: An optional mask function to be applied to output + :param bool nullable: Whether the field accepts null values in input + validation. When True, the generated JSON Schema will allow null + values for this field during request payload validation. """ #: The JSON/Swagger schema type @@ -153,6 +156,7 @@ readonly=None, example=None, mask=None, + nullable=None, **kwargs ): self.attribute = attribute @@ -163,6 +167,7 @@ self.readonly = readonly self.example = example if example is not None else self.__schema_example__ self.mask = mask + self.nullable = nullable def format(self, value): """ @@ -284,6 +289,19 @@ schema["allOf"] = allOf else: schema["$ref"] = ref + + # If nullable is True, wrap using anyOf to permit nulls for input validation + if self.nullable: + # Remove structural keys that conflict with anyOf composition + for key in ("$ref", "allOf", "type", "items"): + schema.pop(key, None) + # Create anyOf with the original schema and null type + anyOf = [{"$ref": ref}] + if self.as_list: + anyOf = [{"type": "array", "items": {"$ref": ref}}] + anyOf.append({"type": "null"}) + schema["anyOf"] = anyOf + return schema def clone(self, mask=None): @@ -333,9 +351,11 @@ return [ self.container.output( idx, - val - if (isinstance(val, dict) or is_attr(val)) and not is_nested - else value, + ( + val + if (isinstance(val, dict) or is_attr(val)) and not is_nested + else value + ), ) for idx, val in enumerate(value) ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/flask_restx/inputs.py new/flask-restx-1.3.2/flask_restx/inputs.py --- old/flask-restx-1.3.0/flask_restx/inputs.py 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/flask_restx/inputs.py 2025-09-23 22:33:47.000000000 +0200 @@ -19,16 +19,15 @@ import re import socket -from datetime import datetime, time, timedelta +from datetime import datetime, time, timedelta, timezone from email.utils import parsedate_tz, mktime_tz from urllib.parse import urlparse import aniso8601 -import pytz # Constants for upgrading date-based intervals to full datetimes. -START_OF_DAY = time(0, 0, 0, tzinfo=pytz.UTC) -END_OF_DAY = time(23, 59, 59, 999999, tzinfo=pytz.UTC) +START_OF_DAY = time(0, 0, 0, tzinfo=timezone.utc) +END_OF_DAY = time(23, 59, 59, 999999, tzinfo=timezone.utc) netloc_regex = re.compile( @@ -338,11 +337,11 @@ end = datetime.combine(end, START_OF_DAY) if start.tzinfo is None: - start = pytz.UTC.localize(start) - end = pytz.UTC.localize(end) + start = start.replace(tzinfo=timezone.utc) + end = end.replace(tzinfo=timezone.utc) else: - start = start.astimezone(pytz.UTC) - end = end.astimezone(pytz.UTC) + start = start.astimezone(timezone.utc) + end = end.astimezone(timezone.utc) return start, end @@ -424,11 +423,11 @@ start, end = _normalize_interval(start, end, value) - except ValueError: + except ValueError as e: msg = ( "Invalid {arg}: {value}. {arg} must be a valid ISO8601 date/time interval." ) - raise ValueError(msg.format(arg=argument, value=value)) + raise ValueError(msg.format(arg=argument, value=value)) from e return start, end @@ -559,9 +558,9 @@ timetuple = parsedate_tz(value) timestamp = mktime_tz(timetuple) if timetuple[-1] is None: - return datetime.fromtimestamp(timestamp).replace(tzinfo=pytz.utc) + return datetime.fromtimestamp(timestamp).replace(tzinfo=timezone.utc) else: - return datetime.fromtimestamp(timestamp, pytz.utc) + return datetime.fromtimestamp(timestamp, timezone.utc) except Exception: raise ValueError('Invalid date literal "{0}"'.format(raw)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/flask_restx/marshalling.py new/flask-restx-1.3.2/flask_restx/marshalling.py --- old/flask-restx-1.3.0/flask_restx/marshalling.py 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/flask_restx/marshalling.py 2025-09-23 22:33:47.000000000 +0200 @@ -171,9 +171,11 @@ return (key, value) items = ( - (k, marshal(data, v, skip_none=skip_none, ordered=ordered)) - if isinstance(v, dict) - else __format_field(k, v) + ( + (k, marshal(data, v, skip_none=skip_none, ordered=ordered)) + if isinstance(v, dict) + else __format_field(k, v) + ) for k, v in fields.items() ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/flask_restx/model.py new/flask-restx-1.3.2/flask_restx/model.py --- old/flask-restx-1.3.0/flask_restx/model.py 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/flask_restx/model.py 2025-09-23 22:33:47.000000000 +0200 @@ -11,6 +11,7 @@ from .errors import abort from jsonschema import Draft4Validator +from jsonschema.validators import validator_for from jsonschema.exceptions import ValidationError from .utils import not_none @@ -89,9 +90,48 @@ return model def validate(self, data, resolver=None, format_checker=None): - validator = Draft4Validator( - self.__schema__, resolver=resolver, format_checker=format_checker - ) + # For backward compatibility, resolver can be either a RefResolver or a Registry + if resolver is not None and hasattr(resolver, "resolve"): + # Old RefResolver - convert to registry + registry = None + validator = Draft4Validator( + self.__schema__, resolver=resolver, format_checker=format_checker + ) + else: + # New Registry or None + # If we have a registry, we need to create a schema that includes definitions + schema_to_validate = self.__schema__ + if resolver is not None: + # Check if the schema has $ref that need to be resolved + import json + + schema_str = json.dumps(self.__schema__) + if '"$ref"' in schema_str: + # Create a schema with inline definitions from the registry + definitions = {} + for uri in resolver: + resource = resolver[uri] + if isinstance(resource, dict) and "definitions" in resource: + definitions.update(resource["definitions"]) + + if definitions: + # Create a new schema that includes the definitions + schema_to_validate = { + "$id": "http://localhost/schema.json", + "definitions": definitions, + **self.__schema__, + } + + ValidatorClass = validator_for(schema_to_validate) + if resolver is not None: + validator = ValidatorClass( + schema_to_validate, registry=resolver, format_checker=format_checker + ) + else: + validator = ValidatorClass( + schema_to_validate, format_checker=format_checker + ) + try: validator.validate(data) except ValidationError: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/flask_restx/namespace.py new/flask-restx-1.3.2/flask_restx/namespace.py --- old/flask-restx-1.3.0/flask_restx/namespace.py 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/flask_restx/namespace.py 2025-09-23 22:33:47.000000000 +0200 @@ -253,9 +253,11 @@ def wrapper(func): doc = { "responses": { - str(code): (description, [fields], kwargs) - if as_list - else (description, fields, kwargs) + str(code): ( + (description, [fields], kwargs) + if as_list + else (description, fields, kwargs) + ) }, "__mask__": kwargs.get( "mask", True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/flask_restx/schemas/__init__.py new/flask-restx-1.3.2/flask_restx/schemas/__init__.py --- old/flask-restx-1.3.0/flask_restx/schemas/__init__.py 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/flask_restx/schemas/__init__.py 2025-09-23 22:33:47.000000000 +0200 @@ -4,6 +4,7 @@ .. versionadded:: 0.12.1 """ + import io import json diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/package.json new/flask-restx-1.3.2/package.json --- old/flask-restx-1.3.0/package.json 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/package.json 2025-09-23 22:33:47.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "flask-restx", - "version": "1.3.0", + "version": "1.3.1", "description": "Fully featured framework for fast, easy and documented API development with Flask", "repository": "python-restx/flask-restx", "keywords": [ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/requirements/install.pip new/flask-restx-1.3.2/requirements/install.pip --- old/flask-restx-1.3.0/requirements/install.pip 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/requirements/install.pip 2025-09-23 22:33:47.000000000 +0200 @@ -1,6 +1,6 @@ aniso8601>=0.82 jsonschema +referencing Flask>=0.8, !=2.0.0 werkzeug!=2.0.0 -pytz importlib_resources diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/requirements/test.pip new/flask-restx-1.3.2/requirements/test.pip --- old/flask-restx-1.3.0/requirements/test.pip 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/requirements/test.pip 2025-09-23 22:33:47.000000000 +0200 @@ -7,7 +7,7 @@ pytest-flask==1.3.0 pytest-mock==3.6.1 pytest-profiling==1.7.0 -tzlocal invoke==2.2.0 twine==3.8.0 setuptools +backports.zoneinfo;python_version<"3.9" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/setup.py new/flask-restx-1.3.2/setup.py --- old/flask-restx-1.3.0/setup.py 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/setup.py 2025-09-23 22:33:47.000000000 +0200 @@ -102,7 +102,6 @@ "Topic :: System :: Software Distribution", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -111,4 +110,5 @@ "Topic :: Software Development :: Libraries :: Python Modules", "License :: OSI Approved :: BSD License", ], + python_requires=">=3.9", ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/tasks.py new/flask-restx-1.3.2/tasks.py --- old/flask-restx-1.3.0/tasks.py 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/tasks.py 2025-09-23 22:33:47.000000000 +0200 @@ -125,9 +125,11 @@ "--benchmark-max-time={0}".format(max_time), "--benchmark-autosave" if save else None, "--benchmark-compare" if compare else None, - "--benchmark-histogram=histograms/{0:%Y%m%d-%H%M%S}".format(ts) - if histogram - else None, + ( + "--benchmark-histogram=histograms/{0:%Y%m%d-%H%M%S}".format(ts) + if histogram + else None + ), "--benchmark-cprofile=tottime" if profile else None, ) cmd = "pytest tests/benchmarks {0}".format(kwargs) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/tests/conftest.py new/flask-restx-1.3.2/tests/conftest.py --- old/flask-restx-1.3.0/tests/conftest.py 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/tests/conftest.py 2025-09-23 22:33:47.000000000 +0200 @@ -44,7 +44,7 @@ @pytest.fixture def app(): - app = Flask(__name__) + app = Flask(__name__, subdomain_matching=True) app.test_client_class = TestClient yield app diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/tests/test_fields.py new/flask-restx-1.3.2/tests/test_fields.py --- old/flask-restx-1.3.0/tests/test_fields.py 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/tests/test_fields.py 2025-09-23 22:33:47.000000000 +0200 @@ -1,9 +1,13 @@ +try: + import zoneinfo +except ImportError: + from backports import zoneinfo + from collections import OrderedDict -from datetime import date, datetime +from datetime import date, datetime, timezone from decimal import Decimal from functools import partial -import pytz import pytest from flask import Blueprint @@ -538,11 +542,11 @@ (datetime(2011, 1, 1), "Sat, 01 Jan 2011 00:00:00 -0000"), (datetime(2011, 1, 1, 23, 59, 59), "Sat, 01 Jan 2011 23:59:59 -0000"), ( - datetime(2011, 1, 1, 23, 59, 59, tzinfo=pytz.utc), + datetime(2011, 1, 1, 23, 59, 59, tzinfo=timezone.utc), "Sat, 01 Jan 2011 23:59:59 -0000", ), ( - datetime(2011, 1, 1, 23, 59, 59, tzinfo=pytz.timezone("CET")), + datetime(2011, 1, 1, 23, 59, 59, tzinfo=zoneinfo.ZoneInfo("CET")), "Sat, 01 Jan 2011 22:59:59 -0000", ), ], @@ -558,15 +562,15 @@ (datetime(2011, 1, 1, 23, 59, 59), "2011-01-01T23:59:59"), (datetime(2011, 1, 1, 23, 59, 59, 1000), "2011-01-01T23:59:59.001000"), ( - datetime(2011, 1, 1, 23, 59, 59, tzinfo=pytz.utc), + datetime(2011, 1, 1, 23, 59, 59, tzinfo=timezone.utc), "2011-01-01T23:59:59+00:00", ), ( - datetime(2011, 1, 1, 23, 59, 59, 1000, tzinfo=pytz.utc), + datetime(2011, 1, 1, 23, 59, 59, 1000, tzinfo=timezone.utc), "2011-01-01T23:59:59.001000+00:00", ), ( - datetime(2011, 1, 1, 23, 59, 59, tzinfo=pytz.timezone("CET")), + datetime(2011, 1, 1, 23, 59, 59, tzinfo=zoneinfo.ZoneInfo("CET")), "2011-01-01T23:59:59+01:00", ), ], @@ -673,10 +677,10 @@ (datetime(2011, 1, 1), "2011-01-01"), (datetime(2011, 1, 1, 23, 59, 59), "2011-01-01"), (datetime(2011, 1, 1, 23, 59, 59, 1000), "2011-01-01"), - (datetime(2011, 1, 1, 23, 59, 59, tzinfo=pytz.utc), "2011-01-01"), - (datetime(2011, 1, 1, 23, 59, 59, 1000, tzinfo=pytz.utc), "2011-01-01"), + (datetime(2011, 1, 1, 23, 59, 59, tzinfo=timezone.utc), "2011-01-01"), + (datetime(2011, 1, 1, 23, 59, 59, 1000, tzinfo=timezone.utc), "2011-01-01"), ( - datetime(2011, 1, 1, 23, 59, 59, tzinfo=pytz.timezone("CET")), + datetime(2011, 1, 1, 23, 59, 59, tzinfo=zoneinfo.ZoneInfo("CET")), "2011-01-01", ), ], @@ -877,6 +881,17 @@ assert field.allow_null assert field.__schema__ == {"$ref": "#/definitions/NestedModel"} + def test_with_nullable_schema(self, api): + nested_fields = api.model("NestedModel", {"name": fields.String}) + field = fields.Nested(nested_fields, nullable=True) + # Should allow null in schema via anyOf + assert field.__schema__ == { + "anyOf": [ + {"$ref": "#/definitions/NestedModel"}, + {"type": "null"}, + ] + } + def test_with_skip_none(self, api): nested_fields = api.model("NestedModel", {"name": fields.String}) field = fields.Nested(nested_fields, skip_none=True) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/tests/test_inputs.py new/flask-restx-1.3.2/tests/test_inputs.py --- old/flask-restx-1.3.0/tests/test_inputs.py 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/tests/test_inputs.py 2025-09-23 22:33:47.000000000 +0200 @@ -1,8 +1,7 @@ import re -import pytz import pytest -from datetime import date, datetime +from datetime import date, datetime, timezone from flask_restx import inputs @@ -37,18 +36,18 @@ "value,expected", [ ("2011-01-01", datetime(2011, 1, 1)), - ("2011-01-01T00:00:00+00:00", datetime(2011, 1, 1, tzinfo=pytz.utc)), + ("2011-01-01T00:00:00+00:00", datetime(2011, 1, 1, tzinfo=timezone.utc)), ( "2011-01-01T23:59:59+00:00", - datetime(2011, 1, 1, 23, 59, 59, tzinfo=pytz.utc), + datetime(2011, 1, 1, 23, 59, 59, tzinfo=timezone.utc), ), ( "2011-01-01T23:59:59.001000+00:00", - datetime(2011, 1, 1, 23, 59, 59, 1000, tzinfo=pytz.utc), + datetime(2011, 1, 1, 23, 59, 59, 1000, tzinfo=timezone.utc), ), ( "2011-01-01T23:59:59+02:00", - datetime(2011, 1, 1, 21, 59, 59, tzinfo=pytz.utc), + datetime(2011, 1, 1, 21, 59, 59, tzinfo=timezone.utc), ), ], ) @@ -70,22 +69,28 @@ @pytest.mark.parametrize( "value,expected", [ - ("Sat, 01 Jan 2011", datetime(2011, 1, 1, tzinfo=pytz.utc)), - ("Sat, 01 Jan 2011 00:00", datetime(2011, 1, 1, tzinfo=pytz.utc)), - ("Sat, 01 Jan 2011 00:00:00", datetime(2011, 1, 1, tzinfo=pytz.utc)), - ("Sat, 01 Jan 2011 00:00:00 +0000", datetime(2011, 1, 1, tzinfo=pytz.utc)), - ("Sat, 01 Jan 2011 00:00:00 -0000", datetime(2011, 1, 1, tzinfo=pytz.utc)), + ("Sat, 01 Jan 2011", datetime(2011, 1, 1, tzinfo=timezone.utc)), + ("Sat, 01 Jan 2011 00:00", datetime(2011, 1, 1, tzinfo=timezone.utc)), + ("Sat, 01 Jan 2011 00:00:00", datetime(2011, 1, 1, tzinfo=timezone.utc)), + ( + "Sat, 01 Jan 2011 00:00:00 +0000", + datetime(2011, 1, 1, tzinfo=timezone.utc), + ), + ( + "Sat, 01 Jan 2011 00:00:00 -0000", + datetime(2011, 1, 1, tzinfo=timezone.utc), + ), ( "Sat, 01 Jan 2011 23:59:59 -0000", - datetime(2011, 1, 1, 23, 59, 59, tzinfo=pytz.utc), + datetime(2011, 1, 1, 23, 59, 59, tzinfo=timezone.utc), ), ( "Sat, 01 Jan 2011 21:00:00 +0200", - datetime(2011, 1, 1, 19, 0, 0, tzinfo=pytz.utc), + datetime(2011, 1, 1, 19, 0, 0, tzinfo=timezone.utc), ), ( "Sat, 01 Jan 2011 21:00:00 -0200", - datetime(2011, 1, 1, 23, 0, 0, tzinfo=pytz.utc), + datetime(2011, 1, 1, 23, 0, 0, tzinfo=timezone.utc), ), ], ) @@ -653,7 +658,7 @@ @pytest.mark.parametrize( "value", [ - "[email protected]", + "[email protected]", "me@localhost", "[email protected]", "[email protected]", @@ -985,136 +990,136 @@ # Full precision with explicit UTC. "2013-01-01T12:30:00Z/P1Y2M3DT4H5M6S", ( - datetime(2013, 1, 1, 12, 30, 0, tzinfo=pytz.utc), - datetime(2014, 3, 5, 16, 35, 6, tzinfo=pytz.utc), + datetime(2013, 1, 1, 12, 30, 0, tzinfo=timezone.utc), + datetime(2014, 3, 5, 16, 35, 6, tzinfo=timezone.utc), ), ), ( # Full precision with alternate UTC indication "2013-01-01T12:30+00:00/P2D", ( - datetime(2013, 1, 1, 12, 30, 0, tzinfo=pytz.utc), - datetime(2013, 1, 3, 12, 30, 0, tzinfo=pytz.utc), + datetime(2013, 1, 1, 12, 30, 0, tzinfo=timezone.utc), + datetime(2013, 1, 3, 12, 30, 0, tzinfo=timezone.utc), ), ), ( # Implicit UTC with time "2013-01-01T15:00/P1M", ( - datetime(2013, 1, 1, 15, 0, 0, tzinfo=pytz.utc), - datetime(2013, 1, 31, 15, 0, 0, tzinfo=pytz.utc), + datetime(2013, 1, 1, 15, 0, 0, tzinfo=timezone.utc), + datetime(2013, 1, 31, 15, 0, 0, tzinfo=timezone.utc), ), ), ( # TZ conversion "2013-01-01T17:00-05:00/P2W", ( - datetime(2013, 1, 1, 22, 0, 0, tzinfo=pytz.utc), - datetime(2013, 1, 15, 22, 0, 0, tzinfo=pytz.utc), + datetime(2013, 1, 1, 22, 0, 0, tzinfo=timezone.utc), + datetime(2013, 1, 15, 22, 0, 0, tzinfo=timezone.utc), ), ), ( # Date upgrade to midnight-midnight period "2013-01-01/P3D", ( - datetime(2013, 1, 1, 0, 0, 0, tzinfo=pytz.utc), - datetime(2013, 1, 4, 0, 0, 0, 0, tzinfo=pytz.utc), + datetime(2013, 1, 1, 0, 0, 0, tzinfo=timezone.utc), + datetime(2013, 1, 4, 0, 0, 0, 0, tzinfo=timezone.utc), ), ), ( # Start/end with UTC "2013-01-01T12:00:00Z/2013-02-01T12:00:00Z", ( - datetime(2013, 1, 1, 12, 0, 0, tzinfo=pytz.utc), - datetime(2013, 2, 1, 12, 0, 0, tzinfo=pytz.utc), + datetime(2013, 1, 1, 12, 0, 0, tzinfo=timezone.utc), + datetime(2013, 2, 1, 12, 0, 0, tzinfo=timezone.utc), ), ), ( # Start/end with time upgrade "2013-01-01/2013-06-30", ( - datetime(2013, 1, 1, tzinfo=pytz.utc), - datetime(2013, 6, 30, tzinfo=pytz.utc), + datetime(2013, 1, 1, tzinfo=timezone.utc), + datetime(2013, 6, 30, tzinfo=timezone.utc), ), ), ( # Start/end with TZ conversion "2013-02-17T12:00:00-07:00/2013-02-28T15:00:00-07:00", ( - datetime(2013, 2, 17, 19, 0, 0, tzinfo=pytz.utc), - datetime(2013, 2, 28, 22, 0, 0, tzinfo=pytz.utc), + datetime(2013, 2, 17, 19, 0, 0, tzinfo=timezone.utc), + datetime(2013, 2, 28, 22, 0, 0, tzinfo=timezone.utc), ), ), ( # Resolution expansion for single date(time) # Second with UTC "2013-01-01T12:30:45Z", ( - datetime(2013, 1, 1, 12, 30, 45, tzinfo=pytz.utc), - datetime(2013, 1, 1, 12, 30, 46, tzinfo=pytz.utc), + datetime(2013, 1, 1, 12, 30, 45, tzinfo=timezone.utc), + datetime(2013, 1, 1, 12, 30, 46, tzinfo=timezone.utc), ), ), ( # Second with tz conversion "2013-01-01T12:30:45+02:00", ( - datetime(2013, 1, 1, 10, 30, 45, tzinfo=pytz.utc), - datetime(2013, 1, 1, 10, 30, 46, tzinfo=pytz.utc), + datetime(2013, 1, 1, 10, 30, 45, tzinfo=timezone.utc), + datetime(2013, 1, 1, 10, 30, 46, tzinfo=timezone.utc), ), ), ( # Second with implicit UTC "2013-01-01T12:30:45", ( - datetime(2013, 1, 1, 12, 30, 45, tzinfo=pytz.utc), - datetime(2013, 1, 1, 12, 30, 46, tzinfo=pytz.utc), + datetime(2013, 1, 1, 12, 30, 45, tzinfo=timezone.utc), + datetime(2013, 1, 1, 12, 30, 46, tzinfo=timezone.utc), ), ), ( # Minute with UTC "2013-01-01T12:30+00:00", ( - datetime(2013, 1, 1, 12, 30, tzinfo=pytz.utc), - datetime(2013, 1, 1, 12, 31, tzinfo=pytz.utc), + datetime(2013, 1, 1, 12, 30, tzinfo=timezone.utc), + datetime(2013, 1, 1, 12, 31, tzinfo=timezone.utc), ), ), ( # Minute with conversion "2013-01-01T12:30+04:00", ( - datetime(2013, 1, 1, 8, 30, tzinfo=pytz.utc), - datetime(2013, 1, 1, 8, 31, tzinfo=pytz.utc), + datetime(2013, 1, 1, 8, 30, tzinfo=timezone.utc), + datetime(2013, 1, 1, 8, 31, tzinfo=timezone.utc), ), ), ( # Minute with implicit UTC "2013-01-01T12:30", ( - datetime(2013, 1, 1, 12, 30, tzinfo=pytz.utc), - datetime(2013, 1, 1, 12, 31, tzinfo=pytz.utc), + datetime(2013, 1, 1, 12, 30, tzinfo=timezone.utc), + datetime(2013, 1, 1, 12, 31, tzinfo=timezone.utc), ), ), ( # Hour, explicit UTC "2013-01-01T12Z", ( - datetime(2013, 1, 1, 12, tzinfo=pytz.utc), - datetime(2013, 1, 1, 13, tzinfo=pytz.utc), + datetime(2013, 1, 1, 12, tzinfo=timezone.utc), + datetime(2013, 1, 1, 13, tzinfo=timezone.utc), ), ), ( # Hour with offset "2013-01-01T12-07:00", ( - datetime(2013, 1, 1, 19, tzinfo=pytz.utc), - datetime(2013, 1, 1, 20, tzinfo=pytz.utc), + datetime(2013, 1, 1, 19, tzinfo=timezone.utc), + datetime(2013, 1, 1, 20, tzinfo=timezone.utc), ), ), ( # Hour with implicit UTC "2013-01-01T12", ( - datetime(2013, 1, 1, 12, tzinfo=pytz.utc), - datetime(2013, 1, 1, 13, tzinfo=pytz.utc), + datetime(2013, 1, 1, 12, tzinfo=timezone.utc), + datetime(2013, 1, 1, 13, tzinfo=timezone.utc), ), ), ( @@ -1122,8 +1127,8 @@ # be accepted. "2013-01-01T12:00:00.0/2013-01-01T12:30:00.000000", ( - datetime(2013, 1, 1, 12, tzinfo=pytz.utc), - datetime(2013, 1, 1, 12, 30, tzinfo=pytz.utc), + datetime(2013, 1, 1, 12, tzinfo=timezone.utc), + datetime(2013, 1, 1, 12, 30, tzinfo=timezone.utc), ), ), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flask-restx-1.3.0/tox.ini new/flask-restx-1.3.2/tox.ini --- old/flask-restx-1.3.0/tox.ini 2023-12-10 15:48:18.000000000 +0100 +++ new/flask-restx-1.3.2/tox.ini 2025-09-23 22:33:47.000000000 +0200 @@ -5,9 +5,9 @@ [tox] envlist = - py{38, 39, 310, 311}-flask2, + py{39, 310, 311}-flask2, py{311, 312}-flask3 - pypy3.8 + pypy3.9 doc [testenv] ++++++ flask-restx-importlib.patch ++++++ --- /var/tmp/diff_new_pack.f5Xd65/_old 2026-04-09 16:22:44.161554811 +0200 +++ /var/tmp/diff_new_pack.f5Xd65/_new 2026-04-09 16:22:44.165554975 +0200 @@ -1,36 +1,46 @@ -Index: flask-restx-1.3.0/flask_restx/schemas/__init__.py -=================================================================== ---- flask-restx-1.3.0.orig/flask_restx/schemas/__init__.py -+++ flask-restx-1.3.0/flask_restx/schemas/__init__.py -@@ -7,7 +7,10 @@ and allows to validate specs against the +commit b61314fcae641ef56c9041551b31a9427bedd00f +Author: Steve Kowalik <[email protected]> +Date: Thu Apr 9 15:37:51 2026 +1000 + + Replace importlib_resources with stdlib + + Now that Python 3.9 is the minimum version supported, we can switch + to using the stdlib importlib.resources, and drop one external + dependency. + +diff --git a/flask_restx/schemas/__init__.py b/flask_restx/schemas/__init__.py +index 84e4db1..1ef21de 100644 +--- a/flask_restx/schemas/__init__.py ++++ b/flask_restx/schemas/__init__.py +@@ -5,11 +5,10 @@ and allows to validate specs against them. + .. versionadded:: 0.12.1 + """ + ++import importlib.resources import io import json -import importlib_resources -+try: -+ from importlib.resources import files as importlib_resources_files -+except ImportError: -+ from importlib_resources import files as importlib_resources_files - +- from collections.abc import Mapping from jsonschema import Draft4Validator -@@ -57,7 +60,7 @@ class LazySchema(Mapping): + +@@ -58,7 +57,7 @@ class LazySchema(Mapping): def _load(self): if not self._schema: - ref = importlib_resources.files(__name__) / self.filename -+ ref = importlib_resources_files(__name__) / self.filename ++ ref = importlib.resources.files(__name__) / self.filename with io.open(ref) as infile: self._schema = json.load(infile) -Index: flask-restx-1.3.0/requirements/install.pip -=================================================================== ---- flask-restx-1.3.0.orig/requirements/install.pip -+++ flask-restx-1.3.0/requirements/install.pip -@@ -2,4 +2,4 @@ aniso8601>=0.82 - jsonschema +diff --git a/requirements/install.pip b/requirements/install.pip +index f24652b..6001624 100644 +--- a/requirements/install.pip ++++ b/requirements/install.pip +@@ -3,4 +3,3 @@ jsonschema + referencing Flask>=0.8, !=2.0.0 werkzeug!=2.0.0 -importlib_resources -+importlib_resources;python_version<"3.9" ++++++ support-python-314.patch ++++++ >From f35d23dba69f71aae24f9b77694bf701b438a11e Mon Sep 17 00:00:00 2001 From: Steve Kowalik <[email protected]> Date: Thu, 9 Apr 2026 15:46:25 +1000 Subject: [PATCH] Support Python 3.14 partial() changes functools.partial() was changed in Python 3.14 to be a method descriptor, which means we need to wrap it in staticmethod now. This change does appear to be backward compatible, so I haven't guarded it with a version check. --- tests/test_fields.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_fields.py b/tests/test_fields.py index 9f255c5c..b57f83c6 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -693,7 +693,7 @@ def test_unsupported_value_format(self): class FormatedStringFieldTest(StringTestMixin, BaseFieldTestMixin, FieldTestCase): - field_class = partial(fields.FormattedString, "Hello {name}") + field_class = staticmethod(partial(fields.FormattedString, "Hello {name}")) def test_defaults(self): field = fields.FormattedString("Hello {name}") @@ -731,7 +731,7 @@ def test_tuple(self): class UrlFieldTest(StringTestMixin, BaseFieldTestMixin, FieldTestCase): - field_class = partial(fields.Url, "endpoint") + field_class = staticmethod(partial(fields.Url, "endpoint")) def test_defaults(self): field = fields.Url("endpoint") @@ -931,7 +931,7 @@ def test_as_list_is_reusable(self, api): class ListFieldTest(BaseFieldTestMixin, FieldTestCase): - field_class = partial(fields.List, fields.String) + field_class = staticmethod(partial(fields.List, fields.String)) def test_defaults(self): field = fields.List(fields.String) @@ -1025,7 +1025,7 @@ def test_list_of_raw(self): class WildcardFieldTest(BaseFieldTestMixin, FieldTestCase): - field_class = partial(fields.Wildcard, fields.String) + field_class = staticmethod(partial(fields.Wildcard, fields.String)) def test_types(self): with pytest.raises(fields.MarshallingError):
