Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Lib/tarfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -2723,6 +2723,9 @@ def makelink_with_filter(self, tarinfo, targetpath,
return
else:
if os.path.exists(tarinfo._link_target):
if os.path.lexists(targetpath):
# Avoid FileExistsError on following os.link.
os.unlink(targetpath)
os.link(tarinfo._link_target, targetpath)
return
except symlink_exception:
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_codecs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3293,7 +3293,7 @@ def test_code_page_name(self):
codecs.code_page_encode, 932, '\xff')
self.assertRaisesRegex(UnicodeDecodeError, 'cp932',
codecs.code_page_decode, 932, b'\x81\x00', 'strict', True)
self.assertRaisesRegex(UnicodeDecodeError, 'CP_UTF8',
self.assertRaisesRegex(UnicodeDecodeError, 'cp65001',
codecs.code_page_decode, self.CP_UTF8, b'\xff', 'strict', True)

def check_decode(self, cp, tests):
Expand Down
51 changes: 51 additions & 0 deletions Lib/test/test_tarfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,57 @@ def test_next_on_empty_tarfile(self):
with tarfile.open(fileobj=fd, mode="r") as tf:
self.assertEqual(tf.next(), None)

def _setup_symlink_to_target(self, temp_dirpath):
target_filepath = os.path.join(temp_dirpath, "target")
ustar_dirpath = os.path.join(temp_dirpath, "ustar")
hardlink_filepath = os.path.join(ustar_dirpath, "lnktype")
with open(target_filepath, "wb") as f:
f.write(b"target")
os.makedirs(ustar_dirpath)
os.symlink(target_filepath, hardlink_filepath)
return target_filepath, hardlink_filepath

def _assert_on_file_content(self, filepath, digest):
with open(filepath, "rb") as f:
data = f.read()
self.assertEqual(sha256sum(data), digest)

@unittest.skipUnless(
hasattr(os, "link"), "Missing hardlink implementation"
)
@os_helper.skip_unless_symlink
def test_extract_hardlink_on_symlink(self):
"""
This test verifies that extracting a hardlink will not follow an
existing symlink after a FileExistsError on os.link.
"""
with os_helper.temp_dir() as DIR:
target_filepath, hardlink_filepath = self._setup_symlink_to_target(DIR)
with tarfile.open(tarname, encoding="iso8859-1") as tar:
tar.extract("ustar/regtype", DIR, filter="data")
tar.extract("ustar/lnktype", DIR, filter="data")
self._assert_on_file_content(target_filepath, sha256sum(b"target"))
self._assert_on_file_content(hardlink_filepath, sha256_regtype)

@unittest.skipUnless(
hasattr(os, "link"), "Missing hardlink implementation"
)
@os_helper.skip_unless_symlink
def test_extractall_hardlink_on_symlink(self):
"""
This test verifies that extracting a hardlink will not follow an
existing symlink after a FileExistsError on os.link.
"""
with os_helper.temp_dir() as DIR:
target_filepath, hardlink_filepath = self._setup_symlink_to_target(DIR)
with tarfile.open(tarname, encoding="iso8859-1") as tar:
tar.extractall(
DIR, members=["ustar/regtype", "ustar/lnktype"], filter="data",
)
self._assert_on_file_content(target_filepath, sha256sum(b"target"))
self._assert_on_file_content(hardlink_filepath, sha256_regtype)


class MiscReadTest(MiscReadTestBase, unittest.TestCase):
test_fail_comp = None

Expand Down
2 changes: 1 addition & 1 deletion Mac/BuildScript/build-installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1747,7 +1747,7 @@ def main():
fn = os.path.join(folder, "ReadMe.rtf")
patchFile("resources/ReadMe.rtf", fn)
fn = os.path.join(folder, "Update Shell Profile.command")
patchScript("scripts/postflight.patch-profile", fn)
patchScript("resources/update_shell_profile.command", fn)
fn = os.path.join(folder, "Install Certificates.command")
patchScript("resources/install_certificates.command", fn)
os.chmod(folder, STAT_0o755)
Expand Down
116 changes: 116 additions & 0 deletions Mac/BuildScript/resources/update_shell_profile.command
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#!/bin/sh

echo "This script will update your shell profile when the 'bin' directory"
echo "of python is not early enough of the PATH of your shell."
echo "These changes will be effective only in shell windows that you open"
echo "after running this script."

PYVER=@PYVER@
PYTHON_ROOT="/Library/Frameworks/Python.framework/Versions/@PYVER@"

if [ `id -ur` = 0 ]; then
# Run from the installer, do some trickery to fetch the information
# we need.
theShell="`finger $USER | grep Shell: | head -1 | awk '{ print $NF }'`"

else
theShell="${SHELL}"
fi

# Make sure the directory ${PYTHON_ROOT}/bin is on the users PATH.
BSH="`basename "${theShell}"`"
case "${BSH}" in
bash|ksh|sh|*csh|zsh|fish)
if [ `id -ur` = 0 ]; then
P=`su - ${USER} -c 'echo A-X-4-X@@$PATH@@X-4-X-A' | grep 'A-X-4-X@@.*@@X-4-X-A' | sed -e 's/^A-X-4-X@@//g' -e 's/@@X-4-X-A$//g'`
else
P="`(exec -l ${theShell} -c 'echo $PATH')`"
fi
;;
*)
echo "Sorry, I don't know how to patch $BSH shells"
exit 0
;;
esac

# Now ensure that our bin directory is on $P and before /usr/bin at that
for elem in `echo $P | tr ':' ' '`
do
if [ "${elem}" = "${PYTHON_ROOT}/bin" ]; then
echo "All right, you're a python lover already"
exit 0
elif [ "${elem}" = "/usr/bin" ]; then
break
fi
done

echo "${PYTHON_ROOT}/bin is not on your PATH or at least not early enough"
case "${BSH}" in
*csh)
if [ -f "${HOME}/.tcshrc" ]; then
RC="${HOME}/.tcshrc"
else
RC="${HOME}/.cshrc"
fi
# Create backup copy before patching
if [ -f "${RC}" ]; then
cp -fp "${RC}" "${RC}.pysave"
fi
echo "" >> "${RC}"
echo "# Setting PATH for Python ${PYVER}" >> "${RC}"
echo "# The original version is saved in .cshrc.pysave" >> "${RC}"
echo "set path=(${PYTHON_ROOT}/bin "'$path'")" >> "${RC}"
if [ `id -ur` = 0 ]; then
chown -h "${USER}" "${RC}"
fi
exit 0
;;
bash)
if [ -e "${HOME}/.bash_profile" ]; then
PR="${HOME}/.bash_profile"
elif [ -e "${HOME}/.bash_login" ]; then
PR="${HOME}/.bash_login"
elif [ -e "${HOME}/.profile" ]; then
PR="${HOME}/.profile"
else
PR="${HOME}/.bash_profile"
fi
;;
fish)
CONFIG_DIR="${HOME}/.config/fish/conf.d/"
RC="${CONFIG_DIR}/python-${PYVER}.fish"
mkdir -p "$CONFIG_DIR"
if [ -f "${RC}" ]; then
cp -fp "${RC}" "${RC}.pysave"
fi
echo "# Setting PATH for Python ${PYVER}" > "${RC}"
if [ -f "${RC}.pysave" ]; then
echo "# The original version is saved in ${RC}.pysave" >> "${RC}"
fi
echo "fish_add_path -g \"${PYTHON_ROOT}/bin\"" >> "${RC}"
if [ `id -ur` = 0 ]; then
chown -h "${USER}" "${RC}"
fi
exit 0
;;
zsh)
PR="${HOME}/.zprofile"
;;
*sh)
PR="${HOME}/.profile"
;;
esac

