diff --git a/Makefile b/Makefile index b94538b84..679df9fd6 100644 --- a/Makefile +++ b/Makefile @@ -144,7 +144,7 @@ docs-clean: ## Clean current and legacy docs build directories rm -rf docs/build $(BIN_FOLDER)/sphinx-autobuild $(BIN_FOLDER)/sphinx-build: $(BIN_FOLDER)/pip ## Install dependencies for building docs - $(BIN_FOLDER)/pip install -r requirements-docs.txt + $(BIN_FOLDER)/pip install -r requirements-docs.txt -r requirements.txt .PHONY: docs-livehtml docs-livehtml: $(BIN_FOLDER)/sphinx-autobuild ## Rebuild Sphinx documentation on changes, with live-reload in the browser diff --git a/docs/source/conf.py b/docs/source/conf.py index 08cebeb6f..f4f056d22 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -10,6 +10,8 @@ # All configuration values have a default; values that are commented out # serve to show the default. +from importlib.metadata import version + import datetime # If extensions (or modules to document with autodoc) are in another directory, @@ -34,10 +36,7 @@ # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. -# TODO: There must be a way to import this from `setup.py` so we don't have to -# update it manually for each release. -version = "9.7.2.dev0" -release = version +version = release = version("plone.restapi") # -- General configuration ---------------------------------------------------- diff --git a/news/4126.bugfix b/news/4126.bugfix new file mode 100644 index 000000000..488c5838b --- /dev/null +++ b/news/4126.bugfix @@ -0,0 +1 @@ +Replace `pkg_resources` with `importlib.metadata`/`importlib.resources`/`packaging` @gforcada diff --git a/setup.py b/setup.py index 43970a09b..a6c8d20fa 100644 --- a/setup.py +++ b/setup.py @@ -76,6 +76,7 @@ zip_safe=False, install_requires=[ "setuptools", + "packaging", "python-dateutil", "plone.rest", # json renderer moved to plone.restapi "plone.schema>=1.2.1", # new/fixed json field diff --git a/src/plone/restapi/__init__.py b/src/plone/restapi/__init__.py index c2cd6a1bb..5c71f91a9 100644 --- a/src/plone/restapi/__init__.py +++ b/src/plone/restapi/__init__.py @@ -2,17 +2,17 @@ from AccessControl import allow_module from AccessControl.Permissions import add_user_folders from importlib import import_module +from importlib.metadata import distribution +from importlib.metadata import PackageNotFoundError from plone.restapi.pas import plugin from Products.PluggableAuthService.PluggableAuthService import registerMultiPlugin from zope.i18nmessageid import MessageFactory -import pkg_resources - try: - pkg_resources.get_distribution("plone.app.multilingual") + distribution("plone.app.multilingual") HAS_MULTILINGUAL = True -except pkg_resources.DistributionNotFound: +except PackageNotFoundError: HAS_MULTILINGUAL = False _ = MessageFactory("plone.restapi") diff --git a/src/plone/restapi/services/addons/addons.py b/src/plone/restapi/services/addons/addons.py index 974b2398e..2e808390d 100644 --- a/src/plone/restapi/services/addons/addons.py +++ b/src/plone/restapi/services/addons/addons.py @@ -1,3 +1,5 @@ +from importlib.metadata import distribution +from importlib.metadata import PackageNotFoundError from plone.memoize import view from plone.restapi.bbb import INonInstallable from Products.CMFCore.utils import getToolByName @@ -7,7 +9,7 @@ from zope.i18n import translate import logging -import pkg_resources +import packaging try: @@ -228,9 +230,9 @@ def get_product_version(self, product_id): That implementation used to fall back to getting the version.txt. """ try: - dist = pkg_resources.get_distribution(product_id) + dist = distribution(product_id) return dist.version - except pkg_resources.DistributionNotFound: + except PackageNotFoundError: if "." in product_id: return "" # For CMFPlacefulWorkflow we need to try Products.CMFPlacefulWorkflow. @@ -247,7 +249,7 @@ def get_latest_upgrade_step(self, profile_id): available = self.ps.listUpgrades(profile_id, True) if available: # could return empty sequence latest = available[-1] - profile_version = max(latest["dest"], key=pkg_resources.parse_version) + profile_version = max(latest["dest"], key=packaging.version.parse) except Exception: pass return profile_version diff --git a/src/plone/restapi/services/querystringsearch/get.py b/src/plone/restapi/services/querystringsearch/get.py index 5e2bfee45..7cd280f06 100644 --- a/src/plone/restapi/services/querystringsearch/get.py +++ b/src/plone/restapi/services/querystringsearch/get.py @@ -1,5 +1,4 @@ -from pkg_resources import get_distribution -from pkg_resources import parse_version +from importlib.metadata import distribution from plone.restapi.bbb import IPloneSiteRoot from plone.restapi.deserializer import json_body from plone.restapi.deserializer import parse_int @@ -10,9 +9,11 @@ from zExceptions import BadRequest from zope.component import getMultiAdapter +import packaging -zcatalog_version = get_distribution("Products.ZCatalog").version -if parse_version(zcatalog_version) >= parse_version("5.1"): + +zcatalog_version = distribution("Products.ZCatalog").version +if packaging.version.parse(zcatalog_version) >= packaging.version.parse("5.1"): SUPPORT_NOT_UUID_QUERIES = True else: SUPPORT_NOT_UUID_QUERIES = False diff --git a/src/plone/restapi/services/system/get.py b/src/plone/restapi/services/system/get.py index 5f666a5de..265ef7597 100644 --- a/src/plone/restapi/services/system/get.py +++ b/src/plone/restapi/services/system/get.py @@ -1,3 +1,4 @@ +from importlib.metadata import distribution from plone.restapi.services import Service @@ -6,10 +7,8 @@ except ImportError: from plone.app.controlpanel.overview import OverviewControlPanel -import pkg_resources - -plone_restapi_version = pkg_resources.require("plone.restapi")[0].version +plone_restapi_version = distribution("plone.restapi").version class SystemGet(Service): diff --git a/src/plone/restapi/tests/test_content_delete.py b/src/plone/restapi/tests/test_content_delete.py index 15844ef59..98fa419e6 100644 --- a/src/plone/restapi/tests/test_content_delete.py +++ b/src/plone/restapi/tests/test_content_delete.py @@ -1,5 +1,5 @@ -from pkg_resources import get_distribution -from pkg_resources import parse_version +from importlib.metadata import distribution +from packaging.version import parse as version_parse from plone.app.testing import login from plone.app.testing import setRoles from plone.app.testing import SITE_OWNER_NAME @@ -14,8 +14,8 @@ import unittest -linkintegrity_version = get_distribution("plone.app.linkintegrity").version -if parse_version(linkintegrity_version) >= parse_version("3.0.dev0"): +linkintegrity_version = distribution("plone.app.linkintegrity").version +if version_parse(linkintegrity_version) >= version_parse("3.0.dev0"): NEW_LINKINTEGRITY = True else: NEW_LINKINTEGRITY = False diff --git a/src/plone/restapi/tests/test_documentation.py b/src/plone/restapi/tests/test_documentation.py index 466e308a8..c9d0acd09 100644 --- a/src/plone/restapi/tests/test_documentation.py +++ b/src/plone/restapi/tests/test_documentation.py @@ -1,7 +1,6 @@ from base64 import b64encode from datetime import datetime from datetime import timezone -from pkg_resources import resource_filename from plone import api from plone.app.discussion.interfaces import ICommentAddedEvent from plone.app.discussion.interfaces import IConversation @@ -49,6 +48,7 @@ import io import json import os +import pathlib import re import transaction import unittest @@ -74,8 +74,7 @@ RESPONSE_HEADER_KEYS = ["content-type", "allow", "location"] + TUS_HEADERS - -base_path = resource_filename("plone.restapi.tests", "http-examples") +base_path = str(pathlib.Path(__file__).parent / "http-examples") UPLOAD_DATA = b"abcdefgh" UPLOAD_MIMETYPE = b"text/plain" diff --git a/src/plone/restapi/tests/test_search.py b/src/plone/restapi/tests/test_search.py index 471340994..93ba90c50 100644 --- a/src/plone/restapi/tests/test_search.py +++ b/src/plone/restapi/tests/test_search.py @@ -1,7 +1,6 @@ from datetime import date from DateTime import DateTime -from pkg_resources import get_distribution -from pkg_resources import parse_version +from importlib.metadata import distribution from plone import api from plone.app.discussion.interfaces import IDiscussionSettings from plone.app.testing import SITE_OWNER_NAME @@ -22,13 +21,14 @@ from zope.interface import alsoProvides from zope.interface import noLongerProvides +import packaging import transaction import unittest -HAS_PLONE_6 = parse_version( - get_distribution("Products.CMFPlone").version -) >= parse_version("6.0.0a1") +HAS_PLONE_6 = packaging.version.parse( + distribution("Products.CMFPlone").version +) >= packaging.version.parse("6.0.0a1") class TestSearchFunctional(unittest.TestCase): diff --git a/src/plone/restapi/tests/test_serializer_catalog.py b/src/plone/restapi/tests/test_serializer_catalog.py index 67b0604ae..0e3339fd5 100644 --- a/src/plone/restapi/tests/test_serializer_catalog.py +++ b/src/plone/restapi/tests/test_serializer_catalog.py @@ -1,6 +1,5 @@ from DateTime import DateTime -from pkg_resources import get_distribution -from pkg_resources import parse_version +from importlib.metadata import distribution from plone.dexterity.utils import createContentInContainer from plone.restapi.interfaces import ISerializeToJson from plone.restapi.interfaces import ISerializeToJsonSummary @@ -9,12 +8,13 @@ from Products.CMFCore.utils import getToolByName from zope.component import getMultiAdapter +import packaging import unittest -HAS_PLONE_6 = parse_version( - get_distribution("Products.CMFPlone").version -) >= parse_version("6.0.0a1") +HAS_PLONE_6 = packaging.version.parse( + distribution("Products.CMFPlone").version +) >= packaging.version.parse("6.0.0a1") class TestCatalogSerializers(unittest.TestCase):