Commit 347ea21d authored by Jérome Perrin's avatar Jérome Perrin

util: add type annotations and fix bytes vs str

parent 72011755
[mypy]
# XXX according to mypy doc, this is a bad idea.
ignore_missing_imports = True
...@@ -19,10 +19,15 @@ ...@@ -19,10 +19,15 @@
# See COPYING file for full licensing terms. # See COPYING file for full licensing terms.
# See https://www.nexedi.com/licensing for rationale and options. # See https://www.nexedi.com/licensing for rationale and options.
try:
from typing import Tuple
except ImportError:
pass
from collections import OrderedDict from collections import OrderedDict
# topic_name -> (topic_summary, topic_help) # topic_name -> (topic_summary, topic_help)
topic_dict = OrderedDict() topic_dict = OrderedDict() # type: OrderedDict[str, Tuple[str, str]]
help_zurl = """\ help_zurl = """\
Almost every zodb command works with a database. Almost every zodb command works with a database.
......
...@@ -41,8 +41,8 @@ def test_zodbcommit(zext): ...@@ -41,8 +41,8 @@ def test_zodbcommit(zext):
# commit some transactions via zodbcommit and verify if storage dump gives # commit some transactions via zodbcommit and verify if storage dump gives
# what is expected. # what is expected.
t1 = Transaction(z64, ' ', b'user name', b'description ...', zext(dumps({'a': 'b'}, _protocol)), [ t1 = Transaction(z64, ' ', b'user name', b'description ...', zext(dumps({'a': 'b'}, _protocol)), [
ObjectData(p64(1), b'data1', 'sha1', sha1('data1')), ObjectData(p64(1), b'data1', 'sha1', sha1(b'data1')),
ObjectData(p64(2), b'data2', 'sha1', sha1('data2'))]) ObjectData(p64(2), b'data2', 'sha1', sha1(b'data2'))])
t1.tid = zodbcommit(stor, head, t1) t1.tid = zodbcommit(stor, head, t1)
...@@ -56,7 +56,7 @@ def test_zodbcommit(zext): ...@@ -56,7 +56,7 @@ def test_zodbcommit(zext):
zodbdump(stor, p64(u64(head)+1), None, out=buf) zodbdump(stor, p64(u64(head)+1), None, out=buf)
dumped = buf.getvalue() dumped = buf.getvalue()
assert dumped == ''.join([_.zdump() for _ in (t1, t2)]) assert dumped == b''.join([_.zdump() for _ in (t1, t2)])
# ObjectCopy. XXX zodbcommit handled ObjectCopy by actually copying data, # ObjectCopy. XXX zodbcommit handled ObjectCopy by actually copying data,
# not referencing previous transaction via backpointer. # not referencing previous transaction via backpointer.
......
...@@ -21,14 +21,14 @@ from zodbtools.zodbdump import ( ...@@ -21,14 +21,14 @@ from zodbtools.zodbdump import (
zodbdump, DumpReader, Transaction, ObjectDelete, ObjectCopy, zodbdump, DumpReader, Transaction, ObjectDelete, ObjectCopy,
ObjectData, HashOnly ObjectData, HashOnly
) )
from zodbtools.util import fromhex
from ZODB.FileStorage import FileStorage from ZODB.FileStorage import FileStorage
from ZODB.utils import p64 from ZODB.utils import p64
from cStringIO import StringIO from io import BytesIO
from os.path import dirname from os.path import dirname
from zodbtools.test.testutil import zext_supported from pytest import raises
from pytest import raises, xfail
# verify zodbdump output against golden # verify zodbdump output against golden
def test_zodbdump(zext): def test_zodbdump(zext):
...@@ -39,7 +39,7 @@ def test_zodbdump(zext): ...@@ -39,7 +39,7 @@ def test_zodbdump(zext):
with open('%s/testdata/1%s.zdump.ok' % (tdir, zkind)) as f: with open('%s/testdata/1%s.zdump.ok' % (tdir, zkind)) as f:
dumpok = f.read() dumpok = f.read()
out = StringIO() out = BytesIO()
zodbdump(stor, None, None, out=out) zodbdump(stor, None, None, out=out)
assert out.getvalue() == dumpok assert out.getvalue() == dumpok
...@@ -69,10 +69,10 @@ extension "qqq" ...@@ -69,10 +69,10 @@ extension "qqq"
""" """
r = DumpReader(StringIO(in_)) r = DumpReader(BytesIO(in_))
t1 = r.readtxn() t1 = r.readtxn()
assert isinstance(t1, Transaction) assert isinstance(t1, Transaction)
assert t1.tid == '0123456789abcdef'.decode('hex') assert t1.tid == fromhex('0123456789abcdef')
assert t1.user == b'my name' assert t1.user == b'my name'
assert t1.description == b'o la-la...' assert t1.description == b'o la-la...'
assert t1.extension_bytes == b'zzz123 def' assert t1.extension_bytes == b'zzz123 def'
...@@ -83,29 +83,29 @@ extension "qqq" ...@@ -83,29 +83,29 @@ extension "qqq"
_ = t1.objv[1] _ = t1.objv[1]
assert isinstance(_, ObjectCopy) assert isinstance(_, ObjectCopy)
assert _.oid == p64(2) assert _.oid == p64(2)
assert _.copy_from == '0123456789abcdee'.decode('hex') assert _.copy_from == fromhex('0123456789abcdee')
_ = t1.objv[2] _ = t1.objv[2]
assert isinstance(_, ObjectData) assert isinstance(_, ObjectData)
assert _.oid == p64(3) assert _.oid == p64(3)
assert _.data == HashOnly(54) assert _.data == HashOnly(54)
assert _.hashfunc == 'adler32' assert _.hashfunc == 'adler32'
assert _.hash_ == '01234567'.decode('hex') assert _.hash_ == fromhex('01234567')
_ = t1.objv[3] _ = t1.objv[3]
assert isinstance(_, ObjectData) assert isinstance(_, ObjectData)
assert _.oid == p64(4) assert _.oid == p64(4)
assert _.data == b'ZZZZ' assert _.data == b'ZZZZ'
assert _.hashfunc == 'sha1' assert _.hashfunc == 'sha1'
assert _.hash_ == '9865d483bc5a94f2e30056fc256ed3066af54d04'.decode('hex') assert _.hash_ == fromhex('9865d483bc5a94f2e30056fc256ed3066af54d04')
_ = t1.objv[4] _ = t1.objv[4]
assert isinstance(_, ObjectData) assert isinstance(_, ObjectData)
assert _.oid == p64(5) assert _.oid == p64(5)
assert _.data == b'ABC\n\nDEF!' assert _.data == b'ABC\n\nDEF!'
assert _.hashfunc == 'crc32' assert _.hashfunc == 'crc32'
assert _.hash_ == '52fdeac5'.decode('hex') assert _.hash_ == fromhex('52fdeac5')
t2 = r.readtxn() t2 = r.readtxn()
assert isinstance(t2, Transaction) assert isinstance(t2, Transaction)
assert t2.tid == '0123456789abcdf0'.decode('hex') assert t2.tid == fromhex('0123456789abcdf0')
assert t2.user == b'author2' assert t2.user == b'author2'
assert t2.description == b'zzz' assert t2.description == b'zzz'
assert t2.extension_bytes == b'qqq' assert t2.extension_bytes == b'qqq'
...@@ -113,11 +113,11 @@ extension "qqq" ...@@ -113,11 +113,11 @@ extension "qqq"
assert r.readtxn() == None assert r.readtxn() == None
z = ''.join([_.zdump() for _ in (t1, t2)]) z = b''.join([_.zdump() for _ in (t1, t2)])
assert z == in_ assert z == in_
# unknown hash function # unknown hash function
r = DumpReader(StringIO("""\ r = DumpReader(BytesIO(b"""\
txn 0000000000000000 " " txn 0000000000000000 " "
user "" user ""
description "" description ""
...@@ -130,7 +130,7 @@ obj 0000000000000001 1 xyz:0123 - ...@@ -130,7 +130,7 @@ obj 0000000000000001 1 xyz:0123 -
assert exc.value.args == ("""+5: invalid line: unknown hash function "xyz" ('obj 0000000000000001 1 xyz:0123 -')""",) assert exc.value.args == ("""+5: invalid line: unknown hash function "xyz" ('obj 0000000000000001 1 xyz:0123 -')""",)
# data integrity error # data integrity error
r = DumpReader(StringIO("""\ r = DumpReader(BytesIO(b"""\
txn 0000000000000000 " " txn 0000000000000000 " "
user "" user ""
description "" description ""
...@@ -141,4 +141,4 @@ hello ...@@ -141,4 +141,4 @@ hello
""")) """))
with raises(RuntimeError) as exc: with raises(RuntimeError) as exc:
r.readtxn() r.readtxn()
assert exc.value.args == ("""+6: data corrupt: crc32 = 3610a686, expected 01234567""",) assert exc.value.args == ("""+6: data corrupt: crc32 = 3610a686, expected 01234567""",)
\ No newline at end of file
...@@ -22,7 +22,9 @@ try: ...@@ -22,7 +22,9 @@ try:
from unittest import mock from unittest import mock
except ImportError: except ImportError:
# BBB python2 # BBB python2
import mock import mock # type: ignore
# mypy complains: error: Name 'mock' already defined (by an import)
# https://github.com/python/mypy/issues/1153#issuecomment-253842414
import pytest import pytest
......
...@@ -18,20 +18,32 @@ ...@@ -18,20 +18,32 @@
# See COPYING file for full licensing terms. # See COPYING file for full licensing terms.
# See https://www.nexedi.com/licensing for rationale and options. # See https://www.nexedi.com/licensing for rationale and options.
try:
from typing import Tuple, Optional, Union, Iterable, Any, Mapping, Callable
except ImportError:
pass
import hashlib, struct, codecs import hashlib, struct, codecs
import zodburi import zodburi # type: ignore
from six.moves.urllib_parse import urlsplit, urlunsplit from six.moves.urllib_parse import urlsplit, urlunsplit
from zlib import crc32, adler32 from zlib import crc32, adler32
from ZODB.TimeStamp import TimeStamp from ZODB.TimeStamp import TimeStamp
import dateparser import dateparser
# XXX note that for ashex and fromhex I run mypy with a typeshed patch
# https://github.com/python/typeshed/issues/300#issuecomment-459151016
def ashex(s): def ashex(s):
return s.encode('hex') # type: (bytes) -> str
return codecs.encode(s, 'hex').decode()
def fromhex(s): def fromhex(s):
# type: (Union[str,bytes]) -> bytes
return codecs.decode(s, 'hex') return codecs.decode(s, 'hex')
def sha1(data): def sha1(data):
# type: (bytes) -> bytes
m = hashlib.sha1() m = hashlib.sha1()
m.update(data) m.update(data)
return m.digest() return m.digest()
...@@ -53,6 +65,8 @@ def nextitem(it): ...@@ -53,6 +65,8 @@ def nextitem(it):
# objects of a IStorageTransactionInformation # objects of a IStorageTransactionInformation
def txnobjv(txn): def txnobjv(txn):
# type: (Any) -> Iterable[Any]
# XXX type ?
objv = [] objv = []
for obj in txn: for obj in txn:
assert obj.tid == txn.tid assert obj.tid == txn.tid
...@@ -72,6 +86,7 @@ class TidRangeInvalid(ValueError): ...@@ -72,6 +86,7 @@ class TidRangeInvalid(ValueError):
def parse_tid(tid_string, raw_only=False): def parse_tid(tid_string, raw_only=False):
# type: (str, bool) -> bytes
"""Try to parse `tid_string` as a time and returns the """Try to parse `tid_string` as a time and returns the
corresponding raw TID. corresponding raw TID.
If `tid_string` cannot be parsed as a time, assume it was If `tid_string` cannot be parsed as a time, assume it was
...@@ -121,19 +136,16 @@ def parse_tid(tid_string, raw_only=False): ...@@ -121,19 +136,16 @@ def parse_tid(tid_string, raw_only=False):
# #
# see `zodb help tidrange` for accepted tidrange syntax. # see `zodb help tidrange` for accepted tidrange syntax.
def parse_tidrange(tidrange): def parse_tidrange(tidrange):
# type: (str) -> Tuple[Optional[bytes], Optional[bytes]]
try: try:
tidmin, tidmax = tidrange.split("..") tidmin, tidmax = tidrange.split("..")
except ValueError: # not exactly 2 parts in between ".." except ValueError: # not exactly 2 parts in between ".."
raise TidRangeInvalid(tidrange) raise TidRangeInvalid(tidrange)
if tidmin:
tidmin = parse_tid(tidmin)
if tidmax:
tidmax = parse_tid(tidmax)
# empty tid means -inf / +inf respectively # empty tid means -inf / +inf respectively
# ( which is None in IStorage.iterator() ) # ( which is None in IStorage.iterator() )
return (tidmin or None, tidmax or None) return (parse_tid(tidmin) if tidmin else None,
parse_tid(tidmax) if tidmax else None)
# storageFromURL opens a ZODB-storage specified by url # storageFromURL opens a ZODB-storage specified by url
...@@ -169,12 +181,15 @@ class NullHasher: ...@@ -169,12 +181,15 @@ class NullHasher:
digest_size = 1 digest_size = 1
def update(self, data): def update(self, data):
# type: (bytes) -> None
pass pass
def digest(self): def digest(self):
# type: () -> bytes
return b'\0' return b'\0'
def hexdigest(self): def hexdigest(self):
# type: () -> str
return "00" return "00"
# adler32 in hashlib interface # adler32 in hashlib interface
...@@ -183,15 +198,19 @@ class Adler32Hasher: ...@@ -183,15 +198,19 @@ class Adler32Hasher:
digest_size = 4 digest_size = 4
def __init__(self): def __init__(self):
self._h = adler32('') # type: () -> None
self._h = adler32(b'')
def update(self, data): def update(self, data):
# type: (bytes) -> None
self._h = adler32(data, self._h) self._h = adler32(data, self._h)
def digest(self): def digest(self):
# type: () -> bytes
return struct.pack('>I', self._h & 0xffffffff) return struct.pack('>I', self._h & 0xffffffff)
def hexdigest(self): def hexdigest(self):
# type: () -> str
return '%08x' % (self._h & 0xffffffff) return '%08x' % (self._h & 0xffffffff)
# crc32 in hashlib interface # crc32 in hashlib interface
...@@ -200,15 +219,19 @@ class CRC32Hasher: ...@@ -200,15 +219,19 @@ class CRC32Hasher:
digest_size = 4 digest_size = 4
def __init__(self): def __init__(self):
self._h = crc32('') # type: () -> None
self._h = crc32(b'')
def update(self, data): def update(self, data):
# type: (bytes) -> None
self._h = crc32(data, self._h) self._h = crc32(data, self._h)
def digest(self): def digest(self):
# type: () -> bytes
return struct.pack('>I', self._h & 0xffffffff) return struct.pack('>I', self._h & 0xffffffff)
def hexdigest(self): def hexdigest(self):
# type: () -> str
return '%08x' % (self._h & 0xffffffff) return '%08x' % (self._h & 0xffffffff)
# {} name -> hasher # {} name -> hasher
...@@ -219,4 +242,4 @@ hashRegistry = { ...@@ -219,4 +242,4 @@ hashRegistry = {
"sha1": hashlib.sha1, "sha1": hashlib.sha1,
"sha256": hashlib.sha256, "sha256": hashlib.sha256,
"sha512": hashlib.sha512, "sha512": hashlib.sha512,
} } # type: Mapping[str, Callable] # XXX "Callable" is a bit too wide typing
...@@ -9,7 +9,7 @@ from __future__ import print_function ...@@ -9,7 +9,7 @@ from __future__ import print_function
import sys import sys
import os import os
import getopt import getopt
from six.moves import dbm_gnu as dbm from six.moves import dbm_gnu as dbm # type: ignore
import tempfile import tempfile
import shutil import shutil
from ZODB.FileStorage import FileIterator, packed_version from ZODB.FileStorage import FileIterator, packed_version
......
...@@ -53,6 +53,10 @@ TODO also protect txn record by hash. ...@@ -53,6 +53,10 @@ TODO also protect txn record by hash.
""" """
from __future__ import print_function from __future__ import print_function
try:
from typing import Any, Set, Optional, BinaryIO, NoReturn, Union
except ImportError:
pass
from zodbtools.util import ashex, fromhex, sha1, txnobjv, parse_tidrange, TidRangeInvalid, \ from zodbtools.util import ashex, fromhex, sha1, txnobjv, parse_tidrange, TidRangeInvalid, \
storageFromURL, hashRegistry storageFromURL, hashRegistry
from ZODB._compat import loads, _protocol, BytesIO from ZODB._compat import loads, _protocol, BytesIO
...@@ -61,6 +65,7 @@ from zodbpickle.slowpickle import Pickler as pyPickler ...@@ -61,6 +65,7 @@ from zodbpickle.slowpickle import Pickler as pyPickler
from ZODB.interfaces import IStorageTransactionInformation from ZODB.interfaces import IStorageTransactionInformation
from zope.interface import implementer from zope.interface import implementer
import six
import sys import sys
import logging import logging
import re import re
...@@ -92,50 +97,53 @@ def txn_raw_extension(stor, txn): ...@@ -92,50 +97,53 @@ def txn_raw_extension(stor, txn):
return serializeext(txn.extension) return serializeext(txn.extension)
# set of storage names already warned for not providing IStorageTransactionInformationRaw # set of storage names already warned for not providing IStorageTransactionInformationRaw
_already_warned_notxnraw = set() _already_warned_notxnraw = set() # type: Set[str]
# zodbdump dumps content of a ZODB storage to a file. # zodbdump dumps content of a ZODB storage to a file.
# please see module doc-string for dump format and details # please see module doc-string for dump format and details
def zodbdump(stor, tidmin, tidmax, hashonly=False, out=sys.stdout): def zodbdump(stor, tidmin, tidmax, hashonly=False, out=sys.stdout.buffer):
# type: (Any, Optional[bytes], Optional[bytes], bool, BinaryIO) -> None
for txn in stor.iterator(tidmin, tidmax): for txn in stor.iterator(tidmin, tidmax):
# XXX .status not covered by IStorageTransactionInformation # XXX .status not covered by IStorageTransactionInformation
# XXX but covered by BaseStorage.TransactionRecord # XXX but covered by BaseStorage.TransactionRecord
out.write("txn %s %s\nuser %s\ndescription %s\nextension %s\n" % ( out.write(("txn %s %s\nuser %s\ndescription %s\nextension %s\n" % (
ashex(txn.tid), qq(txn.status), ashex(txn.tid), qq(txn.status),
qq(txn.user), qq(txn.user),
qq(txn.description), qq(txn.description),
qq(txn_raw_extension(stor, txn)) )) qq(txn_raw_extension(stor, txn)) )).encode())
objv = txnobjv(txn) objv = txnobjv(txn)
for obj in objv: for obj in objv:
entry = "obj %s " % ashex(obj.oid) entry = b"obj %s " % ashex(obj.oid).encode()
write_data = False write_data = False
if obj.data is None: if obj.data is None:
entry += "delete" entry += b"delete"
# was undo and data taken from obj.data_txn # was undo and data taken from obj.data_txn
elif obj.data_txn is not None: elif obj.data_txn is not None:
entry += "from %s" % ashex(obj.data_txn) entry += b"from %s" % ashex(obj.data_txn)
else: else:
# XXX sha1 is hardcoded for now. Dump format allows other hashes. # XXX sha1 is hardcoded for now. Dump format allows other hashes.
entry += "%i sha1:%s" % (len(obj.data), ashex(sha1(obj.data))) entry += b"%i sha1:%s" % (len(obj.data), ashex(sha1(obj.data)).encode())
write_data = True write_data = True
if six.PY2:
entry = entry.encode('utf-8')
out.write(entry) out.write(entry)
if write_data: if write_data:
if hashonly: if hashonly:
out.write(" -") out.write(b" -")
else: else:
out.write("\n") out.write(b"\n")
out.write(obj.data) out.write(obj.data or b"")
out.write("\n") out.write(b"\n")
out.write("\n") out.write(b"\n")
# ---------------------------------------- # ----------------------------------------
# XPickler is Pickler that tries to save objects stably # XPickler is Pickler that tries to save objects stably
...@@ -309,13 +317,15 @@ class DumpReader(object): ...@@ -309,13 +317,15 @@ class DumpReader(object):
# .lineno - line number position in read stream # .lineno - line number position in read stream
def __init__(self, r): def __init__(self, r):
self._r = r # type (BinaryIO) -> None
self._r = r # type: BinaryIO
self._line = None # last read line self._line = None # last read line
self.lineno = 0 self.lineno = 0
def _readline(self): def _readline(self):
# type: () -> Optional[bytes]
l = self._r.readline() l = self._r.readline()
if l == '': if l == b'':
self._line = None self._line = None
return None # EOF return None # EOF
...@@ -326,11 +336,17 @@ class DumpReader(object): ...@@ -326,11 +336,17 @@ class DumpReader(object):
# report a problem found around currently-read line # report a problem found around currently-read line
def _badline(self, msg): def _badline(self, msg):
raise RuntimeError("%s+%d: invalid line: %s (%r)" % (_ioname(self._r), self.lineno, msg, self._line)) # type: (str) -> NoReturn
raise RuntimeError("%s+%d: invalid line: %s (%r)" % (
_ioname(self._r),
self.lineno, msg,
# BBB produce same output in python 2 and 3
self._line.decode() if six.PY3 else self._line.encode()))
# readtxn reads one transaction record from input stream and returns # readtxn reads one transaction record from input stream and returns
# Transaction instance or None at EOF. # Transaction instance or None at EOF.
def readtxn(self): def readtxn(self):
# type: () -> Optional[Transaction]
# header # header
l = self._readline() l = self._readline()
if l is None: if l is None:
...@@ -356,7 +372,7 @@ class DumpReader(object): ...@@ -356,7 +372,7 @@ class DumpReader(object):
objv = [] objv = []
while 1: while 1:
l = self._readline() l = self._readline()
if l == '': if l == b'':
break # empty line - end of transaction break # empty line - end of transaction
if l is None or not l.startswith(b'obj '): if l is None or not l.startswith(b'obj '):
...@@ -366,7 +382,7 @@ class DumpReader(object): ...@@ -366,7 +382,7 @@ class DumpReader(object):
if m is None: if m is None:
self._badline('invalid obj entry') self._badline('invalid obj entry')
obj = None # will be Object* obj = None # type: Optional[Union[ObjectDelete, ObjectCopy, ObjectData]]
oid = fromhex(m.group('oid')) oid = fromhex(m.group('oid'))
from_ = m.group('from') from_ = m.group('from')
...@@ -380,10 +396,10 @@ class DumpReader(object): ...@@ -380,10 +396,10 @@ class DumpReader(object):
else: else:
size = int(m.group('size')) size = int(m.group('size'))
hashfunc = m.group('hashfunc') hashfunc = m.group('hashfunc').decode()
hashok = fromhex(m.group('hash')) hashok = fromhex(m.group('hash'))
hashonly = m.group('hashonly') is not None hashonly = m.group('hashonly') is not None
data = None # see vvv data = None # type: Optional[Union[HashOnly, bytes]] # see vvv
hcls = hashRegistry.get(hashfunc) hcls = hashRegistry.get(hashfunc)
if hcls is None: if hcls is None:
...@@ -399,7 +415,7 @@ class DumpReader(object): ...@@ -399,7 +415,7 @@ class DumpReader(object):
chunk = self._r.read(n) chunk = self._r.read(n)
data += chunk data += chunk
n -= len(chunk) n -= len(chunk)
self.lineno += data.count('\n') self.lineno += data.count(b'\n')
self._line = None self._line = None
if data[-1:] != b'\n': if data[-1:] != b'\n':
raise RuntimeError('%s+%d: no LF after obj data' % (_ioname(self._r), self.lineno)) raise RuntimeError('%s+%d: no LF after obj data' % (_ioname(self._r), self.lineno))
...@@ -460,13 +476,14 @@ class Transaction(object): ...@@ -460,13 +476,14 @@ class Transaction(object):
# zdump returns text representation of a record in zodbdump format. # zdump returns text representation of a record in zodbdump format.
def zdump(self): def zdump(self):
z = 'txn %s %s\n' % (ashex(self.tid), qq(self.status)) # type: () -> bytes
z += 'user %s\n' % qq(self.user) z = b'txn %s %s\n' % (ashex(self.tid).encode(), qq(self.status).encode())
z += 'description %s\n' % qq(self.description) z += b'user %s\n' % qq(self.user).encode()
z += 'extension %s\n' % qq(self.extension_bytes) z += b'description %s\n' % qq(self.description).encode()
z += b'extension %s\n' % qq(self.extension_bytes).encode()
for obj in self.objv: for obj in self.objv:
z += obj.zdump() z += obj.zdump()
z += '\n' z += b'\n'
return z return z
...@@ -483,7 +500,8 @@ class ObjectDelete(Object): ...@@ -483,7 +500,8 @@ class ObjectDelete(Object):
super(ObjectDelete, self).__init__(oid) super(ObjectDelete, self).__init__(oid)
def zdump(self): def zdump(self):
return 'obj %s delete\n' % (ashex(self.oid)) # type: () -> bytes
return b'obj %s delete\n' % (ashex(self.oid).encode())
# ObjectCopy represents object data copy. # ObjectCopy represents object data copy.
class ObjectCopy(Object): class ObjectCopy(Object):
...@@ -493,7 +511,8 @@ class ObjectCopy(Object): ...@@ -493,7 +511,8 @@ class ObjectCopy(Object):
self.copy_from = copy_from self.copy_from = copy_from
def zdump(self): def zdump(self):
return 'obj %s from %s\n' % (ashex(self.oid), ashex(self.copy_from)) # type: () -> bytes
return b'obj %s from %s\n' % (ashex(self.oid).encode(), ashex(self.copy_from).encode())
# ObjectData represents record with object data. # ObjectData represents record with object data.
class ObjectData(Object): class ObjectData(Object):
...@@ -507,19 +526,20 @@ class ObjectData(Object): ...@@ -507,19 +526,20 @@ class ObjectData(Object):
self.hash_ = hash_ self.hash_ = hash_
def zdump(self): def zdump(self):
# type: () -> bytes
data = self.data data = self.data
hashonly = isinstance(data, HashOnly) hashonly = isinstance(data, HashOnly)
if hashonly: if hashonly:
size = data.size size = data.size
else: else:
size = len(data) size = len(data)
z = 'obj %s %d %s:%s' % (ashex(self.oid), size, self.hashfunc, ashex(self.hash_)) z = b'obj %s %d %s:%s' % (ashex(self.oid).encode(), size, self.hashfunc.encode(), ashex(self.hash_).encode())
if hashonly: if hashonly:
z += ' -' z += b' -'
else: else:
z += '\n' z += b'\n'
z += data z += data
z += '\n' z += b'\n'
return z return z
# HashOnly indicated that this ObjectData record contains only hash and does not contain object data. # HashOnly indicated that this ObjectData record contains only hash and does not contain object data.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment