diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 674524b94f62a..a700ae1c8603f 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -8,6 +8,8 @@ import logging import operator import re +from itertools import zip_longest + import requests import threading @@ -17,7 +19,7 @@ from lxml import etree, html from psycopg2 import sql from werkzeug import urls -from werkzeug.datastructures import OrderedMultiDict +from werkzeug.datastructures import MultiDict from werkzeug.exceptions import NotFound from markupsafe import Markup @@ -1400,11 +1402,11 @@ def _get_canonical_url(self, canonical_params): def _is_canonical_url(self, canonical_params): """Returns whether the current request URL is canonical.""" self.ensure_one() - # Compare OrderedMultiDict because the order is important, there must be - # only one canonical and not params permutations. - params = request.httprequest.args - canonical_params = canonical_params or OrderedMultiDict() - if params != canonical_params: + params = request.httprequest.args.items(multi=True) + canonical = iter([]) if not canonical_params\ + else canonical_params.items(multi=True) if isinstance(canonical_params, MultiDict)\ + else canonical_params.items() + if any(a != b for a, b in zip_longest(params, canonical)): return False # Compare URL at the first routing iteration because it's the one with # the language in the path. It is important to also test the domain of diff --git a/addons/website_event/controllers/main.py b/addons/website_event/controllers/main.py index ad854157a3ce8..f73c9712fad42 100644 --- a/addons/website_event/controllers/main.py +++ b/addons/website_event/controllers/main.py @@ -10,7 +10,7 @@ from datetime import datetime, timedelta from dateutil.parser import parse from dateutil.relativedelta import relativedelta -from werkzeug.datastructures import OrderedMultiDict +from collections import Counter from werkzeug.exceptions import NotFound from odoo import fields, http, _ @@ -132,7 +132,7 @@ def events(self, page=1, **searches): if searches['date'] == 'old': # the only way to display this content is to set date=old so it must be canonical - values['canonical_params'] = OrderedMultiDict([('date', 'old')]) + values['canonical_params'] = {'date': 'old'} return request.render("website_event.index", values) diff --git a/odoo/__init__.py b/odoo/__init__.py index 08082c11060fe..8839369ed4a13 100644 --- a/odoo/__init__.py +++ b/odoo/__init__.py @@ -17,7 +17,7 @@ import sys MIN_PY_VERSION = (3, 7) -MAX_PY_VERSION = (3, 12) +MAX_PY_VERSION = (3, 13) assert sys.version_info > MIN_PY_VERSION, f"Outdated python version detected, Odoo requires Python >= {'.'.join(map(str, MIN_PY_VERSION))} to run." #---------------------------------------------------------- diff --git a/odoo/addons/test_http/tests/test_misc.py b/odoo/addons/test_http/tests/test_misc.py index 4c06f70d989d9..d48e846693167 100644 --- a/odoo/addons/test_http/tests/test_misc.py +++ b/odoo/addons/test_http/tests/test_misc.py @@ -1,6 +1,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import json +from importlib import metadata from io import StringIO from socket import gethostbyname from unittest.mock import patch @@ -14,12 +15,7 @@ from .test_common import TestHttpBase -try: - from importlib import metadata - werkzeug_version = metadata.version('werkzeug') -except ImportError: - import werkzeug - werkzeug_version = werkzeug.__version__ +werkzeug_version = metadata.version('werkzeug') @tagged('post_install', '-at_install') diff --git a/odoo/http.py b/odoo/http.py index 68c0eb5b6b121..c0980da87cc19 100644 --- a/odoo/http.py +++ b/odoo/http.py @@ -117,6 +117,7 @@ import glob import hashlib import hmac +import importlib.metadata import inspect import json import logging @@ -169,7 +170,6 @@ from .tools.geoipresolver import GeoIPResolver from .tools.facade import Proxy, ProxyAttr, ProxyFunc from .tools.func import filter_kwargs, lazy_property -from .tools.mimetypes import guess_mimetype from .tools.misc import pickle from .tools._vendor import sessions from .tools._vendor.useragents import UserAgent @@ -257,7 +257,7 @@ def get_default_session(): 'alias', 'host', 'methods', } -if parse_version(werkzeug.__version__) >= parse_version('2.0.2'): +if parse_version(importlib.metadata.version('werkzeug')) >= parse_version('2.0.2'): # Werkzeug 2.0.2 adds the websocket option. If a websocket request # (ws/wss) is trying to access an HTTP route, a WebsocketMismatch # exception is raised. On the other hand, Werkzeug 0.16 does not diff --git a/requirements.txt b/requirements.txt index 0fe1f8d584b39..32319b5344314 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,24 +1,33 @@ # The officially supported versions of the following packages are their # python3-* equivalent distributed in Ubuntu 22.04 and Debian 11 -Babel==2.9.1 # min version = 2.6.0 (Focal with security backports) -chardet==4.0.0 +Babel==2.9.1 ; python_version < '3.11' # min version = 2.6.0 (Focal with security backports) +Babel==2.10.3 ; python_version >= '3.11' and python_version < '3.13' +Babel==2.17.0 ; python_version >= '3.13' +chardet==4.0.0 ; python_version < '3.11' # (Jammy) +chardet==5.2.0 ; python_version >= '3.11' cryptography==3.4.8; python_version < '3.12' # incompatibility between pyopenssl 19.0.0 and cryptography>=37.0.0 cryptography==42.0.8 ; python_version >= '3.12' # (Noble) min 41.0.7, pinning 42.0.8 for security fixes -decorator==4.4.2 -docutils==0.16 +decorator==4.4.2 ; python_version < '3.11' # (Jammy) +decorator==5.1.1 ; python_version >= '3.11' +docutils==0.16 ; python_version < '3.11' # (Jammy) +docutils==0.20.1 ; python_version >= '3.11' ebaysdk==2.1.5 freezegun==0.3.11; python_version < '3.8' -freezegun==0.3.15; python_version >= '3.8' +freezegun==0.3.15; python_version >= '3.8' and python_version < '3.11' +freezegun==1.2.1 ; python_version >= '3.11' and python_version < '3.13' +freezegun==1.5.1 ; python_version >= '3.13' gevent==1.5.0 ; sys_platform != 'win32' and python_version == '3.7' gevent==20.9.0 ; sys_platform != 'win32' and python_version > '3.7' and python_version <= '3.9' gevent==21.8.0 ; sys_platform != 'win32' and python_version > '3.9' and python_version <= '3.10' # (Jammy) gevent==22.10.2; sys_platform != 'win32' and python_version > '3.10'and python_version < '3.12' # (Jammy) -gevent==24.2.1 ; sys_platform != 'win32' and python_version >= '3.12' # (Noble) +gevent==24.2.1 ; sys_platform != 'win32' and python_version >= '3.12' and python_version < '3.13' # (Noble) +gevent==24.11.1 ; sys_platform != 'win32' and python_version >= '3.13' # (Trixie) greenlet==0.4.15 ; sys_platform != 'win32' and python_version == '3.7' greenlet==0.4.17 ; sys_platform != 'win32' and python_version > '3.7' and python_version <= '3.9' greenlet==1.1.2 ; sys_platform != 'win32' and python_version > '3.9' and python_version <= '3.10' # (Jammy) greenlet==2.0.2 ; sys_platform != 'win32' and python_version > '3.10' and python_version < '3.12' # (Jammy) -greenlet==3.0.3 ; sys_platform != 'win32' and python_version >= '3.12' # (Noble) +greenlet==3.0.3 ; sys_platform != 'win32' and python_version >= '3.12' and python_version < '3.13' # (Noble) +greenlet==3.1.1 ; sys_platform != 'win32' and python_version >= '3.13' # (Trixie) idna==2.10 Jinja2==2.11.3 ; python_version <= '3.10' # min version = 2.10.1 (Focal - with security backports) Jinja2==3.1.2 ; python_version > '3.10' @@ -37,7 +46,8 @@ ofxparse==0.21; python_version > '3.9' # (Jammy) passlib==1.7.4 # min version = 1.7.2 (Focal with security backports) Pillow==9.0.1 ; python_version <= '3.10' # min version = 7.0.0 (Focal with security backports) Pillow==9.4.0 ; python_version > '3.10' and python_version < '3.12' -Pillow==10.2.0 ; python_version >= '3.12' # (Noble) Mostly to have a wheel package +Pillow==10.2.0 ; python_version >= '3.12' and python_version < '3.13' # (Noble) Mostly to have a wheel package +Pillow==11.1.0 ; python_version >= '3.13' # (Noble) Mostly to have a wheel package polib==1.1.0 psutil==5.8.0 ; python_version <= '3.10' psutil==5.9.4 ; python_version > '3.10' and python_version < '3.12' @@ -47,7 +57,8 @@ psycopg2==2.8.6 ; sys_platform != 'win32' and python_version >= '3.8' and python psycopg2==2.8.6 ; sys_platform == 'win32' and python_version < '3.10' # psycopg2==2.9.2 ; python_version == '3.10' psycopg2==2.9.5 ; python_version >= '3.11' and python_version < '3.12' -psycopg2==2.9.9 ; python_version >= '3.12' # (Noble) Mostly to have a wheel package +psycopg2==2.9.9 ; python_version >= '3.12' and python_version < '3.13' # (Noble) +psycopg2==2.9.10 ; python_version >= '3.13' # (Trixie) pydot==1.4.2 pyopenssl==20.0.1; python_version < '3.12' pyopenssl==24.1.0 ; python_version >= '3.12' # (Noble) min 23.2.0, pinned for compatibility with cryptography==42.0.8 and security patches @@ -79,4 +90,6 @@ xlrd==1.1.0; python_version < '3.8' xlrd==1.2.0; python_version >= '3.8' XlsxWriter==1.1.2 xlwt==1.3.0 -zeep==4.0.0 +zeep==4.1.0 ; python_version < '3.11' # (jammy) +zeep==4.2.1 ; python_version >= '3.11' and python_version < '3.13' +zeep==4.3.1 ; python_version >= '3.13'