From ea55396cc4df42720b8557a13f4fd80283fc32e8 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 13 Apr 2024 11:46:42 +0200 Subject: [PATCH 1/9] Apply ruff/refurb rule (FURB105) FURB105 Unnecessary empty string passed to `print` --- distutils/dist.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/distutils/dist.py b/distutils/dist.py index f29a34faba..668ce7eb0a 100644 --- a/distutils/dist.py +++ b/distutils/dist.py @@ -647,7 +647,7 @@ def _show_help( options = self.global_options parser.set_option_table(options) parser.print_help(self.common_usage + "\nGlobal options:") - print('') + print() if display_options: parser.set_option_table(self.display_options) @@ -655,7 +655,7 @@ def _show_help( "Information display options (just display " + "information, ignore any commands)" ) - print('') + print() for command in self.commands: if isinstance(command, type) and issubclass(command, Command): @@ -669,7 +669,7 @@ def _show_help( else: parser.set_option_table(klass.user_options) parser.print_help("Options for '%s' command:" % klass.__name__) - print('') + print() print(gen_usage(self.script_name)) @@ -686,7 +686,7 @@ def handle_display_options(self, option_order): # we ignore "foo bar"). if self.help_commands: self.print_commands() - print('') + print() print(gen_usage(self.script_name)) return 1 From 0d6794fdc2987703982f7d0e89123fffc9bbda79 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 13 Apr 2024 11:48:29 +0200 Subject: [PATCH 2/9] Apply ruff/refurb rule (FURB129) FURB129 Instead of calling `readlines()`, iterate over file object directly --- distutils/tests/test_msvc9compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distutils/tests/test_msvc9compiler.py b/distutils/tests/test_msvc9compiler.py index 58e24f017a..6f6aabee4d 100644 --- a/distutils/tests/test_msvc9compiler.py +++ b/distutils/tests/test_msvc9compiler.py @@ -161,7 +161,7 @@ def test_remove_visual_c_ref(self): f = open(manifest) try: # removing trailing spaces - content = '\n'.join([line.rstrip() for line in f.readlines()]) + content = '\n'.join([line.rstrip() for line in f]) finally: f.close() From bfadc24bc9c120a6feae918cea5a9d80453cc8c6 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 13 Apr 2024 11:50:52 +0200 Subject: [PATCH 3/9] Apply ruff/refurb rule (FURB142) FURB142 Use of `set.add()` in a for loop --- distutils/dir_util.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/distutils/dir_util.py b/distutils/dir_util.py index 2021bed82e..8a3aca6521 100644 --- a/distutils/dir_util.py +++ b/distutils/dir_util.py @@ -95,8 +95,7 @@ def create_tree(base_dir, files, mode=0o777, verbose=1, dry_run=0): """ # First get the list of directories to create need_dir = set() - for file in files: - need_dir.add(os.path.join(base_dir, os.path.dirname(file))) + need_dir.update(os.path.join(base_dir, os.path.dirname(file)) for file in files) # Now create them for dir in sorted(need_dir): From ec303d5963920fb8e6fce5919615fcffb0c93fe5 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 13 Apr 2024 11:53:21 +0200 Subject: [PATCH 4/9] Apply ruff/refurb rule (FURB140) FURB140 Use `itertools.starmap` instead of the generator --- distutils/unixccompiler.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/distutils/unixccompiler.py b/distutils/unixccompiler.py index a1fe2b57a2..caf4cd338e 100644 --- a/distutils/unixccompiler.py +++ b/distutils/unixccompiler.py @@ -389,10 +389,7 @@ def find_library_file(self, dirs, lib, debug=0): roots = map(self._library_root, dirs) - searched = ( - os.path.join(root, lib_name) - for root, lib_name in itertools.product(roots, lib_names) - ) + searched = itertools.starmap(os.path.join, itertools.product(roots, lib_names)) found = filter(os.path.exists, searched) From 8b9f35e00549615b43793efd3c90f75739b55abf Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 13 Apr 2024 09:31:27 -0400 Subject: [PATCH 5/9] Construct the set in one expression. --- distutils/dir_util.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/distutils/dir_util.py b/distutils/dir_util.py index 8a3aca6521..370c6ffd49 100644 --- a/distutils/dir_util.py +++ b/distutils/dir_util.py @@ -94,8 +94,7 @@ def create_tree(base_dir, files, mode=0o777, verbose=1, dry_run=0): 'dry_run' flags are as for 'mkpath()'. """ # First get the list of directories to create - need_dir = set() - need_dir.update(os.path.join(base_dir, os.path.dirname(file)) for file in files) + need_dir = set(os.path.join(base_dir, os.path.dirname(file)) for file in files) # Now create them for dir in sorted(need_dir): From a04913a51327c64f807e85119fd750485bbceb0a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 13 Apr 2024 13:33:48 -0400 Subject: [PATCH 6/9] Add type declaration for runtime_library_dir_option, making explicit the different return types one might expect. --- distutils/unixccompiler.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/distutils/unixccompiler.py b/distutils/unixccompiler.py index caf4cd338e..a54481c01b 100644 --- a/distutils/unixccompiler.py +++ b/distutils/unixccompiler.py @@ -13,6 +13,8 @@ * link shared library handled by 'cc -shared' """ +from __future__ import annotations + import itertools import os import re @@ -281,7 +283,7 @@ def _is_gcc(self): compiler = os.path.basename(shlex.split(cc_var)[0]) return "gcc" in compiler or "g++" in compiler - def runtime_library_dir_option(self, dir): + def runtime_library_dir_option(self, dir: str) -> str | list[str]: # XXX Hackish, at the very least. See Python bug #445902: # https://bugs.python.org/issue445902 # Linkers on different platforms need different options to From d2581bf30b6cfaa64f8b570b368a6f4ed5a710ff Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 13 Apr 2024 13:47:03 -0400 Subject: [PATCH 7/9] Add 'consolidate_linker_args' wrapper to protect the old behavior for now. Closes pypa/distutils#246. --- distutils/compat/__init__.py | 15 +++++++++++++++ distutils/compat/py38.py | 23 +++++++++++++++++++++++ distutils/tests/test_unixccompiler.py | 17 +++++++++-------- distutils/unixccompiler.py | 5 +++-- 4 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 distutils/compat/__init__.py create mode 100644 distutils/compat/py38.py diff --git a/distutils/compat/__init__.py b/distutils/compat/__init__.py new file mode 100644 index 0000000000..b7be72678f --- /dev/null +++ b/distutils/compat/__init__.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +from .py38 import removeprefix + + +def consolidate_linker_args(args: list[str]) -> str: + """ + Ensure the return value is a string for backward compatibility. + + Retain until at least 2024-10-31. + """ + + if not all(arg.startswith('-Wl,') for arg in args): + return args + return '-Wl,' + ','.join(removeprefix(arg, '-Wl,') for arg in args) diff --git a/distutils/compat/py38.py b/distutils/compat/py38.py new file mode 100644 index 0000000000..0af3814017 --- /dev/null +++ b/distutils/compat/py38.py @@ -0,0 +1,23 @@ +import sys + +if sys.version_info < (3, 9): + + def removesuffix(self, suffix): + # suffix='' should not call self[:-0]. + if suffix and self.endswith(suffix): + return self[: -len(suffix)] + else: + return self[:] + + def removeprefix(self, prefix): + if self.startswith(prefix): + return self[len(prefix) :] + else: + return self[:] +else: + + def removesuffix(self, suffix): + return self.removesuffix(suffix) + + def removeprefix(self, prefix): + return self.removeprefix(prefix) diff --git a/distutils/tests/test_unixccompiler.py b/distutils/tests/test_unixccompiler.py index f17edf2f6b..6f05fa6989 100644 --- a/distutils/tests/test_unixccompiler.py +++ b/distutils/tests/test_unixccompiler.py @@ -4,6 +4,7 @@ import sys import unittest.mock as mock from distutils import sysconfig +from distutils.compat import consolidate_linker_args from distutils.errors import DistutilsPlatformError from distutils.unixccompiler import UnixCCompiler from distutils.util import _clear_cached_macosx_ver @@ -149,10 +150,10 @@ def gcv(v): return 'yes' sysconfig.get_config_var = gcv - assert self.cc.rpath_foo() == [ + assert self.cc.rpath_foo() == consolidate_linker_args([ '-Wl,--enable-new-dtags', '-Wl,-rpath,/foo', - ] + ]) def gcv(v): if v == 'CC': @@ -161,10 +162,10 @@ def gcv(v): return 'yes' sysconfig.get_config_var = gcv - assert self.cc.rpath_foo() == [ + assert self.cc.rpath_foo() == consolidate_linker_args([ '-Wl,--enable-new-dtags', '-Wl,-rpath,/foo', - ] + ]) # GCC non-GNULD sys.platform = 'bar' @@ -189,10 +190,10 @@ def gcv(v): return 'yes' sysconfig.get_config_var = gcv - assert self.cc.rpath_foo() == [ + assert self.cc.rpath_foo() == consolidate_linker_args([ '-Wl,--enable-new-dtags', '-Wl,-rpath,/foo', - ] + ]) # non-GCC GNULD sys.platform = 'bar' @@ -204,10 +205,10 @@ def gcv(v): return 'yes' sysconfig.get_config_var = gcv - assert self.cc.rpath_foo() == [ + assert self.cc.rpath_foo() == consolidate_linker_args([ '-Wl,--enable-new-dtags', '-Wl,-rpath,/foo', - ] + ]) # non-GCC non-GNULD sys.platform = 'bar' diff --git a/distutils/unixccompiler.py b/distutils/unixccompiler.py index a54481c01b..0248bde87b 100644 --- a/distutils/unixccompiler.py +++ b/distutils/unixccompiler.py @@ -22,6 +22,7 @@ import sys from . import sysconfig +from .compat import consolidate_linker_args from ._log import log from ._macos_compat import compiler_fixup from ._modified import newer @@ -315,11 +316,11 @@ def runtime_library_dir_option(self, dir: str) -> str | list[str]: # For all compilers, `-Wl` is the presumed way to pass a # compiler option to the linker if sysconfig.get_config_var("GNULD") == "yes": - return [ + return consolidate_linker_args([ # Force RUNPATH instead of RPATH "-Wl,--enable-new-dtags", "-Wl,-rpath," + dir, - ] + ]) else: return "-Wl,-R" + dir From 98eee7f74c93fb84226d18f370f883956e644619 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 13 Apr 2024 14:03:03 -0400 Subject: [PATCH 8/9] Exclude compat package from coverage. --- .coveragerc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.coveragerc b/.coveragerc index 35b98b1df9..bcef31d957 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,6 +2,9 @@ omit = # leading `*/` for pytest-dev/pytest-cov#456 */.tox/* + + # local + */compat/* disable_warnings = couldnt-parse From ef297f26182823d54acfe3719416aa2661706b29 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 13 Apr 2024 16:40:21 -0400 Subject: [PATCH 9/9] Extend the retention of the compatibility. --- distutils/compat/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distutils/compat/__init__.py b/distutils/compat/__init__.py index b7be72678f..b1ee3fe8b0 100644 --- a/distutils/compat/__init__.py +++ b/distutils/compat/__init__.py @@ -7,7 +7,7 @@ def consolidate_linker_args(args: list[str]) -> str: """ Ensure the return value is a string for backward compatibility. - Retain until at least 2024-10-31. + Retain until at least 2024-04-31. See pypa/distutils#246 """ if not all(arg.startswith('-Wl,') for arg in args):