Skip to content

Commit 36c4297

Browse files
committed
py3: Port more CLI tools
Bring under test - test/unit/cli/test_dispersion_report.py - test/unit/cli/test_info.py and - test/unit/cli/test_relinker.py I've verified that swift-*-info (at least) behave reasonably under py3, even swift-object-info when there's non-utf8 metadata on the data/meta file. Change-Id: Ifed4b8059337c395e56f5e9f8d939c34fe4ff8dd
1 parent 624b531 commit 36c4297

File tree

11 files changed

+76
-33
lines changed

11 files changed

+76
-33
lines changed

bin/swift-object-info

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,23 @@
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
1616

17+
import codecs
1718
import sys
1819
from optparse import OptionParser
1920

21+
import six
22+
2023
from swift.common.storage_policy import reload_storage_policies
2124
from swift.common.utils import set_swift_dir
2225
from swift.cli.info import print_obj, InfoSystemExit
2326

2427

2528
if __name__ == '__main__':
29+
if not six.PY2:
30+
# Make stdout able to write escaped bytes
31+
sys.stdout = codecs.getwriter("utf-8")(
32+
sys.stdout.detach(), errors='surrogateescape')
33+
2634
parser = OptionParser('%prog [options] OBJECT_FILE')
2735
parser.add_option(
2836
'-n', '--no-check-etag', default=True,

swift/cli/info.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,8 @@ def print_obj_metadata(metadata):
352352
def print_metadata(title, items):
353353
print(title)
354354
if items:
355-
for meta_key in sorted(items):
356-
print(' %s: %s' % (meta_key, items[meta_key]))
355+
for key, value in sorted(items.items()):
356+
print(' %s: %s' % (key, value))
357357
else:
358358
print(' No metadata found')
359359

swift/common/db.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,19 @@ def utf8encode(*args):
5656
for s in args]
5757

5858

59-
def utf8encodekeys(metadata):
60-
uni_keys = [k for k in metadata if isinstance(k, six.text_type)]
61-
for k in uni_keys:
62-
sv = metadata[k]
63-
del metadata[k]
64-
metadata[k.encode('utf-8')] = sv
59+
def native_str_keys(metadata):
60+
if six.PY2:
61+
uni_keys = [k for k in metadata if isinstance(k, six.text_type)]
62+
for k in uni_keys:
63+
sv = metadata[k]
64+
del metadata[k]
65+
metadata[k.encode('utf-8')] = sv
66+
else:
67+
bin_keys = [k for k in metadata if isinstance(k, six.binary_type)]
68+
for k in bin_keys:
69+
sv = metadata[k]
70+
del metadata[k]
71+
metadata[k.decode('utf-8')] = sv
6572

6673

6774
def _db_timeout(timeout, db_file, call):
@@ -741,7 +748,7 @@ def metadata(self):
741748
metadata = self.get_raw_metadata()
742749
if metadata:
743750
metadata = json.loads(metadata)
744-
utf8encodekeys(metadata)
751+
native_str_keys(metadata)
745752
else:
746753
metadata = {}
747754
return metadata
@@ -803,7 +810,7 @@ def update_metadata(self, metadata_updates, validate_metadata=False):
803810
self.db_type)
804811
md = row[0]
805812
md = json.loads(md) if md else {}
806-
utf8encodekeys(md)
813+
native_str_keys(md)
807814
except sqlite3.OperationalError as err:
808815
if 'no such column: metadata' not in str(err):
809816
raise

swift/common/swob.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,8 @@ def setter(self, value):
290290
else:
291291
if isinstance(value, six.text_type):
292292
value = value.encode('utf-8')
293-
self.status_int = int(value.split(' ', 1)[0])
294-
self.explanation = self.title = value.split(' ', 1)[1]
293+
self.status_int = int(value.split(b' ', 1)[0])
294+
self.explanation = self.title = value.split(b' ', 1)[1]
295295

296296
return property(getter, setter,
297297
doc="Retrieve and set the Response status, e.g. '200 OK'")

