From a75611aec4e492a9760dc9dbf02388506f49afa7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 20 Mar 2025 17:01:56 -0400 Subject: [PATCH 1/6] Remove news fragments; merged downstream. --- newsfragments/4876.bugfix.2.rst | 1 - 1 file changed, 1 deletion(-) delete mode 100644 newsfragments/4876.bugfix.2.rst diff --git a/newsfragments/4876.bugfix.2.rst b/newsfragments/4876.bugfix.2.rst deleted file mode 100644 index 8350c5a736..0000000000 --- a/newsfragments/4876.bugfix.2.rst +++ /dev/null @@ -1 +0,0 @@ -Restore `distutils.ccompiler._default_compilers` -- by :user:`ManiacDC` From 754fcc21e6ca9de82ff6d2837841c4300aeb941f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 9 Mar 2025 13:35:09 -0400 Subject: [PATCH 2/6] Remove 'dry run' functionality throughout. --- distutils/archive_util.py | 64 +++++++++++------------- distutils/cmd.py | 31 +++--------- distutils/command/bdist_dumb.py | 2 +- distutils/command/bdist_rpm.py | 47 +++++++++--------- distutils/command/build_clib.py | 4 +- distutils/command/build_ext.py | 1 - distutils/command/build_py.py | 5 +- distutils/command/build_scripts.py | 36 +++++--------- distutils/command/clean.py | 15 +++--- distutils/command/config.py | 12 +---- distutils/command/install_egg_info.py | 7 ++- distutils/command/install_lib.py | 2 - distutils/command/install_scripts.py | 9 ++-- distutils/command/sdist.py | 4 +- distutils/compilers/C/base.py | 24 +++------ distutils/compilers/C/cygwin.py | 8 +-- distutils/compilers/C/msvc.py | 4 +- distutils/compilers/C/zos.py | 4 +- distutils/dir_util.py | 36 +++++--------- distutils/dist.py | 4 +- distutils/file_util.py | 14 ++---- distutils/spawn.py | 6 +-- distutils/tests/test_archive_util.py | 11 ----- distutils/util.py | 70 +++++++++++---------------- 24 files changed, 150 insertions(+), 270 deletions(-) diff --git a/distutils/archive_util.py b/distutils/archive_util.py index d860f55272..4a7fb9c9e7 100644 --- a/distutils/archive_util.py +++ b/distutils/archive_util.py @@ -61,7 +61,6 @@ def make_tarball( base_dir: str | os.PathLike[str], compress: Literal["gzip", "bzip2", "xz"] | None = "gzip", verbose: bool = False, - dry_run: bool = False, owner: str | None = None, group: str | None = None, ) -> str: @@ -96,7 +95,7 @@ def make_tarball( archive_name = base_name + '.tar' archive_name += compress_ext.get(compress, '') - mkpath(os.path.dirname(archive_name), dry_run=dry_run) + mkpath(os.path.dirname(archive_name)) # creating the tarball import tarfile # late import so Python build itself doesn't break @@ -115,21 +114,19 @@ def _set_uid_gid(tarinfo): tarinfo.uname = owner return tarinfo - if not dry_run: - tar = tarfile.open(archive_name, f'w|{tar_compression[compress]}') - try: - tar.add(base_dir, filter=_set_uid_gid) - finally: - tar.close() + tar = tarfile.open(archive_name, f'w|{tar_compression[compress]}') + try: + tar.add(base_dir, filter=_set_uid_gid) + finally: + tar.close() return archive_name -def make_zipfile( # noqa: C901 +def make_zipfile( base_name: str, base_dir: str | os.PathLike[str], verbose: bool = False, - dry_run: bool = False, ) -> str: """Create a zip file from all the files under 'base_dir'. @@ -140,7 +137,7 @@ def make_zipfile( # noqa: C901 file. """ zip_filename = base_name + ".zip" - mkpath(os.path.dirname(zip_filename), dry_run=dry_run) + mkpath(os.path.dirname(zip_filename)) # If zipfile module is not available, try spawning an external # 'zip' command. @@ -151,7 +148,7 @@ def make_zipfile( # noqa: C901 zipoptions = "-rq" try: - spawn(["zip", zipoptions, zip_filename, base_dir], dry_run=dry_run) + spawn(["zip", zipoptions, zip_filename, base_dir]) except DistutilsExecError: # XXX really should distinguish between "couldn't find # external 'zip' command" and "zip failed". @@ -164,29 +161,26 @@ def make_zipfile( # noqa: C901 else: log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir) - if not dry_run: - try: - zip = zipfile.ZipFile( - zip_filename, "w", compression=zipfile.ZIP_DEFLATED - ) - except RuntimeError: - zip = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_STORED) - - with zip: - if base_dir != os.curdir: - path = os.path.normpath(os.path.join(base_dir, '')) + try: + zip = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_DEFLATED) + except RuntimeError: + zip = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_STORED) + + with zip: + if base_dir != os.curdir: + path = os.path.normpath(os.path.join(base_dir, '')) + zip.write(path, path) + log.info("adding '%s'", path) + for dirpath, dirnames, filenames in os.walk(base_dir): + for name in dirnames: + path = os.path.normpath(os.path.join(dirpath, name, '')) zip.write(path, path) log.info("adding '%s'", path) - for dirpath, dirnames, filenames in os.walk(base_dir): - for name in dirnames: - path = os.path.normpath(os.path.join(dirpath, name, '')) + for name in filenames: + path = os.path.normpath(os.path.join(dirpath, name)) + if os.path.isfile(path): zip.write(path, path) log.info("adding '%s'", path) - for name in filenames: - path = os.path.normpath(os.path.join(dirpath, name)) - if os.path.isfile(path): - zip.write(path, path) - log.info("adding '%s'", path) return zip_filename @@ -219,7 +213,6 @@ def make_archive( root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes] | None = None, base_dir: str | None = None, verbose: bool = False, - dry_run: bool = False, owner: str | None = None, group: str | None = None, ) -> str: ... @@ -230,7 +223,6 @@ def make_archive( root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes], base_dir: str | None = None, verbose: bool = False, - dry_run: bool = False, owner: str | None = None, group: str | None = None, ) -> str: ... @@ -240,7 +232,6 @@ def make_archive( root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes] | None = None, base_dir: str | None = None, verbose: bool = False, - dry_run: bool = False, owner: str | None = None, group: str | None = None, ) -> str: @@ -264,13 +255,12 @@ def make_archive( if root_dir is not None: log.debug("changing into '%s'", root_dir) base_name = os.path.abspath(base_name) - if not dry_run: - os.chdir(root_dir) + os.chdir(root_dir) if base_dir is None: base_dir = os.curdir - kwargs = {'dry_run': dry_run} + kwargs: dict[str, bool | None] = {} try: format_info = ARCHIVE_FORMATS[format] diff --git a/distutils/cmd.py b/distutils/cmd.py index 241621bd51..530cc108d4 100644 --- a/distutils/cmd.py +++ b/distutils/cmd.py @@ -91,13 +91,8 @@ def __init__(self, dist: Distribution) -> None: # Per-command versions of the global flags, so that the user can # customize Distutils' behaviour command-by-command and let some - # commands fall back on the Distribution's behaviour. None means - # "not defined, check self.distribution's copy", while 0 or 1 mean - # false and true (duh). Note that this means figuring out the real - # value of each flag is a touch complicated -- hence "self._dry_run" - # will be handled by __getattr__, below. - # XXX This needs to be fixed. - self._dry_run = None + # commands fall back on the Distribution's behaviour. None means + # "not defined, check self.distribution's copy". # verbose is largely ignored, but needs to be set for # backwards compatibility (I think)? @@ -119,17 +114,6 @@ def __init__(self, dist: Distribution) -> None: # always calls 'finalize_options()', to respect/update it. self.finalized = False - # XXX A more explicit way to customize dry_run would be better. - def __getattr__(self, attr): - if attr == 'dry_run': - myval = getattr(self, "_" + attr) - if myval is None: - return getattr(self.distribution, attr) - else: - return myval - else: - raise AttributeError(attr) - def ensure_finalized(self) -> None: if not self.finalized: self.finalize_options() @@ -381,10 +365,10 @@ def execute( msg: object = None, level: int = 1, ) -> None: - util.execute(func, args, msg, dry_run=self.dry_run) + util.execute(func, args, msg) def mkpath(self, name: str, mode: int = 0o777) -> None: - dir_util.mkpath(name, mode, dry_run=self.dry_run) + dir_util.mkpath(name, mode) @overload def copy_file( @@ -425,7 +409,6 @@ def copy_file( preserve_times, not self.force, link, - dry_run=self.dry_run, ) def copy_tree( @@ -447,7 +430,6 @@ def copy_tree( preserve_times, preserve_symlinks, not self.force, - dry_run=self.dry_run, ) @overload @@ -465,7 +447,7 @@ def move_file( level: int = 1, ) -> str | os.PathLike[str] | bytes | os.PathLike[bytes]: """Move a file respecting dry-run flag.""" - return file_util.move_file(src, dst, dry_run=self.dry_run) + return file_util.move_file(src, dst) def spawn( self, cmd: MutableSequence[str], search_path: bool = True, level: int = 1 @@ -473,7 +455,7 @@ def spawn( """Spawn an external command respecting dry-run flag.""" from distutils.spawn import spawn - spawn(cmd, search_path, dry_run=self.dry_run) + spawn(cmd, search_path) @overload def make_archive( @@ -509,7 +491,6 @@ def make_archive( format, root_dir, base_dir, - dry_run=self.dry_run, owner=owner, group=group, ) diff --git a/distutils/command/bdist_dumb.py b/distutils/command/bdist_dumb.py index ccad66f431..c49ef253b4 100644 --- a/distutils/command/bdist_dumb.py +++ b/distutils/command/bdist_dumb.py @@ -138,4 +138,4 @@ def run(self): self.distribution.dist_files.append(('bdist_dumb', pyversion, filename)) if not self.keep_temp: - remove_tree(self.bdist_dir, dry_run=self.dry_run) + remove_tree(self.bdist_dir) diff --git a/distutils/command/bdist_rpm.py b/distutils/command/bdist_rpm.py index 357b4e861e..dfa5f99dda 100644 --- a/distutils/command/bdist_rpm.py +++ b/distutils/command/bdist_rpm.py @@ -378,30 +378,29 @@ def run(self) -> None: # noqa: C901 self.spawn(rpm_cmd) - if not self.dry_run: - if self.distribution.has_ext_modules(): - pyversion = get_python_version() - else: - pyversion = 'any' - - if not self.binary_only: - srpm = os.path.join(rpm_dir['SRPMS'], source_rpm) - assert os.path.exists(srpm) - self.move_file(srpm, self.dist_dir) - filename = os.path.join(self.dist_dir, source_rpm) - self.distribution.dist_files.append(('bdist_rpm', pyversion, filename)) - - if not self.source_only: - for rpm in binary_rpms: - rpm = os.path.join(rpm_dir['RPMS'], rpm) - if os.path.exists(rpm): - self.move_file(rpm, self.dist_dir) - filename = os.path.join(self.dist_dir, os.path.basename(rpm)) - self.distribution.dist_files.append(( - 'bdist_rpm', - pyversion, - filename, - )) + if self.distribution.has_ext_modules(): + pyversion = get_python_version() + else: + pyversion = 'any' + + if not self.binary_only: + srpm = os.path.join(rpm_dir['SRPMS'], source_rpm) + assert os.path.exists(srpm) + self.move_file(srpm, self.dist_dir) + filename = os.path.join(self.dist_dir, source_rpm) + self.distribution.dist_files.append(('bdist_rpm', pyversion, filename)) + + if not self.source_only: + for rpm in binary_rpms: + rpm = os.path.join(rpm_dir['RPMS'], rpm) + if os.path.exists(rpm): + self.move_file(rpm, self.dist_dir) + filename = os.path.join(self.dist_dir, os.path.basename(rpm)) + self.distribution.dist_files.append(( + 'bdist_rpm', + pyversion, + filename, + )) def _dist_path(self, path): return os.path.join(self.dist_dir, os.path.basename(path)) diff --git a/distutils/command/build_clib.py b/distutils/command/build_clib.py index 8b65b3d8ec..8de5df239c 100644 --- a/distutils/command/build_clib.py +++ b/distutils/command/build_clib.py @@ -88,9 +88,7 @@ def run(self) -> None: if not self.libraries: return - self.compiler = new_compiler( - compiler=self.compiler, dry_run=self.dry_run, force=self.force - ) + self.compiler = new_compiler(compiler=self.compiler, force=self.force) customize_compiler(self.compiler) if self.include_dirs is not None: diff --git a/distutils/command/build_ext.py b/distutils/command/build_ext.py index ec45b4403e..df623d7e0e 100644 --- a/distutils/command/build_ext.py +++ b/distutils/command/build_ext.py @@ -326,7 +326,6 @@ def run(self) -> None: # noqa: C901 self.compiler = new_compiler( compiler=self.compiler, verbose=self.verbose, - dry_run=self.dry_run, force=self.force, ) customize_compiler(self.compiler) diff --git a/distutils/command/build_py.py b/distutils/command/build_py.py index a20b076fe7..d6ec7d10d2 100644 --- a/distutils/command/build_py.py +++ b/distutils/command/build_py.py @@ -394,14 +394,11 @@ def byte_compile(self, files) -> None: # method of the "install_lib" command, except for the determination # of the 'prefix' string. Hmmm. if self.compile: - byte_compile( - files, optimize=0, force=self.force, prefix=prefix, dry_run=self.dry_run - ) + byte_compile(files, optimize=0, force=self.force, prefix=prefix) if self.optimize > 0: byte_compile( files, optimize=self.optimize, force=self.force, prefix=prefix, - dry_run=self.dry_run, ) diff --git a/distutils/command/build_scripts.py b/distutils/command/build_scripts.py index 127c51d8dc..56a1be0e97 100644 --- a/distutils/command/build_scripts.py +++ b/distutils/command/build_scripts.py @@ -87,30 +87,24 @@ def _copy_script(self, script, outfiles, updated_files): # Always open the file, but ignore failures in dry-run mode # in order to attempt to copy directly. - try: - f = tokenize.open(script) - except OSError: - if not self.dry_run: - raise - f = None - else: - first_line = f.readline() - if not first_line: - self.warn(f"{script} is an empty file (skipping)") - return + f = tokenize.open(script) + + first_line = f.readline() + if not first_line: + self.warn(f"{script} is an empty file (skipping)") + return - shebang_match = shebang_pattern.match(first_line) + shebang_match = shebang_pattern.match(first_line) updated_files.append(outfile) if shebang_match: log.info("copying and adjusting %s -> %s", script, self.build_dir) - if not self.dry_run: - post_interp = shebang_match.group(1) or '' - shebang = f"#!python{post_interp}\n" - self._validate_shebang(shebang, f.encoding) - with open(outfile, "w", encoding=f.encoding) as outf: - outf.write(shebang) - outf.writelines(f.readlines()) + post_interp = shebang_match.group(1) or '' + shebang = f"#!python{post_interp}\n" + self._validate_shebang(shebang, f.encoding) + with open(outfile, "w", encoding=f.encoding) as outf: + outf.write(shebang) + outf.writelines(f.readlines()) if f: f.close() else: @@ -126,10 +120,6 @@ def _change_modes(self, outfiles): self._change_mode(file) def _change_mode(self, file): - if self.dry_run: - log.info("changing mode of %s", file) - return - oldmode = os.stat(file)[ST_MODE] & 0o7777 newmode = (oldmode | 0o555) & 0o7777 if newmode != oldmode: diff --git a/distutils/command/clean.py b/distutils/command/clean.py index 23427aba21..fc1711be8e 100644 --- a/distutils/command/clean.py +++ b/distutils/command/clean.py @@ -55,7 +55,7 @@ def run(self): # remove the build/temp. directory (unless it's already # gone) if os.path.exists(self.build_temp): - remove_tree(self.build_temp, dry_run=self.dry_run) + remove_tree(self.build_temp) else: log.debug("'%s' does not exist -- can't clean it", self.build_temp) @@ -63,15 +63,14 @@ def run(self): # remove build directories for directory in (self.build_lib, self.bdist_base, self.build_scripts): if os.path.exists(directory): - remove_tree(directory, dry_run=self.dry_run) + remove_tree(directory) else: log.warning("'%s' does not exist -- can't clean it", directory) # just for the heck of it, try to remove the base build directory: # we might have emptied it right now, but if not we don't care - if not self.dry_run: - try: - os.rmdir(self.build_base) - log.info("removing '%s'", self.build_base) - except OSError: - pass + try: + os.rmdir(self.build_base) + log.info("removing '%s'", self.build_base) + except OSError: + pass diff --git a/distutils/command/config.py b/distutils/command/config.py index 44df823388..4d43ca0d8d 100644 --- a/distutils/command/config.py +++ b/distutils/command/config.py @@ -92,9 +92,7 @@ def _check_compiler(self): # We do this late, and only on-demand, because this is an expensive # import. if not isinstance(self.compiler, CCompiler): - self.compiler = new_compiler( - compiler=self.compiler, dry_run=self.dry_run, force=True - ) + self.compiler = new_compiler(compiler=self.compiler, force=True) customize_compiler(self.compiler) if self.include_dirs: self.compiler.set_include_dirs(self.include_dirs) @@ -159,14 +157,6 @@ def _clean(self, *filenames): except OSError: pass - # XXX these ignore the dry-run flag: what to do, what to do? even if - # you want a dry-run build, you still need some sort of configuration - # info. My inclination is to make it up to the real config command to - # consult 'dry_run', and assume a default (minimal) configuration if - # true. The problem with trying to do it here is that you'd have to - # return either true or false from all the 'try' methods, neither of - # which is correct. - # XXX need access to the header search path and maybe default macros. def try_cpp(self, body=None, headers=None, include_dirs=None, lang="c"): diff --git a/distutils/command/install_egg_info.py b/distutils/command/install_egg_info.py index 230e94ab46..6840b34ec2 100644 --- a/distutils/command/install_egg_info.py +++ b/distutils/command/install_egg_info.py @@ -44,7 +44,7 @@ def finalize_options(self): def run(self): target = self.target if os.path.isdir(target) and not os.path.islink(target): - dir_util.remove_tree(target, dry_run=self.dry_run) + dir_util.remove_tree(target) elif os.path.exists(target): self.execute(os.unlink, (self.target,), "Removing " + target) elif not os.path.isdir(self.install_dir): @@ -52,9 +52,8 @@ def run(self): os.makedirs, (self.install_dir,), "Creating " + self.install_dir ) log.info("Writing %s", target) - if not self.dry_run: - with open(target, 'w', encoding='UTF-8') as f: - self.distribution.metadata.write_pkg_file(f) + with open(target, 'w', encoding='UTF-8') as f: + self.distribution.metadata.write_pkg_file(f) def get_outputs(self): return self.outputs diff --git a/distutils/command/install_lib.py b/distutils/command/install_lib.py index 2aababf800..9909a61762 100644 --- a/distutils/command/install_lib.py +++ b/distutils/command/install_lib.py @@ -142,7 +142,6 @@ def byte_compile(self, files) -> None: optimize=0, force=self.force, prefix=install_root, - dry_run=self.dry_run, ) if self.optimize > 0: byte_compile( @@ -151,7 +150,6 @@ def byte_compile(self, files) -> None: force=self.force, prefix=install_root, verbose=self.verbose, - dry_run=self.dry_run, ) # -- Utility methods ----------------------------------------------- diff --git a/distutils/command/install_scripts.py b/distutils/command/install_scripts.py index 92e8694111..33d235d976 100644 --- a/distutils/command/install_scripts.py +++ b/distutils/command/install_scripts.py @@ -48,12 +48,9 @@ def run(self) -> None: # Set the executable bits (owner, group, and world) on # all the scripts we just installed. for file in self.get_outputs(): - if self.dry_run: - log.info("changing mode of %s", file) - else: - mode = ((os.stat(file)[ST_MODE]) | 0o555) & 0o7777 - log.info("changing mode of %s to %o", file, mode) - os.chmod(file, mode) + mode = ((os.stat(file)[ST_MODE]) | 0o555) & 0o7777 + log.info("changing mode of %s to %o", file, mode) + os.chmod(file, mode) def get_inputs(self): return self.distribution.scripts or [] diff --git a/distutils/command/sdist.py b/distutils/command/sdist.py index b3bf0c326a..e64cc82902 100644 --- a/distutils/command/sdist.py +++ b/distutils/command/sdist.py @@ -450,7 +450,7 @@ def make_release_tree(self, base_dir, files) -> None: # put 'files' there; the 'mkpath()' is just so we don't die # if the manifest happens to be empty. self.mkpath(base_dir) - dir_util.create_tree(base_dir, files, dry_run=self.dry_run) + dir_util.create_tree(base_dir, files) # And walk over the list of files, either making a hard link (if # os.link exists) to each one that doesn't already exist in its @@ -508,7 +508,7 @@ def make_distribution(self) -> None: self.archive_files = archive_files if not self.keep_temp: - dir_util.remove_tree(base_dir, dry_run=self.dry_run) + dir_util.remove_tree(base_dir) def get_archive_files(self): """Return the list of archive files created when the command diff --git a/distutils/compilers/C/base.py b/distutils/compilers/C/base.py index 5efd2a39d6..ea6b5458ef 100644 --- a/distutils/compilers/C/base.py +++ b/distutils/compilers/C/base.py @@ -130,10 +130,7 @@ class Compiler: library dirs specific to this compiler class """ - def __init__( - self, verbose: bool = False, dry_run: bool = False, force: bool = False - ) -> None: - self.dry_run = dry_run + def __init__(self, verbose: bool = False, force: bool = False) -> None: self.force = force self.verbose = verbose @@ -531,12 +528,8 @@ def _need_link(self, objects, output_file): """ if self.force: return True - else: - if self.dry_run: - newer = newer_group(objects, output_file, missing='newer') - else: - newer = newer_group(objects, output_file) - return newer + newer = newer_group(objects, output_file) + return newer def detect_language(self, sources: str | list[str]) -> str | None: """Detect the language of a given file, or list of files. Uses @@ -1150,12 +1143,12 @@ def execute( msg: object = None, level: int = 1, ) -> None: - execute(func, args, msg, self.dry_run) + execute(func, args, msg) def spawn( self, cmd: MutableSequence[bytes | str | os.PathLike[str]], **kwargs ) -> None: - spawn(cmd, dry_run=self.dry_run, **kwargs) + spawn(cmd, **kwargs) @overload def move_file( @@ -1170,10 +1163,10 @@ def move_file( src: str | os.PathLike[str] | bytes | os.PathLike[bytes], dst: str | os.PathLike[str] | bytes | os.PathLike[bytes], ) -> str | os.PathLike[str] | bytes | os.PathLike[bytes]: - return move_file(src, dst, dry_run=self.dry_run) + return move_file(src, dst) def mkpath(self, name, mode=0o777): - mkpath(name, mode, dry_run=self.dry_run) + mkpath(name, mode) # Map a sys.platform/os.name ('posix', 'nt') to the default compiler @@ -1262,7 +1255,6 @@ def new_compiler( plat: str | None = None, compiler: str | None = None, verbose: bool = False, - dry_run: bool = False, force: bool = False, ) -> Compiler: """Generate an instance of some CCompiler subclass for the supplied @@ -1307,7 +1299,7 @@ def new_compiler( # XXX The None is necessary to preserve backwards compatibility # with classes that expect verbose to be the first positional # argument. - return klass(None, dry_run, force) + return klass(None, force=force) def gen_preprocess_options( diff --git a/distutils/compilers/C/cygwin.py b/distutils/compilers/C/cygwin.py index bfabbb306e..f349e9decd 100644 --- a/distutils/compilers/C/cygwin.py +++ b/distutils/compilers/C/cygwin.py @@ -52,8 +52,8 @@ class Compiler(unix.Compiler): dylib_lib_format = "cyg%s%s" exe_extension = ".exe" - def __init__(self, verbose=False, dry_run=False, force=False): - super().__init__(verbose, dry_run, force) + def __init__(self, verbose=False, force=False): + super().__init__(verbose, force=force) status, details = check_config_h() self.debug_print(f"Python's GCC status: {status} (details: {details})") @@ -246,8 +246,8 @@ class MinGW32Compiler(Compiler): compiler_type = 'mingw32' - def __init__(self, verbose=False, dry_run=False, force=False): - super().__init__(verbose, dry_run, force) + def __init__(self, verbose=False, force=False): + super().__init__(verbose, force) shared_option = "-shared" diff --git a/distutils/compilers/C/msvc.py b/distutils/compilers/C/msvc.py index 6db062a9e7..1f50fa5088 100644 --- a/distutils/compilers/C/msvc.py +++ b/distutils/compilers/C/msvc.py @@ -260,8 +260,8 @@ class Compiler(base.Compiler): static_lib_format = shared_lib_format = '%s%s' exe_extension = '.exe' - def __init__(self, verbose=False, dry_run=False, force=False) -> None: - super().__init__(verbose, dry_run, force) + def __init__(self, verbose=False, force=False) -> None: + super().__init__(verbose, force=force) # target platform (.plat_name is consistent with 'bdist') self.plat_name = None self.initialized = False diff --git a/distutils/compilers/C/zos.py b/distutils/compilers/C/zos.py index 82d017fc90..681c63166b 100644 --- a/distutils/compilers/C/zos.py +++ b/distutils/compilers/C/zos.py @@ -136,8 +136,8 @@ def _get_zos_compiler_name(self): return zos_compilers.get(zos_compiler_names[0], 'ibm-openxl') - def __init__(self, verbose=False, dry_run=False, force=False): - super().__init__(verbose, dry_run, force) + def __init__(self, verbose=False, force=False): + super().__init__(verbose, force=force) self.zos_compiler = self._get_zos_compiler_name() sysconfig.customize_compiler(self) diff --git a/distutils/dir_util.py b/distutils/dir_util.py index d9782602cf..61b4575aa0 100644 --- a/distutils/dir_util.py +++ b/distutils/dir_util.py @@ -45,7 +45,7 @@ def wrapper(path, *args, **kwargs): @functools.singledispatch @wrapper -def mkpath(name: pathlib.Path, mode=0o777, verbose=True, dry_run=False) -> None: +def mkpath(name: pathlib.Path, mode=0o777, verbose=True) -> None: """Create a directory and any missing ancestor directories. If the directory already exists (or if 'name' is the empty string, which @@ -58,7 +58,7 @@ def mkpath(name: pathlib.Path, mode=0o777, verbose=True, dry_run=False) -> None: log.info("creating %s", name) try: - dry_run or name.mkdir(mode=mode, parents=True, exist_ok=True) + name.mkdir(mode=mode, parents=True, exist_ok=True) except OSError as exc: raise DistutilsFileError(f"could not create '{name}': {exc.args[-1]}") @@ -76,22 +76,22 @@ def _(name: None, *args, **kwargs): raise DistutilsInternalError(f"mkpath: 'name' must be a string (got {name!r})") -def create_tree(base_dir, files, mode=0o777, verbose=True, dry_run=False): +def create_tree(base_dir, files, mode=0o777, verbose=True): """Create all the empty directories under 'base_dir' needed to put 'files' there. 'base_dir' is just the name of a directory which doesn't necessarily exist yet; 'files' is a list of filenames to be interpreted relative to 'base_dir'. 'base_dir' + the directory portion of every file in 'files' - will be created if it doesn't already exist. 'mode', 'verbose' and - 'dry_run' flags are as for 'mkpath()'. + will be created if it doesn't already exist. 'mode' and 'verbose' + flags are as for 'mkpath()'. """ # First get the list of directories to create 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): - mkpath(dir, mode, verbose=verbose, dry_run=dry_run) + mkpath(dir, mode, verbose=verbose) def copy_tree( @@ -102,7 +102,6 @@ def copy_tree( preserve_symlinks=False, update=False, verbose=True, - dry_run=False, ): """Copy an entire directory tree 'src' to a new location 'dst'. @@ -112,7 +111,7 @@ def copy_tree( file in 'src' is copied to 'dst', and directories under 'src' are recursively copied to 'dst'. Return the list of files that were copied or might have been copied, using their output name. The - return value is unaffected by 'update' or 'dry_run': it is simply + return value is unaffected by 'update': it is simply the list of all files under 'src', with the names changed to be under 'dst'. @@ -123,18 +122,14 @@ def copy_tree( (the default), the destination of the symlink will be copied. 'update' and 'verbose' are the same as for 'copy_file'. """ - if not dry_run and not os.path.isdir(src): + if not os.path.isdir(src): raise DistutilsFileError(f"cannot copy tree '{src}': not a directory") try: names = os.listdir(src) except OSError as e: - if dry_run: - names = [] - else: - raise DistutilsFileError(f"error listing files in '{src}': {e.strerror}") + raise DistutilsFileError(f"error listing files in '{src}': {e.strerror}") - if not dry_run: - mkpath(dst, verbose=verbose) + mkpath(dst, verbose=verbose) copy_one = functools.partial( _copy_one, @@ -142,7 +137,6 @@ def copy_tree( dst=dst, preserve_symlinks=preserve_symlinks, verbose=verbose, - dry_run=dry_run, preserve_mode=preserve_mode, preserve_times=preserve_times, update=update, @@ -157,7 +151,6 @@ def _copy_one( dst, preserve_symlinks, verbose, - dry_run, preserve_mode, preserve_times, update, @@ -173,8 +166,7 @@ def _copy_one( link_dest = os.readlink(src_name) if verbose >= 1: log.info("linking %s -> %s", dst_name, link_dest) - if not dry_run: - os.symlink(link_dest, dst_name) + os.symlink(link_dest, dst_name) yield dst_name elif os.path.isdir(src_name): @@ -186,7 +178,6 @@ def _copy_one( preserve_symlinks, update, verbose=verbose, - dry_run=dry_run, ) else: file_util.copy_file( @@ -196,7 +187,6 @@ def _copy_one( preserve_times, update, verbose=verbose, - dry_run=dry_run, ) yield dst_name @@ -212,7 +202,7 @@ def _build_cmdtuple(path, cmdtuples): cmdtuples.append((os.rmdir, path)) -def remove_tree(directory, verbose=True, dry_run=False): +def remove_tree(directory, verbose=True): """Recursively remove an entire directory tree. Any errors are ignored (apart from being reported to stdout if 'verbose' @@ -220,8 +210,6 @@ def remove_tree(directory, verbose=True, dry_run=False): """ if verbose >= 1: log.info("removing '%s' (and everything under it)", directory) - if dry_run: - return cmdtuples = [] _build_cmdtuple(directory, cmdtuples) for cmd in cmdtuples: diff --git a/distutils/dist.py b/distutils/dist.py index 37b788df92..e04e487b58 100644 --- a/distutils/dist.py +++ b/distutils/dist.py @@ -99,7 +99,6 @@ class Distribution: global_options: ClassVar[_OptionsList] = [ ('verbose', 'v', "run verbosely (default)", 1), ('quiet', 'q', "run quietly (turns verbosity off)"), - ('dry-run', 'n', "don't actually do anything"), ('help', 'h', "show detailed help message"), ('no-user-cfg', None, 'ignore pydistutils.cfg in your home directory'), ] @@ -164,7 +163,6 @@ def __init__(self, attrs: MutableMapping[str, Any] | None = None) -> None: # no # Default values for our command-line options self.verbose = True - self.dry_run = False self.help = False for attr in self.display_option_names: setattr(self, attr, False) @@ -446,7 +444,7 @@ def parse_config_files(self, filenames=None): # noqa: C901 try: if alias: setattr(self, alias, not strtobool(val)) - elif opt in ('verbose', 'dry_run'): # ugh! + elif opt in ('verbose',): # ugh! setattr(self, opt, strtobool(val)) else: setattr(self, opt, val) diff --git a/distutils/file_util.py b/distutils/file_util.py index 0acc8cb84b..a9724b1779 100644 --- a/distutils/file_util.py +++ b/distutils/file_util.py @@ -68,7 +68,6 @@ def copy_file( # noqa: C901 update=False, link=None, verbose=True, - dry_run=False, ): """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is copied there with the same name; otherwise, it must be a filename. (If @@ -91,8 +90,7 @@ def copy_file( # noqa: C901 other systems, uses '_copy_file_contents()' to copy file contents. Return a tuple (dest_name, copied): 'dest_name' is the actual name of - the output file, and 'copied' is true if the file was copied (or would - have been copied, if 'dry_run' true). + the output file, and 'copied' is true if the file was copied. """ # XXX if the destination file already exists, we clobber it if # copying, but blow up if linking. Hmmm. And I don't know what @@ -131,12 +129,9 @@ def copy_file( # noqa: C901 else: log.info("%s %s -> %s", action, src, dst) - if dry_run: - return (dst, True) - # If linking (hard or symbolic), use the appropriate system call # (Unix only, of course, but that's the caller's responsibility) - elif link == 'hard': + if link == 'hard': if not (os.path.exists(dst) and os.path.samefile(src, dst)): try: os.link(src, dst) @@ -169,7 +164,7 @@ def copy_file( # noqa: C901 # XXX I suspect this is Unix-specific -- need porting help! -def move_file(src, dst, verbose=True, dry_run=False): # noqa: C901 +def move_file(src, dst, verbose=True): # noqa: C901 """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will be moved into it with the same name; otherwise, 'src' is just renamed to 'dst'. Return the new full name of the file. @@ -183,9 +178,6 @@ def move_file(src, dst, verbose=True, dry_run=False): # noqa: C901 if verbose >= 1: log.info("moving %s -> %s", src, dst) - if dry_run: - return dst - if not isfile(src): raise DistutilsFileError(f"can't move '{src}': not a regular file") diff --git a/distutils/spawn.py b/distutils/spawn.py index 973668f268..e47f1d1927 100644 --- a/distutils/spawn.py +++ b/distutils/spawn.py @@ -56,7 +56,6 @@ def spawn( cmd: MutableSequence[bytes | str | os.PathLike[str]], search_path: bool = True, verbose: bool = False, - dry_run: bool = False, env: _ENV | None = None, ) -> None: """Run another program, specified as a command list 'cmd', in a new process. @@ -68,15 +67,12 @@ def spawn( If 'search_path' is true (the default), the system's executable search path will be used to find the program; otherwise, cmd[0] - must be the exact path to the executable. If 'dry_run' is true, - the command will not actually be run. + must be the exact path to the executable. Raise DistutilsExecError if running the program fails in any way; just return on success. """ log.info(subprocess.list2cmdline(cmd)) - if dry_run: - return if search_path: executable = shutil.which(cmd[0]) diff --git a/distutils/tests/test_archive_util.py b/distutils/tests/test_archive_util.py index 3e4ed75a76..a9cc5eb009 100644 --- a/distutils/tests/test_archive_util.py +++ b/distutils/tests/test_archive_util.py @@ -177,17 +177,6 @@ def test_tarfile_vs_tar(self): tarball = base_name + '.tar' assert os.path.exists(tarball) - # now for a dry_run - base_name = os.path.join(tmpdir2, 'archive') - old_dir = os.getcwd() - os.chdir(tmpdir) - try: - make_tarball(base_name, 'dist', compress=None, dry_run=True) - finally: - os.chdir(old_dir) - tarball = base_name + '.tar' - assert os.path.exists(tarball) - @pytest.mark.usefixtures('needs_zlib') def test_make_zipfile(self): zipfile = pytest.importorskip('zipfile') diff --git a/distutils/util.py b/distutils/util.py index 6dbe049f42..47bb5af5e4 100644 --- a/distutils/util.py +++ b/distutils/util.py @@ -313,16 +313,11 @@ def execute( args: tuple[Unpack[_Ts]], msg: object = None, verbose: bool = False, - dry_run: bool = False, ) -> None: """ Perform some action that affects the outside world (e.g. by - writing to the filesystem). Such actions are special because they - are disabled by the 'dry_run' flag. This method handles that - complication; simply supply the - function to call and an argument tuple for it (to embody the - "external action" being performed) and an optional message to - emit. + writing to the filesystem). Was previously used to deal with + "dry run" operations, but now runs unconditionally. """ if msg is None: msg = f"{func.__name__}{args!r}" @@ -330,8 +325,7 @@ def execute( msg = msg[0:-2] + ')' log.info(msg) - if not dry_run: - func(*args) + func(*args) def strtobool(val: str) -> bool: @@ -357,7 +351,6 @@ def byte_compile( # noqa: C901 prefix: str | None = None, base_dir: str | None = None, verbose: bool = True, - dry_run: bool = False, direct: bool | None = None, ) -> None: """Byte-compile a collection of Python source files to .pyc @@ -377,9 +370,6 @@ def byte_compile( # noqa: C901 prepended (after 'prefix' is stripped). You can supply either or both (or neither) of 'prefix' and 'base_dir', as you wish. - If 'dry_run' is true, doesn't actually do anything that would - affect the filesystem. - Byte-compilation is either done directly in this interpreter process with the standard py_compile module, or indirectly by writing a temporary script and executing it. Normally, you should let @@ -411,42 +401,41 @@ def byte_compile( # noqa: C901 if not direct: (script_fd, script_name) = tempfile.mkstemp(".py") log.info("writing byte-compilation script '%s'", script_name) - if not dry_run: - script = os.fdopen(script_fd, "w", encoding='utf-8') + script = os.fdopen(script_fd, "w", encoding='utf-8') - with script: - script.write( - """\ + with script: + script.write( + """\ from distutils.util import byte_compile files = [ """ - ) - - # XXX would be nice to write absolute filenames, just for - # safety's sake (script should be more robust in the face of - # chdir'ing before running it). But this requires abspath'ing - # 'prefix' as well, and that breaks the hack in build_lib's - # 'byte_compile()' method that carefully tacks on a trailing - # slash (os.sep really) to make sure the prefix here is "just - # right". This whole prefix business is rather delicate -- the - # problem is that it's really a directory, but I'm treating it - # as a dumb string, so trailing slashes and so forth matter. - - script.write(",\n".join(map(repr, py_files)) + "]\n") - script.write( - f""" + ) + + # XXX would be nice to write absolute filenames, just for + # safety's sake (script should be more robust in the face of + # chdir'ing before running it). But this requires abspath'ing + # 'prefix' as well, and that breaks the hack in build_lib's + # 'byte_compile()' method that carefully tacks on a trailing + # slash (os.sep really) to make sure the prefix here is "just + # right". This whole prefix business is rather delicate -- the + # problem is that it's really a directory, but I'm treating it + # as a dumb string, so trailing slashes and so forth matter. + + script.write(",\n".join(map(repr, py_files)) + "]\n") + script.write( + f""" byte_compile(files, optimize={optimize!r}, force={force!r}, - prefix={prefix!r}, base_dir={base_dir!r}, - verbose={verbose!r}, dry_run=False, - direct=True) + prefix={prefix!r}, base_dir={base_dir!r}, + verbose={verbose!r}, + direct=True) """ - ) + ) cmd = [sys.executable] cmd.extend(subprocess._optim_args_from_interpreter_flags()) cmd.append(script_name) - spawn(cmd, dry_run=dry_run) - execute(os.remove, (script_name,), f"removing {script_name}", dry_run=dry_run) + spawn(cmd) + execute(os.remove, (script_name,), f"removing {script_name}") # "Direct" byte-compilation: use the py_compile module to compile # right here, right now. Note that the script generated in indirect @@ -483,8 +472,7 @@ def byte_compile( # noqa: C901 if direct: if force or newer(file, cfile): log.info("byte-compiling %s to %s", file, cfile_base) - if not dry_run: - compile(file, cfile, dfile) + compile(file, cfile, dfile) else: log.debug("skipping byte-compilation of %s to %s", file, cfile_base) From eabc303af3881cd123523fbaf964b123ad1f3610 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 9 Mar 2025 14:52:05 -0400 Subject: [PATCH 3/6] Remove 'dry run' functionality throughout. --- setuptools/command/alias.py | 2 +- setuptools/command/bdist_egg.py | 37 ++++++---------- setuptools/command/bdist_wheel.py | 3 +- setuptools/command/build_ext.py | 60 +++++++++++++------------- setuptools/command/develop.py | 11 ++--- setuptools/command/easy_install.py | 38 +++++----------- setuptools/command/editable_wheel.py | 1 - setuptools/command/egg_info.py | 33 +++++++------- setuptools/command/install_egg_info.py | 5 +-- setuptools/command/install_scripts.py | 9 ++-- setuptools/command/rotate.py | 9 ++-- setuptools/command/saveopts.py | 2 +- setuptools/command/setopt.py | 8 ++-- setuptools/dist.py | 2 +- setuptools/namespaces.py | 5 --- 15 files changed, 89 insertions(+), 136 deletions(-) diff --git a/setuptools/command/alias.py b/setuptools/command/alias.py index b8d74af71d..0ffa3116d6 100644 --- a/setuptools/command/alias.py +++ b/setuptools/command/alias.py @@ -61,7 +61,7 @@ def run(self) -> None: alias = self.args[0] command = ' '.join(map(shquote, self.args[1:])) - edit_config(self.filename, {'aliases': {alias: command}}, self.dry_run) + edit_config(self.filename, {'aliases': {alias: command}}) def format_alias(name, aliases): diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index 7f66c3ba6a..869bc7e874 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -158,7 +158,6 @@ def call_command(self, cmdname, **kw): for dirname in INSTALL_DIRECTORY_ATTRS: kw.setdefault(dirname, self.bdist_dir) kw.setdefault('skip_build', self.skip_build) - kw.setdefault('dry_run', self.dry_run) cmd = self.reinitialize_command(cmdname, **kw) self.run_command(cmdname) return cmd @@ -185,8 +184,7 @@ def run(self): # noqa: C901 # is too complex (14) # FIXME pyfile = os.path.join(self.bdist_dir, strip_module(filename) + '.py') self.stubs.append(pyfile) log.info("creating stub loader for %s", ext_name) - if not self.dry_run: - write_stub(os.path.basename(ext_name), pyfile) + write_stub(os.path.basename(ext_name), pyfile) to_compile.append(pyfile) ext_outputs[p] = ext_name.replace(os.sep, '/') @@ -208,15 +206,13 @@ def run(self): # noqa: C901 # is too complex (14) # FIXME native_libs = os.path.join(egg_info, "native_libs.txt") if all_outputs: log.info("writing %s", native_libs) - if not self.dry_run: - ensure_directory(native_libs) - with open(native_libs, 'wt', encoding="utf-8") as libs_file: - libs_file.write('\n'.join(all_outputs)) - libs_file.write('\n') + ensure_directory(native_libs) + with open(native_libs, 'wt', encoding="utf-8") as libs_file: + libs_file.write('\n'.join(all_outputs)) + libs_file.write('\n') elif os.path.isfile(native_libs): log.info("removing %s", native_libs) - if not self.dry_run: - os.unlink(native_libs) + os.unlink(native_libs) write_safety_flag(os.path.join(archive_root, 'EGG-INFO'), self.zip_safe()) @@ -234,11 +230,10 @@ def run(self): # noqa: C901 # is too complex (14) # FIXME self.egg_output, archive_root, verbose=self.verbose, - dry_run=self.dry_run, mode=self.gen_header(), ) if not self.keep_temp: - remove_tree(self.bdist_dir, dry_run=self.dry_run) + remove_tree(self.bdist_dir) # Add to 'Distribution.dist_files' so that the "upload" command works getattr(self.distribution, 'dist_files', []).append(( @@ -443,7 +438,6 @@ def make_zipfile( zip_filename: StrPathT, base_dir, verbose: bool = False, - dry_run: bool = False, compress=True, mode: _ZipFileMode = 'w', ) -> StrPathT: @@ -455,7 +449,7 @@ def make_zipfile( """ import zipfile - mkpath(os.path.dirname(zip_filename), dry_run=dry_run) # type: ignore[arg-type] # python/mypy#18075 + mkpath(os.path.dirname(zip_filename)) # type: ignore[arg-type] # python/mypy#18075 log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir) def visit(z, dirname, names): @@ -463,17 +457,12 @@ def visit(z, dirname, names): path = os.path.normpath(os.path.join(dirname, name)) if os.path.isfile(path): p = path[len(base_dir) + 1 :] - if not dry_run: - z.write(path, p) + z.write(path, p) log.debug("adding '%s'", p) compression = zipfile.ZIP_DEFLATED if compress else zipfile.ZIP_STORED - if not dry_run: - z = zipfile.ZipFile(zip_filename, mode, compression=compression) - for dirname, dirs, files in sorted_walk(base_dir): - visit(z, dirname, files) - z.close() - else: - for dirname, dirs, files in sorted_walk(base_dir): - visit(None, dirname, files) + z = zipfile.ZipFile(zip_filename, mode, compression=compression) + for dirname, dirs, files in sorted_walk(base_dir): + visit(z, dirname, files) + z.close() return zip_filename diff --git a/setuptools/command/bdist_wheel.py b/setuptools/command/bdist_wheel.py index 1e3f637bcc..87c62844ed 100644 --- a/setuptools/command/bdist_wheel.py +++ b/setuptools/command/bdist_wheel.py @@ -449,8 +449,7 @@ def run(self): if not self.keep_temp: log.info(f"removing {self.bdist_dir}") - if not self.dry_run: - _shutil.rmtree(self.bdist_dir) + _shutil.rmtree(self.bdist_dir) def write_wheelfile( self, wheelfile_base: str, generator: str = f"setuptools ({__version__})" diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index be833a379c..242047d55f 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -225,7 +225,7 @@ def finalize_options(self) -> None: def setup_shlib_compiler(self): compiler = self.shlib_compiler = new_compiler( - compiler=self.compiler, dry_run=self.dry_run, force=self.force + compiler=self.compiler, force=self.force ) _customize_compiler_for_shlib(compiler) @@ -353,49 +353,47 @@ def _write_stub_file(self, stub_file: str, ext: Extension, compile=False): log.info("writing stub loader for %s to %s", ext._full_name, stub_file) if compile and os.path.exists(stub_file): raise BaseError(stub_file + " already exists! Please delete.") - if not self.dry_run: - with open(stub_file, 'w', encoding="utf-8") as f: - content = '\n'.join([ - "def __bootstrap__():", - " global __bootstrap__, __file__, __loader__", - " import sys, os, pkg_resources, importlib.util" + if_dl(", dl"), - " __file__ = pkg_resources.resource_filename" - f"(__name__,{os.path.basename(ext._file_name)!r})", - " del __bootstrap__", - " if '__loader__' in globals():", - " del __loader__", - if_dl(" old_flags = sys.getdlopenflags()"), - " old_dir = os.getcwd()", - " try:", - " os.chdir(os.path.dirname(__file__))", - if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"), - " spec = importlib.util.spec_from_file_location(", - " __name__, __file__)", - " mod = importlib.util.module_from_spec(spec)", - " spec.loader.exec_module(mod)", - " finally:", - if_dl(" sys.setdlopenflags(old_flags)"), - " os.chdir(old_dir)", - "__bootstrap__()", - "", # terminal \n - ]) - f.write(content) + with open(stub_file, 'w', encoding="utf-8") as f: + content = '\n'.join([ + "def __bootstrap__():", + " global __bootstrap__, __file__, __loader__", + " import sys, os, pkg_resources, importlib.util" + if_dl(", dl"), + " __file__ = pkg_resources.resource_filename" + f"(__name__,{os.path.basename(ext._file_name)!r})", + " del __bootstrap__", + " if '__loader__' in globals():", + " del __loader__", + if_dl(" old_flags = sys.getdlopenflags()"), + " old_dir = os.getcwd()", + " try:", + " os.chdir(os.path.dirname(__file__))", + if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"), + " spec = importlib.util.spec_from_file_location(", + " __name__, __file__)", + " mod = importlib.util.module_from_spec(spec)", + " spec.loader.exec_module(mod)", + " finally:", + if_dl(" sys.setdlopenflags(old_flags)"), + " os.chdir(old_dir)", + "__bootstrap__()", + "", # terminal \n + ]) + f.write(content) if compile: self._compile_and_remove_stub(stub_file) def _compile_and_remove_stub(self, stub_file: str): from distutils.util import byte_compile - byte_compile([stub_file], optimize=0, force=True, dry_run=self.dry_run) + byte_compile([stub_file], optimize=0, force=True) optimize = self.get_finalized_command('install_lib').optimize if optimize > 0: byte_compile( [stub_file], optimize=optimize, force=True, - dry_run=self.dry_run, ) - if os.path.exists(stub_file) and not self.dry_run: + if os.path.exists(stub_file): os.unlink(stub_file) diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py index 7eee29d491..fdcccc78d6 100644 --- a/setuptools/command/develop.py +++ b/setuptools/command/develop.py @@ -119,9 +119,8 @@ def install_for_development(self) -> None: # create an .egg-link in the installation dir, pointing to our egg log.info("Creating %s (link to %s)", self.egg_link, self.egg_base) - if not self.dry_run: - with open(self.egg_link, "w", encoding="utf-8") as f: - f.write(self.egg_path + "\n" + self.setup_path) + with open(self.egg_link, "w", encoding="utf-8") as f: + f.write(self.egg_path + "\n" + self.setup_path) # postprocess the installed distro, fixing up .pth, installing scripts, # and handling requirements self.process_distribution(None, self.dist, not self.no_deps) @@ -138,10 +137,8 @@ def uninstall_link(self) -> None: if contents not in ([self.egg_path], [self.egg_path, self.setup_path]): log.warn("Link points to %s: uninstall aborted", contents) return - if not self.dry_run: - os.unlink(self.egg_link) - if not self.dry_run: - self.update_pth(self.dist) # remove any .pth link to us + os.unlink(self.egg_link) + self.update_pth(self.dist) # remove any .pth link to us if self.distribution.scripts: # XXX should also check for entry point scripts! log.warn("Note: you must uninstall or replace scripts manually!") diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index eb1b4c1fcc..90bb3f80c5 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -205,7 +205,6 @@ def initialize_options(self): # always come from the standard configuration file(s)' "easy_install" # section, even if this is a "develop" or "install" command, or some # other embedding. - self._dry_run = None self.verbose = self.distribution.verbose self.distribution._set_command_options( self, self.distribution.get_option_dict('easy_install') @@ -221,8 +220,6 @@ def delete_blockers(self, blockers) -> None: def _delete_path(self, path): log.info("Deleting %s", path) - if self.dry_run: - return is_tree = os.path.isdir(path) and not os.path.islink(path) remover = _rmtree if is_tree else os.unlink @@ -868,9 +865,6 @@ def write_script(self, script_name, contents, mode: str = "t", blockers=()) -> N target = os.path.join(self.script_dir, script_name) self.add_output(target) - if self.dry_run: - return - mask = current_umask() ensure_directory(target) if os.path.exists(target): @@ -945,15 +939,14 @@ def install_egg(self, egg_path, tmpdir): os.path.basename(egg_path), ) destination = os.path.abspath(destination) - if not self.dry_run: - ensure_directory(destination) + ensure_directory(destination) dist = self.egg_distribution(egg_path) if not ( os.path.exists(destination) and os.path.samefile(egg_path, destination) ): if os.path.isdir(destination) and not os.path.islink(destination): - dir_util.remove_tree(destination, dry_run=self.dry_run) + dir_util.remove_tree(destination) elif os.path.exists(destination): self.execute( os.unlink, @@ -1036,7 +1029,6 @@ def install_exe(self, dist_filename, tmpdir): egg_path, egg_tmp, verbose=self.verbose, - dry_run=self.dry_run, ) # install the .egg return self.install_egg(egg_path, tmpdir) @@ -1099,10 +1091,9 @@ def install_wheel(self, wheel_path, tmpdir): assert wheel.is_compatible() destination = os.path.join(self.install_dir, wheel.egg_name()) destination = os.path.abspath(destination) - if not self.dry_run: - ensure_directory(destination) + ensure_directory(destination) if os.path.isdir(destination) and not os.path.islink(destination): - dir_util.remove_tree(destination, dry_run=self.dry_run) + dir_util.remove_tree(destination) elif os.path.exists(destination): self.execute( os.unlink, @@ -1185,8 +1176,6 @@ def run_setup(self, setup_script, setup_base, args) -> None: args.insert(0, '-' + v) elif self.verbose < 2: args.insert(0, '-q') - if self.dry_run: - args.insert(0, '-n') log.info("Running %s %s", setup_script[len(setup_base) + 1 :], ' '.join(args)) try: run_setup(setup_script, args) @@ -1210,7 +1199,7 @@ def build_and_install(self, setup_script, setup_base): for key in all_eggs for dist in all_eggs[key] ] - if not eggs and not self.dry_run: + if not eggs: log.warn("No eggs found in %s (setup script problem?)", dist_dir) return eggs finally: @@ -1244,7 +1233,7 @@ def _set_fetcher_options(self, base): cfg_filename = os.path.join(base, 'setup.cfg') setopt.edit_config(cfg_filename, settings) - def update_pth(self, dist) -> None: # noqa: C901 # is too complex (11) # FIXME + def update_pth(self, dist) -> None: if self.pth_file is None: return @@ -1269,9 +1258,6 @@ def update_pth(self, dist) -> None: # noqa: C901 # is too complex (11) # FIXM if dist.location not in self.shadow_path: self.shadow_path.append(dist.location) - if self.dry_run: - return - self.pth_file.save() if dist.key != 'setuptools': @@ -1303,14 +1289,13 @@ def pf(src, dst): elif dst.endswith('.dll') or dst.endswith('.so'): to_chmod.append(dst) self.unpack_progress(src, dst) - return not self.dry_run and dst or None + return dst or None unpack_archive(egg_path, destination, pf) self.byte_compile(to_compile) - if not self.dry_run: - for f in to_chmod: - mode = ((os.stat(f)[stat.ST_MODE]) | 0o555) & 0o7755 - chmod(f, mode) + for f in to_chmod: + mode = ((os.stat(f)[stat.ST_MODE]) | 0o555) & 0o7755 + chmod(f, mode) def byte_compile(self, to_compile) -> None: if sys.dont_write_bytecode: @@ -1322,13 +1307,12 @@ def byte_compile(self, to_compile) -> None: # try to make the byte compile messages quieter log.set_verbosity(self.verbose - 1) - byte_compile(to_compile, optimize=0, force=True, dry_run=self.dry_run) + byte_compile(to_compile, optimize=0, force=True) if self.optimize: byte_compile( to_compile, optimize=self.optimize, force=True, - dry_run=self.dry_run, ) finally: log.set_verbosity(self.verbose) # restore original verbosity diff --git a/setuptools/command/editable_wheel.py b/setuptools/command/editable_wheel.py index 1a544ec258..74f0772fc8 100644 --- a/setuptools/command/editable_wheel.py +++ b/setuptools/command/editable_wheel.py @@ -785,7 +785,6 @@ def __init__(self, distribution, installation_dir, editable_name, src_root) -> N self.installation_dir = installation_dir self.editable_name = editable_name self.outputs: list[str] = [] - self.dry_run = False def _get_nspkg_file(self): """Installation target.""" diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index f77631168f..e150a9f62e 100644 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -278,16 +278,14 @@ def write_file(self, what, filename, data) -> None: """ log.info("writing %s to %s", what, filename) data = data.encode("utf-8") - if not self.dry_run: - f = open(filename, 'wb') - f.write(data) - f.close() + f = open(filename, 'wb') + f.write(data) + f.close() def delete_file(self, filename) -> None: """Delete `filename` (if not a dry run) after announcing it""" log.info("deleting %s", filename) - if not self.dry_run: - os.unlink(filename) + os.unlink(filename) def run(self) -> None: # Pre-load to avoid iterating over entry-points while an empty .egg-info @@ -649,21 +647,20 @@ def write_file(filename, contents) -> None: def write_pkg_info(cmd, basename, filename) -> None: log.info("writing %s", filename) - if not cmd.dry_run: - metadata = cmd.distribution.metadata - metadata.version, oldver = cmd.egg_version, metadata.version - metadata.name, oldname = cmd.egg_name, metadata.name + metadata = cmd.distribution.metadata + metadata.version, oldver = cmd.egg_version, metadata.version + metadata.name, oldname = cmd.egg_name, metadata.name - try: - # write unescaped data to PKG-INFO, so older pkg_resources - # can still parse it - metadata.write_pkg_info(cmd.egg_info) - finally: - metadata.name, metadata.version = oldname, oldver + try: + # write unescaped data to PKG-INFO, so older pkg_resources + # can still parse it + metadata.write_pkg_info(cmd.egg_info) + finally: + metadata.name, metadata.version = oldname, oldver - safe = getattr(cmd.distribution, 'zip_safe', None) + safe = getattr(cmd.distribution, 'zip_safe', None) - bdist_egg.write_safety_flag(cmd.egg_info, safe) + bdist_egg.write_safety_flag(cmd.egg_info, safe) def warn_depends_obsolete(cmd, basename, filename) -> None: diff --git a/setuptools/command/install_egg_info.py b/setuptools/command/install_egg_info.py index 44f22ccf51..46138d2fd7 100644 --- a/setuptools/command/install_egg_info.py +++ b/setuptools/command/install_egg_info.py @@ -31,11 +31,10 @@ def finalize_options(self) -> None: def run(self) -> None: self.run_command('egg_info') if os.path.isdir(self.target) and not os.path.islink(self.target): - dir_util.remove_tree(self.target, dry_run=self.dry_run) + dir_util.remove_tree(self.target) elif os.path.exists(self.target): self.execute(os.unlink, (self.target,), "Removing " + self.target) - if not self.dry_run: - ensure_directory(self.target) + ensure_directory(self.target) self.execute(self.copytree, (), f"Copying {self.source} to {self.target}") self.install_namespaces() diff --git a/setuptools/command/install_scripts.py b/setuptools/command/install_scripts.py index 4401cf693d..b34d68b894 100644 --- a/setuptools/command/install_scripts.py +++ b/setuptools/command/install_scripts.py @@ -66,8 +66,7 @@ def write_script(self, script_name, contents, mode: str = "t", *ignored) -> None encoding = None if "b" in mode else "utf-8" mask = current_umask() - if not self.dry_run: - ensure_directory(target) - with open(target, "w" + mode, encoding=encoding) as f: - f.write(contents) - chmod(target, 0o777 - mask) + ensure_directory(target) + with open(target, "w" + mode, encoding=encoding) as f: + f.write(contents) + chmod(target, 0o777 - mask) diff --git a/setuptools/command/rotate.py b/setuptools/command/rotate.py index acdce07baa..9ff72f5600 100644 --- a/setuptools/command/rotate.py +++ b/setuptools/command/rotate.py @@ -58,8 +58,7 @@ def run(self) -> None: files = files[self.keep :] for t, f in files: log.info("Deleting %s", f) - if not self.dry_run: - if os.path.isdir(f): - _shutil.rmtree(f) - else: - os.unlink(f) + if os.path.isdir(f): + _shutil.rmtree(f) + else: + os.unlink(f) diff --git a/setuptools/command/saveopts.py b/setuptools/command/saveopts.py index 2a2cbce6e2..76c3cdbf37 100644 --- a/setuptools/command/saveopts.py +++ b/setuptools/command/saveopts.py @@ -18,4 +18,4 @@ def run(self) -> None: if src == "command line": settings.setdefault(cmd, {})[opt] = val - edit_config(self.filename, settings, self.dry_run) + edit_config(self.filename, settings) diff --git a/setuptools/command/setopt.py b/setuptools/command/setopt.py index 678a0593d6..c9f1bdcea1 100644 --- a/setuptools/command/setopt.py +++ b/setuptools/command/setopt.py @@ -27,7 +27,7 @@ def config_file(kind="local"): raise ValueError("config_file() type must be 'local', 'global', or 'user'", kind) -def edit_config(filename, settings, dry_run=False): +def edit_config(filename, settings): """Edit a configuration file to include `settings` `settings` is a dictionary of dictionaries or ``None`` values, keyed by @@ -64,9 +64,8 @@ def edit_config(filename, settings, dry_run=False): opts.set(section, option, value) log.info("Writing %s", filename) - if not dry_run: - with open(filename, 'w', encoding="utf-8") as f: - opts.write(f) + with open(filename, 'w', encoding="utf-8") as f: + opts.write(f) class option_base(Command): @@ -137,5 +136,4 @@ def run(self) -> None: edit_config( self.filename, {self.command: {self.option.replace('-', '_'): self.set_value}}, - self.dry_run, ) diff --git a/setuptools/dist.py b/setuptools/dist.py index fa734c651f..4b9c3a6c3b 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -614,7 +614,7 @@ def _parse_config_files(self, filenames=None): # noqa: C901 alias = self.negative_opt.get(opt) if alias: val = not strtobool(val) - elif opt in ('verbose', 'dry_run'): # ugh! + elif opt in ('verbose',): # ugh! val = strtobool(val) try: diff --git a/setuptools/namespaces.py b/setuptools/namespaces.py index 85ea2ebd65..7760f07fb4 100644 --- a/setuptools/namespaces.py +++ b/setuptools/namespaces.py @@ -20,11 +20,6 @@ def install_namespaces(self) -> None: log.info("Installing %s", filename) lines = map(self._gen_nspkg_line, nsp) - if self.dry_run: - # always generate the lines, even in dry run - list(lines) - return - with open(filename, 'wt', encoding=py312.PTH_ENCODING) as f: # Python<3.13 requires encoding="locale" instead of "utf-8" # See: python/cpython#77102 From 2b11dff7a0f2e6046027ccc0d0e37708b32e6914 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 9 Mar 2025 15:03:31 -0400 Subject: [PATCH 4/6] Add news fragment. --- newsfragments/4872.removal.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/4872.removal.rst diff --git a/newsfragments/4872.removal.rst b/newsfragments/4872.removal.rst new file mode 100644 index 0000000000..16a08c6273 --- /dev/null +++ b/newsfragments/4872.removal.rst @@ -0,0 +1 @@ +Removed support for the --dry-run parameter to setup.py. This one feature by its nature threads through lots of core and ancillary functionality, adding complexity and friction. Removal of this parameter will help decouple the compiler functionality from distutils and thus the eventual full integration of distutils. These changes do affect some class and function signatures, so any derivative functionality may require some compatibility shims to support their expected interface. Please report any issues to the Setuptools project for investigation. From 75f81b9aa89b716805d94afa001e083c794eb5a1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 9 Mar 2025 15:09:16 -0400 Subject: [PATCH 5/6] Add a dry_run property for temporary compatibility. --- setuptools/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 64464dfaa3..2252b32448 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -167,6 +167,12 @@ class Command(_Command): command_consumes_arguments = False distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution + dry_run = False # type: ignore[assignment] # pyright: ignore[reportAssignmentType] (until #4689; see #4872) + """ + For compatibility with vendored bdist_wheel. + https://github.com/pypa/setuptools/pull/4872/files#r1986395142 + """ + def __init__(self, dist: Distribution, **kw) -> None: """ Construct the command for dist, updating From 92f34d7038508929dc6f073440d9af15cf930783 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 16 Mar 2025 12:24:01 -0400 Subject: [PATCH 6/6] Remove reliance on --dry-run parameter in test. --- setuptools/tests/test_logging.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setuptools/tests/test_logging.py b/setuptools/tests/test_logging.py index ea58001e93..5d487bda2d 100644 --- a/setuptools/tests/test_logging.py +++ b/setuptools/tests/test_logging.py @@ -19,9 +19,9 @@ @pytest.mark.parametrize( - ('flag', 'expected_level'), [("--dry-run", "INFO"), ("--verbose", "DEBUG")] + ('flags', 'expected_level'), [([], "INFO"), (["--verbose"], "DEBUG")] ) -def test_verbosity_level(tmp_path, monkeypatch, flag, expected_level): +def test_verbosity_level(tmp_path, monkeypatch, flags, expected_level): """Make sure the correct verbosity level is set (issue #3038)""" import setuptools # noqa: F401 # import setuptools to monkeypatch distutils @@ -35,7 +35,7 @@ def test_verbosity_level(tmp_path, monkeypatch, flag, expected_level): setup_script = tmp_path / "setup.py" setup_script.write_text(setup_py, encoding="utf-8") dist = distutils.core.run_setup(setup_script, stop_after="init") - dist.script_args = [flag, "sdist"] + dist.script_args = flags + ["sdist"] dist.parse_command_line() # <- where the log level is set log_level = logger.getEffectiveLevel() log_level_name = logging.getLevelName(log_level)