Skip to content

Commit 9cd8fb8

Browse files
bpo-35474: Fix mimetypes.guess_all_extensions() potentially mutating list (GH-28286) (GH-28290)
* Calling guess_all_extensions() with strict=False potentially mutated types_map_inv. * Mutating the result of guess_all_extensions() mutated types_map_inv. (cherry picked from commit 97ea18e) Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 218fe2c commit 9cd8fb8

File tree

3 files changed

+20
-8
lines changed

3 files changed

+20
-8
lines changed

Lib/mimetypes.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ def guess_all_extensions(self, type, strict=True):
169169
but non-standard types.
170170
"""
171171
type = type.lower()
172-
extensions = self.types_map_inv[True].get(type, [])
172+
extensions = list(self.types_map_inv[True].get(type, []))
173173
if not strict:
174174
for ext in self.types_map_inv[False].get(type, []):
175175
if ext not in extensions:

Lib/test/test_mimetypes.py

+16-7
Original file line numberDiff line numberDiff line change
@@ -107,20 +107,29 @@ def test_filename_with_url_delimiters(self):
107107
eq(self.db.guess_type(r" \"\`;b&b&c |.tar.gz"), gzip_expected)
108108

109109
def test_guess_all_types(self):
110-
eq = self.assertEqual
111-
unless = self.assertTrue
112110
# First try strict. Use a set here for testing the results because if
113111
# test_urllib2 is run before test_mimetypes, global state is modified
114112
# such that the 'all' set will have more items in it.
115-
all = set(self.db.guess_all_extensions('text/plain', strict=True))
116-
unless(all >= set(['.bat', '.c', '.h', '.ksh', '.pl', '.txt']))
113+
all = self.db.guess_all_extensions('text/plain', strict=True)
114+
self.assertTrue(set(all) >= {'.bat', '.c', '.h', '.ksh', '.pl', '.txt'})
115+
self.assertEqual(len(set(all)), len(all)) # no duplicates
117116
# And now non-strict
118117
all = self.db.guess_all_extensions('image/jpg', strict=False)
119-
all.sort()
120-
eq(all, ['.jpg'])
118+
self.assertEqual(all, ['.jpg'])
121119
# And now for no hits
122120
all = self.db.guess_all_extensions('image/jpg', strict=True)
123-
eq(all, [])
121+
self.assertEqual(all, [])
122+
# And now for type existing in both strict and non-strict mappings.
123+
self.db.add_type('test-type', '.strict-ext')
124+
self.db.add_type('test-type', '.non-strict-ext', strict=False)
125+
all = self.db.guess_all_extensions('test-type', strict=False)
126+
self.assertEqual(all, ['.strict-ext', '.non-strict-ext'])
127+
all = self.db.guess_all_extensions('test-type')
128+
self.assertEqual(all, ['.strict-ext'])
129+
# Test that changing the result list does not affect the global state
130+
all.append('.no-such-ext')
131+
all = self.db.guess_all_extensions('test-type')
132+
self.assertNotIn('.no-such-ext', all)
124133

125134
def test_encoding(self):
126135
getpreferredencoding = locale.getpreferredencoding
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Calling :func:`mimetypes.guess_all_extensions` with ``strict=False`` no
2+
longer affects the result of the following call with ``strict=True``.
3+
Also, mutating the returned list no longer affects the global state.

0 commit comments

Comments
 (0)