jenkins-bot submitted this change.

View Change


Approvals: Xqt: Looks good to me, approved jenkins-bot: Verified
replace pkg_resources with importlib.metadata and packaging

Bug: T340640
Change-Id: Ib4e9921b8721225bb477e9521ab337c30a0d8450
---
M pywikibot/tools/__init__.py
M setup.py
M pywikibot/scripts/wrapper.py
M pywikibot/backports.py
M .coveragerc
5 files changed, 60 insertions(+), 94 deletions(-)

diff --git a/.coveragerc b/.coveragerc
index 5e64004..dd6f136 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -8,7 +8,6 @@
except ImportError
except KeyboardInterrupt
except OSError
- except pkg_resources
except \w*ServerError
except SyntaxError
raise ImportError
diff --git a/pywikibot/backports.py b/pywikibot/backports.py
index 04a334e..2b2aa4e 100644
--- a/pywikibot/backports.py
+++ b/pywikibot/backports.py
@@ -184,6 +184,12 @@
from itertools import batched # type: ignore[no-redef]


+try:
+ import importlib.metadata as importlib_metadata
+except ImportError:
+ import importlib_metadata
+
+
# import ModuleDeprecationWrapper here to prevent circular import
from pywikibot.tools import ModuleDeprecationWrapper # noqa: E402

diff --git a/pywikibot/scripts/wrapper.py b/pywikibot/scripts/wrapper.py
index 5b08cc8..3b24e25 100755
--- a/pywikibot/scripts/wrapper.py
+++ b/pywikibot/scripts/wrapper.py
@@ -44,7 +44,6 @@
#
from __future__ import annotations

-#
import os
import sys
import types
@@ -59,7 +58,7 @@
site_package = False


-def check_pwb_versions(package):
+def check_pwb_versions(package: str):
"""Validate package version and scripts version.

Rules:
@@ -67,7 +66,8 @@
- Scripts version must not be older than previous Pywikibot version
due to deprecation policy
"""
- from pywikibot.tools import Version
+ from packaging.version import Version
+
scripts_version = Version(getattr(package, '__version__', pwb.__version__))
wikibot_version = Version(pwb.__version__)

@@ -218,19 +218,18 @@
print(f" pip install \"{str(requirement).partition(';')[0]}\"\n")


-def check_modules(script=None):
+def check_modules(script: str | None = None) -> bool:
"""Check whether mandatory modules are present.

This also checks Python version when importing dependencies from setup.py

:param script: The script name to be checked for dependencies
- :type script: str or None
:return: True if all dependencies are installed
- :rtype: bool
:raise RuntimeError: wrong Python version found in setup.py
"""
- import pkg_resources
+ from packaging.requirements import Requirement

+ from pywikibot.backports import importlib_metadata
from setup import script_deps

missing_requirements = []
@@ -240,35 +239,25 @@
dependencies = script_deps.get(Path(script).name, [])
else:
from setup import dependencies
- try:
- next(pkg_resources.parse_requirements(dependencies))
- except ValueError as e: # pragma: no cover
- # T286980: setuptools is too old and requirement parsing fails
- import setuptools
- setupversion = tuple(int(num)
- for num in setuptools.__version__.split('.'))
- if setupversion < (20, 8, 1):
- # print the minimal requirement
- _print_requirements(
- ['setuptools>=20.8.1'], None,
- f'outdated ({setuptools.__version__})')
- return False
- raise e

- for requirement in pkg_resources.parse_requirements(dependencies):
- if requirement.marker is None \
- or pkg_resources.evaluate_marker(str(requirement.marker)):
+ for dependency in dependencies:
+ requirement = Requirement(dependency)
+ if requirement.marker is None or requirement.marker.evaluate():
try:
- pkg_resources.resource_exists(requirement, requirement.name)
- except pkg_resources.DistributionNotFound as e:
+ instlld_vrsn = importlib_metadata.version(requirement.name)
+ except importlib_metadata.PackageNotFoundError as e:
missing_requirements.append(requirement)
print(e)
- except pkg_resources.VersionConflict as e:
- version_conflicts.append(requirement)
- print(e)
+ else:
+ if instlld_vrsn not in requirement.specifier:
+ version_conflicts.append(requirement)
+ print(
+ f'{requirement.name} version {instlld_vrsn} is '
+ f'installed but {requirement.specifier} is required'
+ )

- del pkg_resources
- del dependencies
+ del Requirement
+ del importlib_metadata
del script_deps

_print_requirements(missing_requirements, script, 'missing')
diff --git a/pywikibot/tools/__init__.py b/pywikibot/tools/__init__.py
index caa8763..088120c 100644
--- a/pywikibot/tools/__init__.py
+++ b/pywikibot/tools/__init__.py
@@ -35,15 +35,14 @@
import sys
from contextlib import suppress
from functools import total_ordering, wraps
-from importlib import import_module
from types import TracebackType
from typing import Any
from warnings import catch_warnings, showwarning, warn

-import pkg_resources
+import packaging.version