swift/obj/diskfile.py

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@
8383
DEFAULT_RECLAIM_AGE = timedelta(weeks=1).total_seconds()
8484
HASH_FILE = 'hashes.pkl'
8585
HASH_INVALIDATIONS_FILE = 'hashes.invalid'
86-
METADATA_KEY = 'user.swift.metadata'
87-
METADATA_CHECKSUM_KEY = 'user.swift.metadata_checksum'
86+
METADATA_KEY = b'user.swift.metadata'
87+
METADATA_CHECKSUM_KEY = b'user.swift.metadata_checksum'
8888
DROP_CACHE_WINDOW = 1024 * 1024
8989
# These are system-set metadata keys that cannot be changed with a POST.
9090
# They should be lowercase.
@@ -131,6 +131,26 @@ def encode_str(item):
131131
return dict(((encode_str(k), encode_str(v)) for k, v in metadata.items()))
132132

133133

134+
def _decode_metadata(metadata):
135+
"""
136+
Given a metadata dict from disk, convert keys and values to native strings.
137+
138+
:param metadata: a dict
139+
"""
140+
if six.PY2:
141+
def to_str(item):
142+
if isinstance(item, six.text_type):
143+
return item.encode('utf8')
144+
return item
145+
else:
146+
def to_str(item):
147+
if isinstance(item, six.binary_type):
148+
return item.decode('utf8', 'surrogateescape')
149+
return item
150+
151+
return dict(((to_str(k), to_str(v)) for k, v in metadata.items()))
152+
153+
134154
def read_metadata(fd, add_missing_checksum=False):
135155
"""
136156
Helper function to read the pickled metadata from an object file.
@@ -144,8 +164,8 @@ def read_metadata(fd, add_missing_checksum=False):
144164
key = 0
145165
try:
146166
while True:
147-
metadata += xattr.getxattr(fd, '%s%s' % (METADATA_KEY,
148-
(key or '')))
167+
metadata += xattr.getxattr(
168+
fd, METADATA_KEY + str(key or '').encode('ascii'))
149169
key += 1
150170
except (IOError, OSError) as e:
151171
if errno.errorcode.get(e.errno) in ('ENOTSUP', 'EOPNOTSUPP'):
@@ -173,7 +193,7 @@ def read_metadata(fd, add_missing_checksum=False):
173193
logging.error("Error adding metadata: %s" % e)
174194

175195
if metadata_checksum:
176-
computed_checksum = hashlib.md5(metadata).hexdigest()
196+
computed_checksum = hashlib.md5(metadata).hexdigest().encode('ascii')
177197
if metadata_checksum != computed_checksum:
178198
raise DiskFileBadMetadataChecksum(
179199
"Metadata checksum mismatch for %s: "
@@ -183,7 +203,11 @@ def read_metadata(fd, add_missing_checksum=False):
183203
# strings are utf-8 encoded when written, but have not always been
184204
# (see https://bugs.launchpad.net/swift/+bug/1678018) so encode them again
185205
# when read
186-
return _encode_metadata(pickle.loads(metadata))
206+
if six.PY2:
207+
metadata = pickle.loads(metadata)
208+
else:
209+
metadata = pickle.loads(metadata, encoding='bytes')
210+
return _decode_metadata(metadata)
187211

188212

189213
def write_metadata(fd, metadata, xattr_size=65536):
@@ -194,11 +218,11 @@ def write_metadata(fd, metadata, xattr_size=65536):
194218
:param metadata: metadata to write
195219
"""
196220
metastr = pickle.dumps(_encode_metadata(metadata), PICKLE_PROTOCOL)
197-
metastr_md5 = hashlib.md5(metastr).hexdigest()
221+
metastr_md5 = hashlib.md5(metastr).hexdigest().encode('ascii')
198222
key = 0
199223
try:
200224
while metastr:
201-
xattr.setxattr(fd, '%s%s' % (METADATA_KEY, key or ''),
225+
xattr.setxattr(fd, METADATA_KEY + str(key or '').encode('ascii'),
202226
metastr[:xattr_size])
203227
metastr = metastr[xattr_size:]
204228
key += 1
@@ -368,9 +392,10 @@ def invalidate_hash(suffix_dir):
368392
suffix = basename(suffix_dir)
369393
partition_dir = dirname(suffix_dir)
370394
invalidations_file = join(partition_dir, HASH_INVALIDATIONS_FILE)
371-
with lock_path(partition_dir):
372-
with open(invalidations_file, 'ab') as inv_fh:
373-
inv_fh.write(suffix + "\n")
395+
if not isinstance(suffix, bytes):
396+
suffix = suffix.encode('utf-8')
397+
with lock_path(partition_dir), open(invalidations_file, 'ab') as inv_fh:
398+
inv_fh.write(suffix + b"\n")
374399

375400

376401
def relink_paths(target_path, new_target_path, check_existing=False):

test/unit/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1292,7 +1292,7 @@ def xattr_supported_check():
12921292
# assume the worst -- xattrs aren't supported
12931293
supports_xattr_cached_val = False
12941294

1295-
big_val = 'x' * (4096 + 1) # more than 4k of metadata
1295+
big_val = b'x' * (4096 + 1) # more than 4k of metadata
12961296
try:
12971297
fd, tmppath = mkstemp()
12981298
xattr.setxattr(fd, 'user.swift.testing_key', big_val)

test/unit/cli/test_info.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ class TestCliInfoBase(unittest.TestCase):
4242
def setUp(self):
4343
skip_if_no_xattrs()
4444
self.orig_hp = utils.HASH_PATH_PREFIX, utils.HASH_PATH_SUFFIX
45-
utils.HASH_PATH_PREFIX = 'info'
46-
utils.HASH_PATH_SUFFIX = 'info'
45+
utils.HASH_PATH_PREFIX = b'info'
46+
utils.HASH_PATH_SUFFIX = b'info'
4747
self.testdir = os.path.join(mkdtemp(), 'tmp_test_cli_info')
4848
utils.mkdirs(self.testdir)
4949
rmtree(self.testdir)
@@ -875,7 +875,7 @@ def test_print_obj_invalid(self):
875875
self.assertRaises(InfoSystemExit, print_obj, datafile)
876876

877877
with open(datafile, 'wb') as fp:
878-
fp.write('1234')
878+
fp.write(b'1234')
879879

880880
out = StringIO()
881881
with mock.patch('sys.stdout', out):

test/unit/cli/test_recon.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ def mock_scout_umount(app, host):
511511
self.recon_instance.umount_check(hosts)
512512

513513
output = stdout.getvalue()
514-
r = re.compile("\Not mounted:|Device errors: .*")
514+
r = re.compile("^Not mounted:|Device errors: .*")
515515
lines = output.splitlines()
516516
self.assertTrue(lines)
517517
for line in lines:

test/unit/cli/test_relinker.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def setUp(self):
6262
self.object_fname = "1278553064.00000.data"
6363
self.objname = os.path.join(self.objdir, self.object_fname)
6464
with open(self.objname, "wb") as dummy:
65-
dummy.write("Hello World!")
65+
dummy.write(b"Hello World!")
6666
write_metadata(dummy, {'name': '/a/c/o', 'Content-Length': '12'})
6767

6868
test_policies = [StoragePolicy(0, 'platin', True)]
@@ -164,7 +164,7 @@ def test_cleanup_quarantined(self):
164164
self._common_test_cleanup()
165165
# Pretend the object in the new place got corrupted
166166
with open(self.expected_file, "wb") as obj:
167-
obj.write('trash')
167+
obj.write(b'trash')
168168

169169
self.assertEqual(
170170
1, relinker.cleanup(self.testdir, self.devices, True, self.logger))

test/unit/obj/test_diskfile.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -326,16 +326,16 @@ def check_metadata():
326326
check_metadata()
327327

328328
# simulate a legacy diskfile that might have persisted unicode metadata
329-
with mock.patch.object(diskfile, '_encode_metadata', lambda x: x):
329+
with mock.patch.object(diskfile, '_decode_metadata', lambda x: x):
330330
with open(path, 'wb') as fd:
331331
diskfile.write_metadata(fd, metadata)
332332
# sanity check, while still mocked, that we did persist unicode
333333
with open(path, 'rb') as fd:
334334
actual = diskfile.read_metadata(fd)
335335
for k, v in actual.items():
336336
if k == u'X-Object-Meta-Strange':
337-
self.assertIsInstance(k, six.text_type)
338-
self.assertIsInstance(v, six.text_type)
337+
self.assertIsInstance(k, str)
338+
self.assertIsInstance(v, str)
339339
break
340340
else:
341341
self.fail('Did not find X-Object-Meta-Strange')

tox.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ setenv = VIRTUAL_ENV={envdir}
2929
[testenv:py34]
3030
commands =
3131
nosetests \
32+
test/unit/cli/test_dispersion_report.py \
33+
test/unit/cli/test_info.py \
34+
test/unit/cli/test_relinker.py \
3235
test/unit/cli/test_ring_builder_analyzer.py \
3336
test/unit/cli/test_ringbuilder.py \
3437
test/unit/common/ring \

0 commit comments

Comments
 (0)