# Create backup copy before patching
if [ -f "${PR}" ]; then
cp -fp "${PR}" "${PR}.pysave"
fi
echo "" >> "${PR}"
echo "# Setting PATH for Python ${PYVER}" >> "${PR}"
echo "# The original version is saved in `basename ${PR}`.pysave" >> "${PR}"
echo 'PATH="'"${PYTHON_ROOT}/bin"':${PATH}"' >> "${PR}"
echo 'export PATH' >> "${PR}"
if [ `id -ur` = 0 ]; then
chown -h "${USER}" "${PR}"
fi
exit 0
156 changes: 72 additions & 84 deletions Mac/BuildScript/scripts/postflight.patch-profile
Original file line number Diff line number Diff line change
@@ -1,116 +1,104 @@
#!/bin/sh

echo "This script will update your shell profile when the 'bin' directory"
echo "of python is not early enough of the PATH of your shell."
echo "These changes will be effective only in shell windows that you open"
echo "after running this script."

PYVER=@PYVER@
PYTHON_ROOT="/Library/Frameworks/Python.framework/Versions/@PYVER@"

if [ `id -ur` = 0 ]; then
# Run from the installer, do some trickery to fetch the information
# we need.
theShell="`finger $USER | grep Shell: | head -1 | awk '{ print $NF }'`"

else
theShell="${SHELL}"
fi
# Run from the installer, do some trickery to fetch the information
# we need.
theShell="`finger $USER | grep Shell: | head -1 | awk '{ print $NF }'`"

# Make sure the directory ${PYTHON_ROOT}/bin is on the users PATH.
BSH="`basename "${theShell}"`"
case "${BSH}" in
bash|ksh|sh|*csh|zsh|fish)
if [ `id -ur` = 0 ]; then
P=`su - ${USER} -c 'echo A-X-4-X@@$PATH@@X-4-X-A' | grep 'A-X-4-X@@.*@@X-4-X-A' | sed -e 's/^A-X-4-X@@//g' -e 's/@@X-4-X-A$//g'`
else
P="`(exec -l ${theShell} -c 'echo $PATH')`"
fi
;;
true
;;
*)
echo "Sorry, I don't know how to patch $BSH shells"
exit 0
;;
exit 0
;;
esac

# Now ensure that our bin directory is on $P and before /usr/bin at that
for elem in `echo $P | tr ':' ' '`
do
if [ "${elem}" = "${PYTHON_ROOT}/bin" ]; then
echo "All right, you're a python lover already"
exit 0
elif [ "${elem}" = "/usr/bin" ]; then
break
fi
done

echo "${PYTHON_ROOT}/bin is not on your PATH or at least not early enough"
case "${BSH}" in
*csh)
if [ -f "${HOME}/.tcshrc" ]; then
RC="${HOME}/.tcshrc"
else
RC="${HOME}/.cshrc"
fi
# Create backup copy before patching
if [ -f "${RC}" ]; then
cp -fp "${RC}" "${RC}.pysave"
fi
echo "" >> "${RC}"
echo "# Setting PATH for Python ${PYVER}" >> "${RC}"
echo "# The original version is saved in .cshrc.pysave" >> "${RC}"
echo "set path=(${PYTHON_ROOT}/bin "'$path'")" >> "${RC}"
if [ `id -ur` = 0 ]; then
chown "${USER}" "${RC}"
fi
exit 0
;;
if [ -f "${HOME}/.tcshrc" ]; then
RC="${HOME}/.tcshrc"
else
RC="${HOME}/.cshrc"
fi

# Drop privileges while writing files.
su -m ${USER} <<EOFC
# Create backup copy before patching
if [ -f "${RC}" ]; then
cp -fp "${RC}" "${RC}.pysave"
fi
echo "" >> "${RC}"
echo "# Setting PATH for Python ${PYVER}" >> "${RC}"
echo "# The original version is saved in .cshrc.pysave" >> "${RC}"
echo "set path=(${PYTHON_ROOT}/bin "'\$path'")" >> "${RC}"
EOFC