import pywikibot # T306760
-from pywikibot.backports import Callable
+from pywikibot.backports import Callable, importlib_metadata
from pywikibot.tools._deprecate import (
ModuleDeprecationWrapper,
add_decorated_full_name,
@@ -60,9 +59,6 @@
from pywikibot.tools._unidata import _first_upper_exception


-pkg_Version = pkg_resources.packaging.version.Version # noqa: N816
-
-
__all__ = (
# deprecating functions
'ModuleDeprecationWrapper',
@@ -89,7 +85,6 @@
'first_upper',
'strtobool',
'normalize_username',
- 'Version',
'MediaWikiVersion',
'open_archive',
'merge_unique_dicts',
@@ -117,7 +112,7 @@
return False


-def has_module(module, version=None) -> bool:
+def has_module(module: str, version: str | None = None) -> bool:
"""Check if a module can be imported.

.. versionadded:: 3.0
@@ -127,15 +122,13 @@
removed with Python 3.12.
"""
try:
- m = import_module(module)
- except ImportError:
+ metadata_version = importlib_metadata.version(module)
+ except importlib_metadata.PackageNotFoundError:
return False
if version:
- if not hasattr(m, '__version__'):
- return False # pragma: no cover

- required_version = pkg_resources.parse_version(version)
- module_version = pkg_resources.parse_version(m.__version__)
+ required_version = packaging.version.Version(version)
+ module_version = packaging.version.Version(metadata_version)

if module_version < required_version:
warn('Module version {} is lower than requested version {}'
@@ -404,36 +397,6 @@
return first_upper(username)


-class Version(pkg_Version):
-
- """Version from pkg_resouce vendor package.
-
- This Version provides propreties of vendor package 20.4 shipped with
- setuptools 49.4.0.
-
- .. versionadded:: 6.4
- """
-
- def __getattr__(self, name):
- """Provides propreties of vendor package 20.4."""
- if name in ('epoch', 'release', 'pre', ):
- return getattr(self._version, name)
- if name in ('post', 'dev'):
- attr = getattr(self._version, name)
- return attr[1] if attr else None
- if name == 'is_devrelease':
- return self.dev is not None
-
- parts = ('major', 'minor', 'micro')
- try:
- index = parts.index(name)
- except ValueError:
- raise AttributeError('{!r} object has to attribute {!r}'
- .format(type(self).__name__, name)) from None
- release = self.release
- return release[index] if len(release) >= index + 1 else 0
-
-
@total_ordering
class MediaWikiVersion:

diff --git a/setup.py b/setup.py
index c5da297..f3d0ec8 100755
--- a/setup.py
+++ b/setup.py
@@ -87,7 +87,9 @@
# ------- setup install_requires ------- #
# packages which are mandatory
dependencies = [
+ 'importlib_metadata ; python_version < "3.8"',
'mwparserfromhell>=0.5.2',
+ 'packaging',
'requests>=2.21.0',
# PEP 440
'setuptools>=48.0.0 ; python_version >= "3.10"',
@@ -129,7 +131,6 @@
and is not a developmental release.

:return: pywikibot module version string
- :rtype: str
"""
version = metadata.__version__
if 'sdist' not in sys.argv:
@@ -139,7 +140,8 @@
from contextlib import suppress
from subprocess import PIPE, run

- from pkg_resources import parse_version, safe_version
+ from packaging.version import InvalidVersion, Version
+
try:
tags = run(['git', 'tag'], check=True, stdout=PIPE,
universal_newlines=True).stdout.splitlines()
@@ -155,24 +157,21 @@

last_tag = tags[-1]

- warnings = []
- if parse_version(version) < parse_version('0'): # pragma: no cover
- # any version which is not a valid PEP 440 version will be considered
- # less than any valid PEP 440 version
- warnings.append(
- version + ' is not a valid version string following PEP 440.')
- elif safe_version(version) != version: # pragma: no cover
- warnings.append(f'{version} does not follow PEP 440. Use '
- f'{safe_version(version)} as version string instead.')
+ warning = ''
+ try:
+ vrsn = Version(version)
+ except InvalidVersion: # pragma: no cover
+ warning = f'{version} is not a valid version string following PEP 440.'
+ else:
+ if last_tag and vrsn <= Version(last_tag):
+ warning = ( # pragma: no cover
+ f'New version {version!r} is not higher than last version '
+ f'{last_tag!r}.'
+ )

- if last_tag and parse_version(version) <= parse_version(last_tag):
- warnings.append( # pragma: no cover
- f'New version {version!r} is not higher than last version '
- f'{last_tag!r}.')
-
- if warnings: # pragma: no cover
+ if warning: # pragma: no cover
print(__doc__)
- print('\n\n'.join(warnings))
+ print('\n\n{warning}')
sys.exit('\nBuild of distribution package canceled.')

return version

To view, visit change 985365. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Change-Id: Ib4e9921b8721225bb477e9521ab337c30a0d8450
Gerrit-Change-Number: 985365
Gerrit-PatchSet: 4
Gerrit-Owner: JJMC89 <JJMC89.Wikimedia@gmail.com>
Gerrit-Reviewer: Xqt <info@gno.de>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged