diff --git a/HISTORY.md b/HISTORY.md index 2b8d93bc4e..859bbcba63 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -11,6 +11,9 @@ dev backwards compatible as it inherits from previously thrown exceptions. Can be caught from `requests.exceptions.RequestException` as well. +- Catch `AttributeError` when calculating length of files obtained by + `Tarfile.extractfile()` + 2.26.0 (2021-07-13) ------------------- @@ -1702,7 +1705,7 @@ This is not a backwards compatible change. - Automatic Authentication API Change - Smarter Query URL Parameterization - Allow file uploads and POST data together -- +- New Authentication Manager System @@ -1721,7 +1724,7 @@ This is not a backwards compatible change. 0.2.3 (2011-02-15) ------------------ -- +- New HTTPHandling Methods diff --git a/requests/utils.py b/requests/utils.py index 30998417e0..8b2a5f145e 100644 --- a/requests/utils.py +++ b/requests/utils.py @@ -124,7 +124,10 @@ def super_len(o): elif hasattr(o, 'fileno'): try: fileno = o.fileno() - except io.UnsupportedOperation: + except (io.UnsupportedOperation, AttributeError): + # AttributeError is a surprising exception, seeing as how we've just checked + # that `hasattr(o, 'fileno')`. It happens for objects obtained via + # `Tarfile.extractfile()`, per issue 5229. pass else: total_length = os.fstat(fileno).st_size diff --git a/tests/test_utils.py b/tests/test_utils.py index 98ffb25a6c..559dee657f 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -4,6 +4,7 @@ import copy import filecmp from io import BytesIO +import tarfile import zipfile from collections import deque @@ -86,6 +87,18 @@ def test_file(self, tmpdir, mode, warnings_num, recwarn): assert super_len(fd) == 4 assert len(recwarn) == warnings_num + def test_tarfile_member(self, tmpdir): + file_obj = tmpdir.join('test.txt') + file_obj.write('Test') + + tar_obj = str(tmpdir.join('test.tar')) + with tarfile.open(tar_obj, 'w') as tar: + tar.add(str(file_obj), arcname='test.txt') + + with tarfile.open(tar_obj) as tar: + member = tar.extractfile('test.txt') + assert super_len(member) == 4 + def test_super_len_with__len__(self): foo = [1,2,3,4] len_foo = super_len(foo)