Close ring gz file after loading

The files will eventually be closed by the garbage collector but it's
good practice to close them immediately after use, and stops some unit
test runners displaying many resource warnings about unclosed files.

Change-Id: Ieba030e0988623bf44b1c7b164e4d29990333dc0
This commit is contained in:
Alistair Coles 2021-03-10 18:50:21 +00:00
parent c2f619129c
commit 8492dc75a1
2 changed files with 37 additions and 15 deletions

View File

@ -14,6 +14,8 @@
# limitations under the License. # limitations under the License.
import array import array
import contextlib
import six.moves.cPickle as pickle import six.moves.cPickle as pickle
import json import json
from collections import defaultdict from collections import defaultdict
@ -173,22 +175,21 @@ class RingData(object):
:param bool metadata_only: If True, only load `devs` and `part_shift`. :param bool metadata_only: If True, only load `devs` and `part_shift`.
:returns: A RingData instance containing the loaded data. :returns: A RingData instance containing the loaded data.
""" """
gz_file = RingReader(filename) with contextlib.closing(RingReader(filename)) as gz_file:
# See if the file is in the new format
# See if the file is in the new format magic = gz_file.read(4)
magic = gz_file.read(4) if magic == b'R1NG':
if magic == b'R1NG': format_version, = struct.unpack('!H', gz_file.read(2))
format_version, = struct.unpack('!H', gz_file.read(2)) if format_version == 1:
if format_version == 1: ring_data = cls.deserialize_v1(
ring_data = cls.deserialize_v1( gz_file, metadata_only=metadata_only)
gz_file, metadata_only=metadata_only) else:
raise Exception('Unknown ring format version %d' %
format_version)
else: else:
raise Exception('Unknown ring format version %d' % # Assume old-style pickled ring
format_version) gz_file.seek(0)
else: ring_data = pickle.load(gz_file)
# Assume old-style pickled ring
gz_file.seek(0)
ring_data = pickle.load(gz_file)
if not hasattr(ring_data, 'devs'): if not hasattr(ring_data, 'devs'):
ring_data = RingData(ring_data['replica2part2dev_id'], ring_data = RingData(ring_data['replica2part2dev_id'],

View File

@ -113,6 +113,27 @@ class TestRingData(unittest.TestCase):
rd2 = ring.RingData.load(ring_fname) rd2 = ring.RingData.load(ring_fname)
self.assert_ring_data_equal(rd, rd2) self.assert_ring_data_equal(rd, rd2)
def test_load_closes_file(self):
ring_fname = os.path.join(self.testdir, 'foo.ring.gz')
rd = ring.RingData(
[array.array('H', [0, 1, 0, 1]), array.array('H', [0, 1, 0, 1])],
[{'id': 0, 'zone': 0}, {'id': 1, 'zone': 1}], 30)
rd.save(ring_fname)
class MockReader(ring.ring.RingReader):
calls = []
def close(self):
self.calls.append(('close', self.fp))
return super(MockReader, self).close()
with mock.patch('swift.common.ring.ring.RingReader',
MockReader) as mock_reader:
ring.RingData.load(ring_fname)
self.assertEqual([('close', mock.ANY)], mock_reader.calls)
self.assertTrue(mock_reader.calls[0][1].closed)
def test_byteswapped_serialization(self): def test_byteswapped_serialization(self):
# Manually byte swap a ring and write it out, claiming it was written # Manually byte swap a ring and write it out, claiming it was written
# on a different endian machine. Then read it back in and see if it's # on a different endian machine. Then read it back in and see if it's