diff -r 8e7a738c3840 Lib/gzip.py --- a/Lib/gzip.py Sat Nov 16 10:47:17 2013 -0500 +++ b/Lib/gzip.py Sat Nov 16 21:17:50 2013 +0200 @@ -9,6 +9,7 @@ import zlib import builtins import io +import collections __all__ = ["GzipFile", "open", "compress", "decompress"] @@ -16,6 +17,8 @@ READ, WRITE = 1, 2 +_EXTRA_FIELD_STRUCT = struct.Struct('<2sH') + def open(filename, mode="rb", compresslevel=9, encoding=None, errors=None, newline=None): """Open a gzip-compressed file in binary or text mode. @@ -121,6 +124,37 @@ return getattr(self.file, name) +class ExtraMap(collections.MutableMapping): + def __init__(self, zinfo): + self._zinfo = zinfo + + def __getitem__(self, key): + for xid, data in reversed(self._zinfo._extra_list): + if xid == key: + return data + raise KeyError(key) + + def __setitem__(self, key, value): + self._zinfo._extra_list = [(xid, data) + for xid, data in self._zinfo._extra_list + if xid != key] + [(key, value)] + + def __delitem__(self, key): + self._zinfo._extra_list = [(xid, data) + for xid, data in self._zinfo._extra_list + if xid != key] + + def __iter__(self): + for xid, data in self._zinfo._extra_list: + yield xid + + def items(self): + return iter(self._zinfo._extra_list) + + def __len__(self): + return len(self._zinfo._extra_list) + + class GzipFile(io.BufferedIOBase): """The GzipFile class simulates most of the methods of a file object with the exception of the readinto() and truncate() methods. @@ -134,7 +168,7 @@ max_read_chunk = 10 * 1024 * 1024 # 10Mb def __init__(self, filename=None, mode=None, - compresslevel=9, fileobj=None, mtime=None): + compresslevel=9, fileobj=None, mtime=None, *, extra=None): """Constructor for the GzipFile class. At least one of fileobj and filename must be given a @@ -186,6 +220,9 @@ if mode is None: mode = getattr(fileobj, 'mode', 'rb') + self._extra_bytes = b'' + self._extra_items = None + if mode.startswith('r'): self.mode = READ # Set flag indicating start of a new member @@ -203,6 +240,14 @@ elif mode.startswith(('w', 'a', 'x')): self.mode = WRITE + if extra is None: + self.extra_bytes = b'' + elif isinstance(extra, (bytes, bytearray)): + self.extra_bytes = extra + else: + if hasattr(extra, 'items'): + extra = extra.items() + self._extra_list = tuple(extra) self._init_write(filename) self.compress = zlib.compressobj(compresslevel, zlib.DEFLATED, @@ -264,13 +309,18 @@ flags = 0 if fname: flags = FNAME - self.fileobj.write(chr(flags).encode('latin-1')) + if self.extra_bytes: + flags |= FEXTRA + self.fileobj.write(struct.pack('B', flags)) mtime = self.mtime if mtime is None: mtime = time.time() write32u(self.fileobj, int(mtime)) self.fileobj.write(b'\002') self.fileobj.write(b'\377') + if self.extra_bytes: + self.fileobj.write(struct.unpack('