summaryrefslogtreecommitdiffstats
path: root/contrib/python
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2026-02-21 17:34:55 +0300
committerrobot-piglet <[email protected]>2026-02-21 18:03:01 +0300
commit4a2bb48a518e079622abeee860d5d3040a401bba (patch)
tree4efcdbebaa32ad3c7e7abb57c1cbdd537d057148 /contrib/python
parent274b52970ded8854a34077e3d0caa0164c359c76 (diff)
Intermediate changes
commit_hash:68b40ed025a133871e4c19d3a0290aa01d1c41c1
Diffstat (limited to 'contrib/python')
-rw-r--r--contrib/python/setuptools/py3/.dist-info/METADATA6
-rw-r--r--contrib/python/setuptools/py3/setuptools/__init__.py6
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/archive_util.py62
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/cmd.py31
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_dumb.py2
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_rpm.py43
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/build_clib.py4
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/build_ext.py1
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/build_py.py5
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/build_scripts.py36
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/clean.py15
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/config.py12
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/install_egg_info.py7
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/install_lib.py2
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/install_scripts.py9
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py4
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/base.py24
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/cygwin.py8
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/msvc.py4
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/zos.py4
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/dir_util.py36
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/dist.py4
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/file_util.py14
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/spawn.py6
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/util.py66
-rw-r--r--contrib/python/setuptools/py3/setuptools/build_meta.py4
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/alias.py2
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/bdist_egg.py37
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py3
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/build_ext.py70
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/editable_wheel.py1
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/egg_info.py29
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/install_egg_info.py5
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/install_scripts.py9
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/rotate.py9
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/saveopts.py2
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/setopt.py8
-rw-r--r--contrib/python/setuptools/py3/setuptools/dist.py2
-rw-r--r--contrib/python/setuptools/py3/setuptools/namespaces.py5
-rw-r--r--contrib/python/setuptools/py3/ya.make2
-rw-r--r--contrib/python/wcwidth/py3/.dist-info/METADATA7
-rw-r--r--contrib/python/wcwidth/py3/tests/test_textwrap.py186
-rw-r--r--contrib/python/wcwidth/py3/wcwidth/__init__.py2
-rw-r--r--contrib/python/wcwidth/py3/wcwidth/textwrap.py176
-rw-r--r--contrib/python/wcwidth/py3/ya.make2
45 files changed, 514 insertions, 458 deletions
diff --git a/contrib/python/setuptools/py3/.dist-info/METADATA b/contrib/python/setuptools/py3/.dist-info/METADATA
index dba85b49a65..b7dbd1b7760 100644
--- a/contrib/python/setuptools/py3/.dist-info/METADATA
+++ b/contrib/python/setuptools/py3/.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: setuptools
-Version: 80.10.2
+Version: 81.0.0
Summary: Easily download, build, install, upgrade, and uninstall Python packages
Author-email: Python Packaging Authority <[email protected]>
License-Expression: MIT
@@ -69,14 +69,14 @@ Requires-Dist: more_itertools; extra == "core"
Provides-Extra: check
Requires-Dist: pytest-checkdocs>=2.4; extra == "check"
Requires-Dist: pytest-ruff>=0.2.1; sys_platform != "cygwin" and extra == "check"
-Requires-Dist: ruff>=0.8.0; sys_platform != "cygwin" and extra == "check"
+Requires-Dist: ruff>=0.13.0; sys_platform != "cygwin" and extra == "check"
Provides-Extra: cover
Requires-Dist: pytest-cov; extra == "cover"
Provides-Extra: enabler
Requires-Dist: pytest-enabler>=2.2; extra == "enabler"
Provides-Extra: type
Requires-Dist: pytest-mypy; extra == "type"
-Requires-Dist: mypy==1.14.*; extra == "type"
+Requires-Dist: mypy==1.18.*; extra == "type"
Requires-Dist: importlib_metadata>=7.0.2; python_version < "3.10" and extra == "type"
Requires-Dist: jaraco.develop>=7.21; sys_platform != "cygwin" and extra == "type"
Dynamic: license-file
diff --git a/contrib/python/setuptools/py3/setuptools/__init__.py b/contrib/python/setuptools/py3/setuptools/__init__.py
index b3e78edab6e..87ca47bbc9f 100644
--- a/contrib/python/setuptools/py3/setuptools/__init__.py
+++ b/contrib/python/setuptools/py3/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
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/archive_util.py b/contrib/python/setuptools/py3/setuptools/_distutils/archive_util.py
index d860f552723..4a7fb9c9e7a 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/archive_util.py
+++ b/contrib/python/setuptools/py3/setuptools/_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 make_tarball(
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)
+ 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, ''))
+ 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/contrib/python/setuptools/py3/setuptools/_distutils/cmd.py b/contrib/python/setuptools/py3/setuptools/_distutils/cmd.py
index 241621bd513..530cc108d47 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/cmd.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/cmd.py
@@ -91,13 +91,8 @@ class Command:
# 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 @@ class Command:
# 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 @@ class Command:
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 @@ class Command:
preserve_times,
not self.force,
link,
- dry_run=self.dry_run,
)
def copy_tree(
@@ -447,7 +430,6 @@ class Command:
preserve_times,
preserve_symlinks,
not self.force,
- dry_run=self.dry_run,
)
@overload
@@ -465,7 +447,7 @@ class Command:
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 @@ class Command:
"""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 @@ class Command:
format,
root_dir,
base_dir,
- dry_run=self.dry_run,
owner=owner,
group=group,
)
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_dumb.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_dumb.py
index ccad66f4312..c49ef253b47 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_dumb.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_dumb.py
@@ -138,4 +138,4 @@ class bdist_dumb(Command):
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/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_rpm.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_rpm.py
index 357b4e861e2..dfa5f99dda0 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_rpm.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_rpm.py
@@ -378,30 +378,29 @@ class bdist_rpm(Command):
self.spawn(rpm_cmd)
- if not self.dry_run:
- if self.distribution.has_ext_modules():
- pyversion = get_python_version()
- else:
- pyversion = 'any'
+ 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.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 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/contrib/python/setuptools/py3/setuptools/_distutils/command/build_clib.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/build_clib.py
index 8b65b3d8ece..8de5df239c7 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/build_clib.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/build_clib.py
@@ -88,9 +88,7 @@ class build_clib(Command):
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/contrib/python/setuptools/py3/setuptools/_distutils/command/build_ext.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/build_ext.py
index ec45b4403e5..df623d7e0ef 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/build_ext.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/build_ext.py
@@ -326,7 +326,6 @@ class build_ext(Command):
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/contrib/python/setuptools/py3/setuptools/_distutils/command/build_py.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/build_py.py
index a20b076fe7c..d6ec7d10d2a 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/build_py.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/build_py.py
@@ -394,14 +394,11 @@ class build_py(Command):
# 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/contrib/python/setuptools/py3/setuptools/_distutils/command/build_scripts.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/build_scripts.py
index b86ee6e6ba6..0b551d11be0 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/build_scripts.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/build_scripts.py
@@ -87,30 +87,24 @@ class build_scripts(Command):
# 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 = "#!" + self.executable + 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 = "#!" + self.executable + 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 @@ class build_scripts(Command):
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/contrib/python/setuptools/py3/setuptools/_distutils/command/clean.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/clean.py
index 23427aba211..fc1711be8ef 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/clean.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/clean.py
@@ -55,7 +55,7 @@ class clean(Command):
# remove the build/temp.<plat> 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 @@ class clean(Command):
# 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/contrib/python/setuptools/py3/setuptools/_distutils/command/config.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/config.py
index c825765c873..9818418f267 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/config.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/config.py
@@ -90,9 +90,7 @@ class config(Command):
if not, make it one.
"""
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)
@@ -157,14 +155,6 @@ class config(Command):
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/contrib/python/setuptools/py3/setuptools/_distutils/command/install_egg_info.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/install_egg_info.py
index 230e94ab46f..6840b34ec2b 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/install_egg_info.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/install_egg_info.py
@@ -44,7 +44,7 @@ class install_egg_info(Command):
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 @@ class install_egg_info(Command):
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/contrib/python/setuptools/py3/setuptools/_distutils/command/install_lib.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/install_lib.py
index 2aababf8004..9909a61762b 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/install_lib.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/install_lib.py
@@ -142,7 +142,6 @@ class install_lib(Command):
optimize=0,
force=self.force,
prefix=install_root,
- dry_run=self.dry_run,
)
if self.optimize > 0:
byte_compile(
@@ -151,7 +150,6 @@ class install_lib(Command):
force=self.force,
prefix=install_root,
verbose=self.verbose,
- dry_run=self.dry_run,
)
# -- Utility methods -----------------------------------------------
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/install_scripts.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/install_scripts.py
index 92e8694111e..33d235d976e 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/install_scripts.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/install_scripts.py
@@ -48,12 +48,9 @@ class install_scripts(Command):
# 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/contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py
index b3bf0c326aa..e64cc82902a 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py
@@ -450,7 +450,7 @@ class sdist(Command):
# 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 @@ class sdist(Command):
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/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/base.py b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/base.py
index 5efd2a39d6a..ea6b5458ef2 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/base.py
+++ b/contrib/python/setuptools/py3/setuptools/_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 @@ class Compiler:
"""
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 @@ int main (int argc, char **argv) {{
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 @@ int main (int argc, char **argv) {{
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/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/cygwin.py b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/cygwin.py
index bfabbb306e7..f349e9decd0 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/cygwin.py
+++ b/contrib/python/setuptools/py3/setuptools/_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/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/msvc.py b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/msvc.py
index 6db062a9e71..1f50fa50880 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/msvc.py
+++ b/contrib/python/setuptools/py3/setuptools/_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/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/zos.py b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/zos.py
index 82d017fc90d..681c63166be 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/zos.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/zos.py
@@ -136,8 +136,8 @@ class Compiler(unix.Compiler):
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/contrib/python/setuptools/py3/setuptools/_distutils/dir_util.py b/contrib/python/setuptools/py3/setuptools/_distutils/dir_util.py
index d9782602cfe..61b4575aa03 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/dir_util.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/dir_util.py
@@ -45,7 +45,7 @@ wrapper = SkipRepeatAbsolutePaths().wrap
@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/contrib/python/setuptools/py3/setuptools/_distutils/dist.py b/contrib/python/setuptools/py3/setuptools/_distutils/dist.py
index 37b788df925..e04e487b58b 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/dist.py
+++ b/contrib/python/setuptools/py3/setuptools/_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 @@ Common commands: (see '--help-commands' for more)
# 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 @@ Common commands: (see '--help-commands' for more)
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/contrib/python/setuptools/py3/setuptools/_distutils/file_util.py b/contrib/python/setuptools/py3/setuptools/_distutils/file_util.py
index 0acc8cb84bd..a9724b17797 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/file_util.py
+++ b/contrib/python/setuptools/py3/setuptools/_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/contrib/python/setuptools/py3/setuptools/_distutils/spawn.py b/contrib/python/setuptools/py3/setuptools/_distutils/spawn.py
index 973668f2684..e47f1d19272 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/spawn.py
+++ b/contrib/python/setuptools/py3/setuptools/_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/contrib/python/setuptools/py3/setuptools/_distutils/util.py b/contrib/python/setuptools/py3/setuptools/_distutils/util.py
index 6dbe049f427..47bb5af5e4b 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/util.py
+++ b/contrib/python/setuptools/py3/setuptools/_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.
+ # 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"""
+ 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 @@ byte_compile(files, optimize={optimize!r}, force={force!r},
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)
diff --git a/contrib/python/setuptools/py3/setuptools/build_meta.py b/contrib/python/setuptools/py3/setuptools/build_meta.py
index 0dc04f6cbb7..1d93b6193be 100644
--- a/contrib/python/setuptools/py3/setuptools/build_meta.py
+++ b/contrib/python/setuptools/py3/setuptools/build_meta.py
@@ -357,7 +357,9 @@ class _BuildMetaBackend(_ConfigSettingsTranslator):
candidates = [f for f in dirs if f.endswith(suffix)]
if len(candidates) != 0 or len(dirs) != 1:
- assert len(candidates) == 1, f"Multiple {suffix} directories found"
+ assert len(candidates) == 1, (
+ f"Exactly one {suffix} should have been produced, but found {len(candidates)}: {candidates}"
+ )
return Path(parent, candidates[0])
msg = f"No {suffix} directory found in {metadata_directory}"
diff --git a/contrib/python/setuptools/py3/setuptools/command/alias.py b/contrib/python/setuptools/py3/setuptools/command/alias.py
index b8d74af71d4..0ffa3116d60 100644
--- a/contrib/python/setuptools/py3/setuptools/command/alias.py
+++ b/contrib/python/setuptools/py3/setuptools/command/alias.py
@@ -61,7 +61,7 @@ class alias(option_base):
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/contrib/python/setuptools/py3/setuptools/command/bdist_egg.py b/contrib/python/setuptools/py3/setuptools/command/bdist_egg.py
index ab452680f1a..dbabeccb9df 100644
--- a/contrib/python/setuptools/py3/setuptools/command/bdist_egg.py
+++ b/contrib/python/setuptools/py3/setuptools/command/bdist_egg.py
@@ -159,7 +159,6 @@ class bdist_egg(Command):
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
@@ -186,8 +185,7 @@ class bdist_egg(Command):
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, '/')
@@ -209,15 +207,13 @@ class bdist_egg(Command):
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())
@@ -235,11 +231,10 @@ class bdist_egg(Command):
self.egg_output,
archive_root,
verbose=self.verbose,
- dry_run=self.dry_run, # type: ignore[arg-type] # Is an actual boolean in vendored _distutils
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((
@@ -446,7 +441,6 @@ def make_zipfile(
zip_filename: StrPathT,
base_dir,
verbose: bool = False,
- dry_run: bool = False,
compress=True,
mode: _ZipFileMode = 'w',
) -> StrPathT:
@@ -458,7 +452,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):
@@ -466,17 +460,12 @@ def make_zipfile(
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/contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py b/contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py
index 91ed00170e9..3bdfa0b35ae 100644
--- a/contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py
+++ b/contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py
@@ -449,8 +449,7 @@ class bdist_wheel(Command):
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/contrib/python/setuptools/py3/setuptools/command/build_ext.py b/contrib/python/setuptools/py3/setuptools/command/build_ext.py
index 334fda62b8a..dbb956db8d0 100644
--- a/contrib/python/setuptools/py3/setuptools/command/build_ext.py
+++ b/contrib/python/setuptools/py3/setuptools/command/build_ext.py
@@ -223,7 +223,7 @@ class build_ext(_build_ext):
def setup_shlib_compiler(self) -> None:
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)
@@ -351,54 +351,52 @@ class build_ext(_build_ext):
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 = (
- textwrap
- .dedent(f"""
- def __bootstrap__():
- global __bootstrap__, __file__, __loader__
- import sys, os, importlib.resources as irs, importlib.util
- #rtld import dl
- with irs.files(__name__).joinpath(
- {os.path.basename(ext._file_name)!r}) as __file__:
- del __bootstrap__
- if '__loader__' in globals():
- del __loader__
- #rtld old_flags = sys.getdlopenflags()
- old_dir = os.getcwd()
- try:
- os.chdir(os.path.dirname(__file__))
- #rtld 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:
- #rtld sys.setdlopenflags(old_flags)
- os.chdir(old_dir)
- __bootstrap__()
- """)
- .lstrip()
- .replace('#rtld', '#rtld' * (not have_rtld))
- )
- f.write(content)
+ with open(stub_file, 'w', encoding="utf-8") as f:
+ content = (
+ textwrap
+ .dedent(f"""
+ def __bootstrap__():
+ global __bootstrap__, __file__, __loader__
+ import sys, os, importlib.resources as irs, importlib.util
+ #rtld import dl
+ with irs.files(__name__).joinpath(
+ {os.path.basename(ext._file_name)!r}) as __file__:
+ del __bootstrap__
+ if '__loader__' in globals():
+ del __loader__
+ #rtld old_flags = sys.getdlopenflags()
+ old_dir = os.getcwd()
+ try:
+ os.chdir(os.path.dirname(__file__))
+ #rtld 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:
+ #rtld sys.setdlopenflags(old_flags)
+ os.chdir(old_dir)
+ __bootstrap__()
+ """)
+ .lstrip()
+ .replace('#rtld', '#rtld' * (not have_rtld))
+ )
+ 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/contrib/python/setuptools/py3/setuptools/command/editable_wheel.py b/contrib/python/setuptools/py3/setuptools/command/editable_wheel.py
index d1d9e79b39c..6f44f136434 100644
--- a/contrib/python/setuptools/py3/setuptools/command/editable_wheel.py
+++ b/contrib/python/setuptools/py3/setuptools/command/editable_wheel.py
@@ -801,7 +801,6 @@ class _NamespaceInstaller(namespaces.Installer):
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/contrib/python/setuptools/py3/setuptools/command/egg_info.py b/contrib/python/setuptools/py3/setuptools/command/egg_info.py
index d9de297ecfa..a5b5932702a 100644
--- a/contrib/python/setuptools/py3/setuptools/command/egg_info.py
+++ b/contrib/python/setuptools/py3/setuptools/command/egg_info.py
@@ -280,16 +280,14 @@ class egg_info(InfoCommon, Command):
"""
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
@@ -650,19 +648,18 @@ 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:
- metadata.write_pkg_info(cmd.egg_info)
- finally:
- metadata.name, metadata.version = oldname, oldver
+ try:
+ 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/contrib/python/setuptools/py3/setuptools/command/install_egg_info.py b/contrib/python/setuptools/py3/setuptools/command/install_egg_info.py
index 44f22ccf51c..46138d2fd7d 100644
--- a/contrib/python/setuptools/py3/setuptools/command/install_egg_info.py
+++ b/contrib/python/setuptools/py3/setuptools/command/install_egg_info.py
@@ -31,11 +31,10 @@ class install_egg_info(namespaces.Installer, Command):
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/contrib/python/setuptools/py3/setuptools/command/install_scripts.py b/contrib/python/setuptools/py3/setuptools/command/install_scripts.py
index 537181e3215..5d7d3869ec5 100644
--- a/contrib/python/setuptools/py3/setuptools/command/install_scripts.py
+++ b/contrib/python/setuptools/py3/setuptools/command/install_scripts.py
@@ -60,8 +60,7 @@ class install_scripts(orig.install_scripts):
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/contrib/python/setuptools/py3/setuptools/command/rotate.py b/contrib/python/setuptools/py3/setuptools/command/rotate.py
index acdce07baaf..9ff72f56006 100644
--- a/contrib/python/setuptools/py3/setuptools/command/rotate.py
+++ b/contrib/python/setuptools/py3/setuptools/command/rotate.py
@@ -58,8 +58,7 @@ class rotate(Command):
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/contrib/python/setuptools/py3/setuptools/command/saveopts.py b/contrib/python/setuptools/py3/setuptools/command/saveopts.py
index 2a2cbce6e2a..76c3cdbf379 100644
--- a/contrib/python/setuptools/py3/setuptools/command/saveopts.py
+++ b/contrib/python/setuptools/py3/setuptools/command/saveopts.py
@@ -18,4 +18,4 @@ class saveopts(option_base):
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/contrib/python/setuptools/py3/setuptools/command/setopt.py b/contrib/python/setuptools/py3/setuptools/command/setopt.py
index 43cb5939993..349041ecb65 100644
--- a/contrib/python/setuptools/py3/setuptools/command/setopt.py
+++ b/contrib/python/setuptools/py3/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) -> None:
+def edit_config(filename, settings) -> None:
"""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) -> None:
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 @@ class setopt(option_base):
edit_config(
self.filename,
{self.command: {self.option.replace('-', '_'): self.set_value}},
- self.dry_run,
)
diff --git a/contrib/python/setuptools/py3/setuptools/dist.py b/contrib/python/setuptools/py3/setuptools/dist.py
index a3d1e5f91e1..a224b3ee44c 100644
--- a/contrib/python/setuptools/py3/setuptools/dist.py
+++ b/contrib/python/setuptools/py3/setuptools/dist.py
@@ -614,7 +614,7 @@ class Distribution(_Distribution):
alias = self.negative_opt.get(opt)
if alias:
val = not strtobool(val)
- elif opt in ('verbose', 'dry_run'): # ugh!
+ elif opt == 'verbose':
val = strtobool(val)
try:
diff --git a/contrib/python/setuptools/py3/setuptools/namespaces.py b/contrib/python/setuptools/py3/setuptools/namespaces.py
index 85ea2ebd654..7760f07fb46 100644
--- a/contrib/python/setuptools/py3/setuptools/namespaces.py
+++ b/contrib/python/setuptools/py3/setuptools/namespaces.py
@@ -20,11 +20,6 @@ class Installer:
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
diff --git a/contrib/python/setuptools/py3/ya.make b/contrib/python/setuptools/py3/ya.make
index 6d3c72a3b87..12934bfb9de 100644
--- a/contrib/python/setuptools/py3/ya.make
+++ b/contrib/python/setuptools/py3/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(80.10.2)
+VERSION(81.0.0)
LICENSE(MIT)
diff --git a/contrib/python/wcwidth/py3/.dist-info/METADATA b/contrib/python/wcwidth/py3/.dist-info/METADATA
index d8c25b9eee9..f6f0235df8f 100644
--- a/contrib/python/wcwidth/py3/.dist-info/METADATA
+++ b/contrib/python/wcwidth/py3/.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: wcwidth
-Version: 0.5.3
+Version: 0.6.0
Summary: Measures the displayed width of unicode strings in a terminal
Project-URL: Homepage, https://github.com/jquast/wcwidth
Author-email: Jeff Quast <[email protected]>
@@ -486,6 +486,11 @@ languages.
History
=======
+0.6.0 *2026-02-06*
+ * **New** Parameters ``expand_tabs``, ``replace_whitespace``, ``fix_sentence_endings``,
+ ``drop_whitespace``, ``max_lines``, and ``placeholder`` for `wrap()`_, completing stdlib
+ `textwrap.wrap()`_ compatibility.
+
0.5.3 *2026-01-30*
* **Bugfix** Brahmic using Virama conjunct formation. `Issue #155`_, `PR #204`_.
diff --git a/contrib/python/wcwidth/py3/tests/test_textwrap.py b/contrib/python/wcwidth/py3/tests/test_textwrap.py
index e39c27cd269..094c8e56725 100644
--- a/contrib/python/wcwidth/py3/tests/test_textwrap.py
+++ b/contrib/python/wcwidth/py3/tests/test_textwrap.py
@@ -26,7 +26,6 @@ def mock_hyperlink_ids(monkeypatch):
SGR_RED = '\x1b[31m'
-SGR_BLUE = '\x1b[34m'
SGR_BOLD = '\x1b[1m'
SGR_RESET = '\x1b[0m'
ATTRS = ('\x1b[31m', '\x1b[34m', '\x1b[4m', '\x1b[7m', '\x1b[41m', '\x1b[37m', '\x1b[107m')
@@ -59,7 +58,7 @@ def _adjust_stdlib_result(expected, kwargs):
"""
if not expected:
return expected
- if kwargs.get('drop_whitespace'):
+ if kwargs.get('drop_whitespace', True):
# Strip trailing whitespace from each line (old Python bug)
expected = [line.rstrip() for line in expected]
# Remove leading all-whitespace lines (old Python bug)
@@ -77,15 +76,17 @@ def _colorize(text):
)
-# Edge cases not covered by stdlib comparison
-BASIC_EDGE_CASES = [
+EDGE_CASES = [
('', 10, []),
(' ', 10, []),
('\u5973', 0, ['\u5973']),
+ ('\u5973', 1, ['\u5973']),
+ (ZWJ_FAMILY, 1, [ZWJ_FAMILY]),
+ (HANGUL_GA, 1, [HANGUL_GA]),
]
[email protected]('text,w,expected', BASIC_EDGE_CASES)
[email protected]('text,w,expected', EDGE_CASES)
def test_wrap_edge_cases(text, w, expected):
assert wrap(text, w) == expected
@@ -94,12 +95,6 @@ def test_wrap_initial_indent():
assert wrap('hello world', 10, initial_indent='> ') == ['> hello', 'world']
-def test_wrap_drops_trailing_whitespace():
- """Trailing whitespace stripped when drop_whitespace=True (CPython #140627)."""
- result = wrap(' Z! a bc defghij', 3)
- assert result[:3] == [' Z!', 'a', 'bc']
-
-
LONG_WORD_CASES = [
('abcdefghij', 3, True, ['abc', 'def', 'ghi', 'j']),
('abcdefghij', 3, False, ['abcdefghij']),
@@ -111,30 +106,19 @@ def test_wrap_long_words(text, w, break_long, expected):
assert wrap(text, w, break_long_words=break_long) == expected
-# Hyphen edge cases for long word breaking
HYPHEN_LONG_WORD_CASES = [
- ('a-b-c-d', 3, True, ['a-', 'b-', 'c-d']),
- ('a-b-c-d', 3, False, ['a-b', '-c-', 'd']),
- ('---', 2, True, ['--', '-']),
- ('a---b', 2, True, ['a-', '--', 'b']),
- # With propagate_sgr=True, SGR continues to next line
- ('a-\x1b[31mb', 2, True, ['a-\x1b[31m\x1b[0m', '\x1b[31mb\x1b[0m']),
+ ('a-b-c-d', 3, True, True, ['a-', 'b-', 'c-d']),
+ ('a-b-c-d', 3, False, True, ['a-b', '-c-', 'd']),
+ ('---', 2, True, True, ['--', '-']),
+ ('a---b', 2, True, True, ['a-', '--', 'b']),
+ ('a-\x1b[31mb', 2, True, True, ['a-\x1b[31m\x1b[0m', '\x1b[31mb\x1b[0m']),
+ ('a-\x1b[31mb', 2, True, False, ['a-\x1b[31m', 'b']),
]
-HYPHEN_LONG_WORD_CASES_NO_PROPAGATE = [
- # With propagate_sgr=False, SGR stays where it is
- ('a-\x1b[31mb', 2, True, ['a-\x1b[31m', 'b']),
-]
-
[email protected]('text,w,break_hyphens,expected', HYPHEN_LONG_WORD_CASES)
-def test_wrap_hyphen_long_words(text, w, break_hyphens, expected):
- assert wrap(text, w, break_on_hyphens=break_hyphens) == expected
-
-
[email protected]('text,w,break_hyphens,expected', HYPHEN_LONG_WORD_CASES_NO_PROPAGATE)
-def test_wrap_hyphen_long_words_no_propagate(text, w, break_hyphens, expected):
- assert wrap(text, w, break_on_hyphens=break_hyphens, propagate_sgr=False) == expected
[email protected]('text,w,break_hyphens,propagate,expected', HYPHEN_LONG_WORD_CASES)
+def test_wrap_hyphen_long_words(text, w, break_hyphens, propagate, expected):
+ assert wrap(text, w, break_on_hyphens=break_hyphens, propagate_sgr=propagate) == expected
# Comprehensive stdlib compatibility
@@ -147,6 +131,17 @@ TEXTWRAP_KWARGS = [
{'break_long_words': True, 'drop_whitespace': True, 'subsequent_indent': ' '},
{'break_long_words': True, 'drop_whitespace': True, 'break_on_hyphens': True},
{'break_long_words': True, 'drop_whitespace': True, 'break_on_hyphens': False},
+ {'break_long_words': True, 'drop_whitespace': False,
+ 'subsequent_indent': '', 'max_lines': 4, 'placeholder': '~'},
+ {'break_long_words': True, 'drop_whitespace': True,
+ 'max_lines': 3, 'placeholder': '...'},
+ {'break_long_words': True, 'drop_whitespace': True,
+ 'max_lines': 1, 'placeholder': '...'},
+ {'expand_tabs': False, 'break_long_words': True, 'drop_whitespace': True},
+ {'replace_whitespace': False, 'break_long_words': True,
+ 'drop_whitespace': True},
+ {'fix_sentence_endings': True, 'break_long_words': True,
+ 'drop_whitespace': True},
]
@@ -187,20 +182,6 @@ def test_wrap_multiline_matches_stdlib():
assert wrap(given, 30) == textwrap.wrap(given, 30)
-# Wide characters that exceed width=1 (tests force-grapheme logic)
-WIDE_CHAR_WIDTH_1_CASES = [
- ('\u5973', 1, ['\u5973']),
- (ZWJ_FAMILY, 1, [ZWJ_FAMILY]),
- (HANGUL_GA, 1, [HANGUL_GA]),
-]
-
-
[email protected]('text,w,expected', WIDE_CHAR_WIDTH_1_CASES)
-def test_wrap_wide_char_width_1(text, w, expected):
- assert wrap(text, w) == expected
-
-
-# Unicode width-aware wrapping
UNICODE_CASES = [
# CJK (2 cells each)
('\u4e2d\u6587\u5b57\u7b26', 4, ['\u4e2d\u6587', '\u5b57\u7b26']),
@@ -244,24 +225,15 @@ SEQUENCE_CASES = [
('abc\x1bdefghij', 3, ['abc\x1b', 'def', 'ghi', 'j']),
]
-# Old behavior tests (propagate_sgr=False)
SEQUENCE_CASES_NO_PROPAGATE = [
- (f'{SGR_RED}red{SGR_RESET} blue', 4, [f'{SGR_RED}red{SGR_RESET}', 'blue']),
(f'hello{SGR_RED} world', 6, [f'hello{SGR_RED}', 'world']),
- (f'{SGR_RED}{SGR_RESET}', 10, [f'{SGR_RED}{SGR_RESET}']),
- (f'hello {SGR_RED}{SGR_RESET}world', 6, ['hello', f'{SGR_RED}{SGR_RESET}world']),
- # Sequences preserved where they are, not propagated
('x\x1b[31mabcdefghij\x1b[0m', 3, ['x\x1b[31mab', 'cde', 'fgh', 'ij\x1b[0m']),
]
@pytest.mark.parametrize('text,w,expected', SEQUENCE_CASES)
def test_wrap_sequences(benchmark, text, w, expected):
- result = benchmark(wrap, text, w)
- if any('\x1b' in e or '\x00' <= e[0] < '\x20' for e in expected if e):
- assert result == expected
- else:
- assert result == expected
+ assert benchmark(wrap, text, w) == expected
@pytest.mark.parametrize('text,w,expected', SEQUENCE_CASES_NO_PROPAGATE)
@@ -445,3 +417,107 @@ def test_wrap_hyperlink_word_boundary(text, w, expected):
"""OSC hyperlink sequences should act as word boundaries."""
result = wrap(text, w)
assert result == expected
+
+
+PLACEHOLDER_STDLIB_CASES = [
+ ('The quick brown fox jumps over the lazy dog',
+ {'width': 10, 'max_lines': 3, 'placeholder': '...'}),
+ ('1234567890 1234567890 extra',
+ {'width': 10, 'max_lines': 2, 'placeholder': '...'}),
+ ('1234567890 1234567890',
+ {'width': 10, 'max_lines': 1, 'placeholder': '...'}),
+ ('short 1234567890 extra',
+ {'width': 10, 'max_lines': 2, 'placeholder': '...'}),
+ ('hello world',
+ {'width': 10, 'max_lines': 5, 'placeholder': '...'}),
+ ('hello world foo bar baz',
+ {'width': 8, 'max_lines': 2, 'placeholder': ' [...]'}),
+ ('a bb ccc',
+ {'width': 3, 'max_lines': 2, 'placeholder': '.'}),
+ ('a bb ccc dddd',
+ {'width': 4, 'max_lines': 3, 'placeholder': '~'}),
+ (' a ', {'width': 1, 'max_lines': 2, 'drop_whitespace': False, 'placeholder': '~'}),
+ ('hello world', {'width': 20, 'max_lines': 5}),
+ ('ab cd', {'width': 5, 'max_lines': 1}),
+ ('aaa bbb ccc', {'width': 3, 'max_lines': 2, 'placeholder': '...'}),
+ ('hello world foo bar',
+ {'width': 10, 'subsequent_indent': ' ', 'max_lines': 2, 'placeholder': '...'}),
+ ('hello world foo bar',
+ {'width': 10, 'initial_indent': '> ', 'max_lines': 2, 'placeholder': '...'}),
+]
+
+
[email protected]('text,kwargs', PLACEHOLDER_STDLIB_CASES)
+def test_wrap_max_lines_matches_stdlib(text, kwargs):
+ expected = _adjust_stdlib_result(textwrap.wrap(text, **kwargs), kwargs)
+ assert wrap(text, **kwargs) == expected
+
+
+def test_wrap_placeholder_too_large():
+ with pytest.raises(ValueError, match="placeholder too large"):
+ wrap('hello', width=3, max_lines=1, placeholder='.....')
+ with pytest.raises(ValueError):
+ textwrap.wrap('fox', width=1, max_lines=3, placeholder='...')
+
+
+MAX_LINES_SEQUENCE_CASES = [
+ (f'{SGR_RED}hello world foo bar{SGR_RESET}',
+ 8, 2, '...', [f'{SGR_RED}hello{SGR_RESET}', f'{SGR_RED}world...{SGR_RESET}']),
+ (f'{SGR_RED}hello{SGR_RESET} world foo',
+ 8, 2, '...', [f'{SGR_RED}hello{SGR_RESET}', 'world...']),
+ (f'{SGR_RED}hello{SGR_RESET} world',
+ 6, 1, '.', [f'{SGR_RED}hello{SGR_RESET}.']),
+ ('\u4e2d\u6587 \u5b57\u7b26 hello', 5, 1, '~', ['\u4e2d\u6587~']),
+ ('\u4e2d\u6587 \u5b57\u7b26 hello world', 5, 2, '~', ['\u4e2d\u6587', '\u5b57\u7b26~']),
+ ('\u4e2d\u6587\u5b57\u7b26 hello', 12, 1, '...', ['\u4e2d\u6587\u5b57\u7b26...']),
+]
+
+
[email protected]('text,w,ml,ph,expected', MAX_LINES_SEQUENCE_CASES)
+def test_wrap_max_lines_sequences(text, w, ml, ph, expected):
+ assert wrap(text, w, max_lines=ml, placeholder=ph) == expected
+
+
+def test_wrap_max_lines_hyperlink_closed():
+ """Truncation inside a hyperlink closes it before the placeholder."""
+ text = f'{OSC_START_ST}Click here please{OSC_END_ST}'
+ result = wrap(text, 10, max_lines=1, placeholder='...')
+ assert len(result) == 1
+ assert result[0].endswith(f'{OSC_END_ST}...')
+ assert OSC_START_ST in result[0]
+
+
+def test_wrap_max_lines_hyperlink_close_on_prev_line():
+ """Fallback to previous line preserves hyperlink close sequence."""
+ text = f'{OSC_START_ST}ab{OSC_END_ST} cccccccccc ddddd'
+ result = wrap(text, 10, max_lines=2, placeholder='...')
+ assert result == [f'{OSC_START_ST}ab{OSC_END_ST}...']
+
+
+# -- expand_tabs, replace_whitespace, fix_sentence_endings --
+
+STDLIB_PARAM_CASES = [
+ ('hello\tworld', {'width': 20, 'expand_tabs': False, 'replace_whitespace': False}),
+ ('hello\tworld foo\tbar baz', {'width': 12, 'expand_tabs': False, 'tabsize': 8}),
+ ('hello\nworld', {'width': 20, 'replace_whitespace': False}),
+ ('a\t b\n c', {'width': 20, 'replace_whitespace': False}),
+ ('Hello world. This is a test. More text.', {'width': 20, 'fix_sentence_endings': True}),
+ ('Dr. Smith went to Washington. He left.', {'width': 20, 'fix_sentence_endings': True}),
+]
+
+
[email protected]('text,kwargs', STDLIB_PARAM_CASES)
+def test_wrap_stdlib_params(text, kwargs):
+ assert wrap(text, **kwargs) == textwrap.wrap(text, **kwargs)
+
+
+def test_wrap_expand_tabs_false_with_sequences():
+ text = f'{SGR_RED}a\tb{SGR_RESET}'
+ result = wrap(text, 20, expand_tabs=False, replace_whitespace=False)
+ assert '\t' in _strip(result[0])
+
+
+def test_wrap_replace_whitespace_false_newlines_zero_width():
+ """Newlines have zero display width, so more text fits per line than stdlib."""
+ assert wrap('hello\nworld foo\nbar', 10, replace_whitespace=False) == [
+ 'hello\nworld', 'foo\nbar']
diff --git a/contrib/python/wcwidth/py3/wcwidth/__init__.py b/contrib/python/wcwidth/py3/wcwidth/__init__.py
index ffb13207b61..400c8a61935 100644
--- a/contrib/python/wcwidth/py3/wcwidth/__init__.py
+++ b/contrib/python/wcwidth/py3/wcwidth/__init__.py
@@ -40,4 +40,4 @@ __all__ = ('wcwidth', 'wcswidth', 'width', 'iter_sequences', 'iter_graphemes',
# Using 'hatchling', it does not seem to provide the pyproject.toml nicety, "dynamic = ['version']"
# like flit_core, maybe there is some better way but for now we have to duplicate it in both places
-__version__ = '0.5.3'
+__version__ = '0.6.0'
diff --git a/contrib/python/wcwidth/py3/wcwidth/textwrap.py b/contrib/python/wcwidth/py3/wcwidth/textwrap.py
index e6cca9161e8..4582cd5e089 100644
--- a/contrib/python/wcwidth/py3/wcwidth/textwrap.py
+++ b/contrib/python/wcwidth/py3/wcwidth/textwrap.py
@@ -228,6 +228,16 @@ class SequenceTextWrapper(textwrap.TextWrapper):
if not chunks:
return []
+ if self.max_lines is not None:
+ if self.max_lines > 1:
+ indent = self.subsequent_indent
+ else:
+ indent = self.initial_indent
+ if (self._width(indent)
+ + self._width(self.placeholder.lstrip())
+ > self.width):
+ raise ValueError("placeholder too large for max width")
+
lines: list[str] = []
is_first_line = True
@@ -296,48 +306,90 @@ class SequenceTextWrapper(textwrap.TextWrapper):
current_line[-1] = current_line[-1] + sequences
if current_line:
- line_content = ''.join(current_line)
+ # Check whether this is a normal append or max_lines
+ # truncation. Matches stdlib textwrap precedence:
+ # normal if max_lines not set, not yet reached, or no
+ # remaining visible content that would need truncation.
+ no_more_content = (
+ not chunks or
+ self.drop_whitespace and
+ len(chunks) == 1 and
+ not self._strip_sequences(chunks[0]).strip()
+ )
+ if (self.max_lines is None or
+ len(lines) + 1 < self.max_lines or
+ no_more_content
+ and current_width <= line_width):
+ line_content = ''.join(current_line)
- # Track hyperlink state through this line's content
- new_state = self._track_hyperlink_state(line_content, hyperlink_state)
+ # Track hyperlink state through this line's content
+ new_state = self._track_hyperlink_state(line_content, hyperlink_state)
- # If we end inside a hyperlink, append close sequence
- if new_state is not None:
- # Ensure we have an id for continuation
- if current_hyperlink_id is None:
- if 'id=' in new_state.params:
- current_hyperlink_id = new_state.params
- elif new_state.params:
- # Prepend id to existing params (per OSC 8 spec, params can have
- # multiple key=value pairs separated by :)
- current_hyperlink_id = (
- f'id={self._next_hyperlink_id()}:{new_state.params}')
- else:
- current_hyperlink_id = f'id={self._next_hyperlink_id()}'
- line_content = line_content + _make_hyperlink_close(new_state.terminator)
+ # If we end inside a hyperlink, append close sequence
+ if new_state is not None:
+ # Ensure we have an id for continuation
+ if current_hyperlink_id is None:
+ if 'id=' in new_state.params:
+ current_hyperlink_id = new_state.params
+ elif new_state.params:
+ # Prepend id to existing params (per OSC 8 spec, params can have
+ # multiple key=value pairs separated by :)
+ current_hyperlink_id = (
+ f'id={self._next_hyperlink_id()}:{new_state.params}')
+ else:
+ current_hyperlink_id = f'id={self._next_hyperlink_id()}'
+ line_content += _make_hyperlink_close(new_state.terminator)
- # Also need to inject the id into the opening sequence if it didn't have one
- if 'id=' not in new_state.params:
- # Find and replace the original open sequence with one that has id
- old_open = _make_hyperlink_open(
- new_state.url, new_state.params, new_state.terminator)
- new_open = _make_hyperlink_open(
+ # Also need to inject the id into the opening
+ # sequence if it didn't have one
+ if 'id=' not in new_state.params:
+ # Find and replace the original open sequence with one that has id
+ old_open = _make_hyperlink_open(
+ new_state.url, new_state.params, new_state.terminator)
+ new_open = _make_hyperlink_open(
+ new_state.url, current_hyperlink_id, new_state.terminator)
+ line_content = line_content.replace(old_open, new_open, 1)
+
+ # Update state for next line, using computed id
+ hyperlink_state = _HyperlinkState(
new_state.url, current_hyperlink_id, new_state.terminator)
- line_content = line_content.replace(old_open, new_open, 1)
+ else:
+ hyperlink_state = None
+ current_hyperlink_id = None # Reset id when hyperlink closes
- # Update state for next line, using computed id
- hyperlink_state = _HyperlinkState(
- new_state.url, current_hyperlink_id, new_state.terminator)
+ # Strip trailing whitespace when drop_whitespace is enabled
+ # (matches CPython #140627 fix behavior)
+ if self.drop_whitespace:
+ line_content = line_content.rstrip()
+ lines.append(indent + line_content)
+ is_first_line = False
else:
- hyperlink_state = None
- current_hyperlink_id = None # Reset id when hyperlink closes
-
- # Strip trailing whitespace when drop_whitespace is enabled
- # (matches CPython #140627 fix behavior)
- if self.drop_whitespace:
- line_content = line_content.rstrip()
- lines.append(indent + line_content)
- is_first_line = False
+ # max_lines reached with remaining content —
+ # pop chunks until placeholder fits, then break.
+ placeholder_w = self._width(self.placeholder)
+ while current_line:
+ last_text = self._strip_sequences(current_line[-1])
+ if (last_text.strip()
+ and current_width + placeholder_w <= line_width):
+ line_content = ''.join(current_line)
+ new_state = self._track_hyperlink_state(
+ line_content, hyperlink_state)
+ if new_state is not None:
+ line_content += _make_hyperlink_close(
+ new_state.terminator)
+ lines.append(indent + line_content + self.placeholder)
+ break
+ current_width -= self._width(current_line[-1])
+ del current_line[-1]
+ else:
+ if lines:
+ prev_line = self._rstrip_visible(lines[-1])
+ if (self._width(prev_line) + placeholder_w
+ <= self.width):
+ lines[-1] = prev_line + self.placeholder
+ break
+ lines.append(indent + self.placeholder.lstrip())
+ break
return lines
@@ -461,15 +513,40 @@ class SequenceTextWrapper(textwrap.TextWrapper):
"""Find the end position of the first grapheme."""
return len(next(iter_graphemes(text)))
+ def _rstrip_visible(self, text: str) -> str:
+ """Strip trailing visible whitespace, preserving trailing sequences."""
+ segments = list(iter_sequences(text))
+ last_vis = -1
+ for i, (segment, is_seq) in enumerate(segments):
+ if not is_seq and segment.rstrip():
+ last_vis = i
+ if last_vis == -1:
+ return ''
+ result = []
+ for i, (segment, is_seq) in enumerate(segments):
+ if i < last_vis:
+ result.append(segment)
+ elif i == last_vis:
+ result.append(segment.rstrip())
+ elif is_seq:
+ result.append(segment)
+ return ''.join(result)
+
def wrap(text: str, width: int = 70, *,
control_codes: Literal['parse', 'strict', 'ignore'] = 'parse',
tabsize: int = 8,
+ expand_tabs: bool = True,
+ replace_whitespace: bool = True,
ambiguous_width: int = 1,
initial_indent: str = '',
subsequent_indent: str = '',
+ fix_sentence_endings: bool = False,
break_long_words: bool = True,
break_on_hyphens: bool = True,
+ drop_whitespace: bool = True,
+ max_lines: int | None = None,
+ placeholder: str = ' [...]',
propagate_sgr: bool = True) -> list[str]:
r"""
Wrap text to fit within given width, returning a list of wrapped lines.
@@ -482,12 +559,28 @@ def wrap(text: str, width: int = 70, *,
:param width: Maximum line width in display cells.
:param control_codes: How to handle terminal sequences (see :func:`~.width`).
:param tabsize: Tab stop width for tab expansion.
+ :param expand_tabs: If True (default), tab characters are expanded
+ to spaces using ``tabsize``.
+ :param replace_whitespace: If True (default), each whitespace character
+ is replaced with a single space after tab expansion. When False,
+ control whitespace like ``\n`` has zero display width (unlike
+ :func:`textwrap.wrap` which counts ``len()``), so wrap points
+ may differ from stdlib for non-space whitespace characters.
:param ambiguous_width: Width to use for East Asian Ambiguous (A)
characters. Default is ``1`` (narrow). Set to ``2`` for CJK contexts.
:param initial_indent: String prepended to first line.
:param subsequent_indent: String prepended to subsequent lines.
+ :param fix_sentence_endings: If True, ensure sentences are always
+ separated by exactly two spaces.
:param break_long_words: If True, break words longer than width.
:param break_on_hyphens: If True, allow breaking at hyphens.
+ :param drop_whitespace: If True (default), whitespace at the beginning
+ and end of each line (after wrapping but before indenting) is dropped.
+ Set to False to preserve whitespace.
+ :param max_lines: If set, output contains at most this many lines, with
+ ``placeholder`` appended to the last line if the text was truncated.
+ :param placeholder: String appended to the last line when text is
+ truncated by ``max_lines``. Default is ``' [...]'``.
:param propagate_sgr: If True (default), SGR (terminal styling) sequences
are propagated across wrapped lines. Each line ends with a reset
sequence and the next line begins with the active style restored.
@@ -526,6 +619,10 @@ def wrap(text: str, width: int = 70, *,
.. versionchanged:: 0.5.0
Added ``propagate_sgr`` parameter (default True).
+ .. versionchanged:: 0.6.0
+ Added ``expand_tabs``, ``replace_whitespace``, ``fix_sentence_endings``,
+ ``drop_whitespace``, ``max_lines``, and ``placeholder`` parameters.
+
Example::
>>> from wcwidth import wrap
@@ -534,15 +631,22 @@ def wrap(text: str, width: int = 70, *,
>>> wrap('中文字符', 4) # CJK characters (2 cells each)
['中文', '字符']
"""
+ # pylint: disable=too-many-arguments,too-many-locals
wrapper = SequenceTextWrapper(
width=width,
control_codes=control_codes,
tabsize=tabsize,
+ expand_tabs=expand_tabs,
+ replace_whitespace=replace_whitespace,
ambiguous_width=ambiguous_width,
initial_indent=initial_indent,
subsequent_indent=subsequent_indent,
+ fix_sentence_endings=fix_sentence_endings,
break_long_words=break_long_words,
break_on_hyphens=break_on_hyphens,
+ drop_whitespace=drop_whitespace,
+ max_lines=max_lines,
+ placeholder=placeholder,
)
lines = wrapper.wrap(text)
diff --git a/contrib/python/wcwidth/py3/ya.make b/contrib/python/wcwidth/py3/ya.make
index 8ae72ce50be..c50d8fe4296 100644
--- a/contrib/python/wcwidth/py3/ya.make
+++ b/contrib/python/wcwidth/py3/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(0.5.3)
+VERSION(0.6.0)
LICENSE(MIT)