From 6b775393e9d20c3c4971ec207ab71dfdf0f987cc Mon Sep 17 00:00:00 2001 From: "John T. Wodder II" Date: Mon, 26 Jun 2023 13:50:36 -0400 Subject: [PATCH 1/2] Eliminate uses of `pkg_resources` --- datalad/cli/parser.py | 29 +++++++++++++---------- datalad/local/run_procedure.py | 35 ++++++++++++++++------------ datalad/support/external_versions.py | 25 +++++++++++++------- setup.py | 1 + 4 files changed, 55 insertions(+), 35 deletions(-) diff --git a/datalad/cli/parser.py b/datalad/cli/parser.py index 4d78bbb02b..b3c44026ca 100644 --- a/datalad/cli/parser.py +++ b/datalad/cli/parser.py @@ -11,29 +11,29 @@ # like error handling, must be done conditionally in-line. import argparse +import logging +import sys from collections import defaultdict from functools import partial -import sys - from datalad import __version__ - -from .common_args import common_args from datalad.interface.base import ( - is_api_arg, get_cmd_doc, get_interface_groups, + is_api_arg, load_interface, ) +from datalad.support.constraints import EnsureChoice from datalad.utils import getargspec + +from .common_args import common_args +from .exec import call_from_parser +from .helpers import get_commands_from_groups from .interface import ( alter_interface_docs_for_cmdline, get_cmd_ex, get_cmdline_command_name, ) -from datalad.support.constraints import EnsureChoice -from .helpers import get_commands_from_groups -from .exec import call_from_parser # special case imports # .helpers import add_entrypoints_to_interface_groups @@ -43,7 +43,6 @@ # .interface._known_extension_commands # .interface._deprecated_commands -import logging lgr = logging.getLogger('datalad.cli.parser') @@ -138,6 +137,7 @@ def setup_parser( # we need the full help, or we have a potential command that # lives in an extension, must load all extension, expensive from .helpers import add_entrypoints_to_interface_groups + # need to load all the extensions and try again # TODO load extensions one-by-one and stop when a command was found add_entrypoints_to_interface_groups(interface_groups) @@ -404,8 +404,8 @@ def try_suggest_extension_with_command(parser, cmd, completing, known_cmds): """If completing=False, this function will trigger sys.exit()""" # check if might be coming from known extensions from .interface import ( - _known_extension_commands, _deprecated_commands, + _known_extension_commands, ) extension_commands = { c: e @@ -541,8 +541,13 @@ def print_version(): # Let's use the standard Python mechanism if underlying module # did not provide __version__ try: - import pkg_resources - version = pkg_resources.get_distribution(mod_name).version + if sys.version_info < (3, 10): + import importlib_metadata as im + else: + import importlib.metadata as im + + pkg = im.packages_distributions()[mod_name][0] + version = im.version(pkg) except Exception: version = "unknown" if include_name: diff --git a/datalad/local/run_procedure.py b/datalad/local/run_procedure.py index 3c3051534b..610b9c0bf0 100644 --- a/datalad/local/run_procedure.py +++ b/datalad/local/run_procedure.py @@ -48,6 +48,17 @@ split_cmdline, ) +if sys.version_info < (3, 9): + from importlib_resources import ( + as_file, + files, + ) +else: + from importlib.resources import ( + as_file, + files, + ) + lgr = logging.getLogger('datalad.local.run_procedures') @@ -148,23 +159,17 @@ def _get_procedure_implementation(name='*', ds=None): # 3. check extensions for procedure from datalad.support.entrypoints import iter_entrypoints - # delay heavy import until here - from pkg_resources import ( - resource_filename, - resource_isdir, - ) + for epname, epmodule, _ in iter_entrypoints('datalad.extensions'): - # use of '/' here is OK wrt to platform compatibility - if resource_isdir(epmodule, 'resources/procedures'): - for m, n in _get_file_match( - resource_filename(epmodule, 'resources/procedures'), - name): - yield (m, n,) + _get_proc_config(n) + res = files(epmodule) / "resources" / "procedures" + if res.is_dir(): + with as_file(res) as p: + for m, n in _get_file_match(p, name): + yield (m, n,) + _get_proc_config(n) # 4. at last check datalad itself for procedure - for m, n in _get_file_match( - resource_filename('datalad', 'resources/procedures'), - name): - yield (m, n,) + _get_proc_config(n) + with as_file(files("datalad") / "resources" / "procedures") as p: + for m, n in _get_file_match(p, name): + yield (m, n,) + _get_proc_config(n) def _guess_exec(script_file): diff --git a/datalad/support/external_versions.py b/datalad/support/external_versions.py index 36c1797b1e..3abe25cf6c 100644 --- a/datalad/support/external_versions.py +++ b/datalad/support/external_versions.py @@ -8,18 +8,19 @@ # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """Module to help maintain a registry of versions for external modules etc """ +import os.path as op import re import sys -import os.path as op +from itertools import chain from os import linesep -from itertools import chain from looseversion import LooseVersion -from datalad.log import lgr # import version helper from config to have only one implementation # config needs this to avoid circular imports from datalad.config import get_git_version as __get_git_version +from datalad.log import lgr + from .exceptions import ( CapturedException, CommandError, @@ -46,14 +47,15 @@ def __cmp__(self, other): # Custom handlers # from datalad.cmd import ( - WitlessRunner, GitWitlessRunner, StdOutErrCapture, + WitlessRunner, ) from datalad.support.exceptions import ( MissingExternalDependency, OutdatedExternalDependency, ) + _runner = WitlessRunner() _git_runner = GitWitlessRunner() @@ -156,7 +158,10 @@ def get_rsync_version(): # that of the debian package it's installed with. Reason is in gh-7320, # which results in the need to detect a patched-by-ubuntu version of rsync # and therefore the package version, not the result of `rsync --version`. - from datalad.utils import on_linux, get_linux_distribution + from datalad.utils import ( + get_linux_distribution, + on_linux, + ) if on_linux: dist = get_linux_distribution()[0] if dist in ['debian', 'ubuntu']: @@ -239,12 +244,16 @@ def _deduce_version(klass, value): version = getattr(value, attr) break - # try pkg_resources + # try importlib.metadata if version is None and hasattr(value, '__name__'): pkg_name = klass._PYTHON_PACKAGES.get(value.__name__, value.__name__) try: - import pkg_resources - version = pkg_resources.get_distribution(pkg_name).version + if sys.version_info < (3, 10): + import importlib_metadata as im + else: + import importlib.metadata as im + + version = im.version(pkg_name) except Exception: pass diff --git a/setup.py b/setup.py index b0becce7bf..0f211bad61 100755 --- a/setup.py +++ b/setup.py @@ -29,6 +29,7 @@ 'colorama; platform_system=="Windows"', 'distro; python_version >= "3.8"', 'importlib-metadata >=3.6; python_version < "3.10"', + 'importlib-resources >= 3.0; python_version < "3.9"', 'iso8601', 'humanize', 'fasteners>=0.14', From c888fae4429f407d4b2e0f25698ce9a526709d18 Mon Sep 17 00:00:00 2001 From: DataLad Bot Date: Mon, 26 Jun 2023 17:51:57 +0000 Subject: [PATCH 2/2] [release-action] Autogenerate changelog snippet for PR 7439 --- changelog.d/pr-7439.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog.d/pr-7439.md diff --git a/changelog.d/pr-7439.md b/changelog.d/pr-7439.md new file mode 100644 index 0000000000..ae7291ab2d --- /dev/null +++ b/changelog.d/pr-7439.md @@ -0,0 +1,3 @@ +### 🏠 Internal + +- Eliminate uses of `pkg_resources`. Fixes [#7435](https://github.com/datalad/datalad/issues/7435) via [PR #7439](https://github.com/datalad/datalad/pull/7439) (by [@jwodder](https://github.com/jwodder))