if [ `id -ur` = 0 ]; then
chown -h "${USER}" "${RC}"
fi
exit 0
;;
bash)
if [ -e "${HOME}/.bash_profile" ]; then
PR="${HOME}/.bash_profile"
elif [ -e "${HOME}/.bash_login" ]; then
PR="${HOME}/.bash_login"
elif [ -e "${HOME}/.profile" ]; then
PR="${HOME}/.profile"
else
PR="${HOME}/.bash_profile"
fi
;;
if [ -e "${HOME}/.bash_profile" ]; then
PR="${HOME}/.bash_profile"
elif [ -e "${HOME}/.bash_login" ]; then
PR="${HOME}/.bash_login"
elif [ -e "${HOME}/.profile" ]; then
PR="${HOME}/.profile"
else
PR="${HOME}/.bash_profile"
fi
;;
fish)
CONFIG_DIR="${HOME}/.config/fish/conf.d/"
RC="${CONFIG_DIR}/python-${PYVER}.fish"
mkdir -p "$CONFIG_DIR"
if [ -f "${RC}" ]; then
cp -fp "${RC}" "${RC}.pysave"
fi
echo "# Setting PATH for Python ${PYVER}" > "${RC}"
if [ -f "${RC}.pysave" ]; then
echo "# The original version is saved in ${RC}.pysave" >> "${RC}"
fi
echo "fish_add_path -g \"${PYTHON_ROOT}/bin\"" >> "${RC}"
if [ `id -ur` = 0 ]; then
chown "${USER}" "${RC}"
fi
exit 0
;;
CONFIG_DIR="${HOME}/.config/fish/conf.d/"
RC="${CONFIG_DIR}/python-${PYVER}.fish"

# Drop privileges while writing files.
su -m ${USER} <<EOFF
mkdir -p "$CONFIG_DIR"
if [ -f "${RC}" ]; then
cp -fp "${RC}" "${RC}.pysave"
fi
echo "# Setting PATH for Python ${PYVER}" > "${RC}"
if [ -f "${RC}.pysave" ]; then
echo "# The original version is saved in ${RC}.pysave" >> "${RC}"
fi
echo "fish_add_path -g \"${PYTHON_ROOT}/bin\"" >> "${RC}"
EOFF

if [ `id -ur` = 0 ]; then
chown -h "${USER}" "${RC}"
fi
exit 0
;;
zsh)
PR="${HOME}/.zprofile"
;;
PR="${HOME}/.zprofile"
;;
*sh)
PR="${HOME}/.profile"
;;
PR="${HOME}/.profile"
;;
esac

# Drop privileges while writing files.
su -m ${USER} <<EOFS
# Create backup copy before patching
if [ -f "${PR}" ]; then
cp -fp "${PR}" "${PR}.pysave"
cp -fp "${PR}" "${PR}.pysave"
fi
echo "" >> "${PR}"
echo "# Setting PATH for Python ${PYVER}" >> "${PR}"
echo "# The original version is saved in `basename ${PR}`.pysave" >> "${PR}"
echo 'PATH="'"${PYTHON_ROOT}/bin"':${PATH}"' >> "${PR}"
echo 'PATH="'"${PYTHON_ROOT}/bin"':\${PATH}"' >> "${PR}"
echo 'export PATH' >> "${PR}"
EOFS

if [ `id -ur` = 0 ]; then
chown "${USER}" "${PR}"
chown -h "${USER}" "${PR}"
fi
exit 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix name of the Python encoding in Unicode errors of the code page codec:
use "cp65000" and "cp65001" instead of "CP_UTF7" and "CP_UTF8" which are not
valid Python code names. Patch by Victor Stinner.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:func:`tarfile.TarFile.extractall` and :func:`tarfile.TarFile.extract` now
overwrite symlinks when extracting hardlinks.
(Contributed by Alexander Enrique Urieles Nieto in :gh:`75989`.)
Loading
Loading