Skip to content

Commit

Permalink
tarfile: Handle deprecation warning for extract and extractall (ansib…
Browse files Browse the repository at this point in the history
…le#81545)

* Python 3.11.4 introduces a new parameter 'filter' in extract and
extractall in tarfile. Handle deprecation warning message emitted
in Python 3.12.
* added probing mechanism in ansible-galaxy code to detect broken
data filter implementation in tarfile.

Fixes: ansible#80832

Signed-off-by: Abhijeet Kasurde <[email protected]>
Co-authored-by: Matt Clay <[email protected]>
  • Loading branch information
Akasurde and mattclay authored Aug 31, 2023
1 parent 5a059b8 commit 1cc5efa
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 4 deletions.
4 changes: 4 additions & 0 deletions changelogs/fragments/tarfile_extract_warn.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
bugfixes:
- tarfile - handle data filter deprecation warning message for extract and extractall (https://github.com/ansible/ansible/issues/80832).
- ansible-galaxy - Enabled the ``data`` tarfile filter during role installation for Python versions that support it. A probing mechanism is used to avoid Python versions with a broken implementation.
34 changes: 33 additions & 1 deletion lib/ansible/galaxy/role.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import errno
import datetime
import functools
import os
import tarfile
import tempfile
Expand All @@ -45,6 +46,32 @@
display = Display()


@functools.cache
def _check_working_data_filter() -> bool:
"""
Check if tarfile.data_filter implementation is working
for the current Python version or not
"""

# Implemented the following code to circumvent broken implementation of data_filter
# in tarfile. See for more information - https://github.com/python/cpython/issues/107845
# deprecated: description='probing broken data filter implementation' python_version='3.11'
ret = False
if hasattr(tarfile, 'data_filter'):
# We explicitly check if tarfile.data_filter is broken or not
ti = tarfile.TarInfo('docs/README.md')
ti.type = tarfile.SYMTYPE
ti.linkname = '../README.md'

try:
tarfile.data_filter(ti, '/foo')
except tarfile.LinkOutsideDestinationError:
pass
else:
ret = True
return ret


class GalaxyRole(object):

SUPPORTED_SCMS = set(['git', 'hg'])
Expand Down Expand Up @@ -391,7 +418,12 @@ def install(self):
continue
n_final_parts.append(n_part)
member.name = os.path.join(*n_final_parts)
role_tar_file.extract(member, to_native(self.path))

if _check_working_data_filter():
# deprecated: description='extract fallback without filter' python_version='3.11'
role_tar_file.extract(member, to_native(self.path), filter='data') # type: ignore[call-arg]
else:
role_tar_file.extract(member, to_native(self.path))

# write out the install info file for later use
self._write_galaxy_install_info()
Expand Down
6 changes: 5 additions & 1 deletion packaging/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -1342,7 +1342,11 @@ def test_sdist() -> None:
except FileNotFoundError:
raise ApplicationError(f"Missing sdist: {sdist_file.relative_to(CHECKOUT_DIR)}") from None

sdist.extractall(temp_dir)
# deprecated: description='extractall fallback without filter' python_version='3.11'
if hasattr(tarfile, 'data_filter'):
sdist.extractall(temp_dir, filter='data') # type: ignore[call-arg]
else:
sdist.extractall(temp_dir)

pyc_glob = "*.pyc*"
pyc_files = sorted(path.relative_to(temp_dir) for path in temp_dir.rglob(pyc_glob))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,12 @@ def publish_collection(module, collection):

# Extract the tarfile to sign the MANIFEST.json
with tarfile.open(collection_path, mode='r') as collection_tar:
collection_tar.extractall(path=os.path.join(collection_dir, '%s-%s-%s' % (namespace, name, version)))
# deprecated: description='extractall fallback without filter' python_version='3.11'
# Replace 'tar_filter' with 'data_filter' and 'filter=tar' with 'filter=data' once Python 3.12 is minimum requirement.
if hasattr(tarfile, 'tar_filter'):
collection_tar.extractall(path=os.path.join(collection_dir, '%s-%s-%s' % (namespace, name, version)), filter='tar')
else:
collection_tar.extractall(path=os.path.join(collection_dir, '%s-%s-%s' % (namespace, name, version)))

manifest_path = os.path.join(collection_dir, '%s-%s-%s' % (namespace, name, version), 'MANIFEST.json')
signature_path = os.path.join(module.params['signature_dir'], '%s-%s-%s-MANIFEST.json.asc' % (namespace, name, version))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,11 @@ def test(self, args: SanityConfig, targets: SanityTargets, python: PythonConfig)
temp_dir = process_scoped_temporary_directory(args)

with tarfile.open(path) as file:
file.extractall(temp_dir)
# deprecated: description='extractall fallback without filter' python_version='3.11'
if hasattr(tarfile, 'data_filter'):
file.extractall(temp_dir, filter='data') # type: ignore[call-arg]
else:
file.extractall(temp_dir)

cmd.extend([
'--original-plugins', temp_dir,
Expand Down

0 comments on commit 1cc5efa

Please sign in to comment.