#!/usr/bin/env python import os import sys import time import random import tarfile try: import StringIO as stringio except ImportError: import io as stringio class No_tell_file: ''' This is somewhat file-like, but we make tell always report 0L and seek raise an exception, because we don't need tarfile to seek or tell, but it's insisting on tell'ing anyway. ''' def __init__(self, file_): self.file_ = file_ def seek(self, *args, **kwargs): raise NotImplementedError('No_tell_file class does not support seeking') def tell(self, *args, **kwargs): return 0 def read(self, *args, **kwargs): self.file_.read(*args, **kwargs) def write(self, *args, **kwargs): self.file_.write(*args, **kwargs) class Dynamically_generated_file: chars = '0123456789abcdef' def __init__(self): self.length = int(random.random() * 2**20) sys.stderr.write('Intending a length of %d\n' % self.length) self.position = 0 self.currently_have = '' self.charno = 0 def read(self, length_desired): # inhale while len(self.currently_have) < length_desired: chunk_length = int(random.random() * 2**17) if self.position + chunk_length > self.length: chunk_length = self.length - self.position if chunk_length == 0: # we've inhaled all we're gonna (supposed to) get already break self.currently_have += chunk_length * self.chars[self.charno] self.charno = (self.charno + 1) % len(self.chars) # advance the "file" pointer and dole out result = self.currently_have[:length_desired] self.currently_have = self.currently_have[length_desired:] return result class rawio: # mostly here to get binary I/O that works in 2.x and 3.x def __init__(self, filename=None, mode=7*64 + 7*8 + 7, perms = 6*64 + 6*8 + 6, handle=None): assert (filename != None) + (handle != None) == 1 self.perms = perms if filename: self.filename = filename if mode == 'r': self.mode = os.O_RDONLY elif mode == 'w': self.mode = os.O_WRONLY | os.O_CREAT | os.O_TRUNC elif mode == 'rw': self.mode = os.O_RDRW | os.O_CREAT else: raise ValueError('Invalid mode: %s' % mode) self.file_descriptor = os.open(filename, self.mode, perms) elif handle: self.file_descriptor = handle open = __init__ def read(self, length): return os.read(self.file_descriptor, length) def write(self, data): if hasattr(data, 'encode'): return os.write(self.file_descriptor, data.encode('latin-1')) else: return os.write(self.file_descriptor, data) def close(self): os.close(self.file_descriptor) def __enter__(self): return self def __exit__(self, *dummy): os.close(self.file_descriptor) string_file_1 = "stringio first.txt", 8, stringio.StringIO("one one\n") string_file_2 = "stringio second.txt", 4, stringio.StringIO("two\n") string_file_3 = "stringio third.txt", 2**16, stringio.StringIO('\0' * 2**16) dyn_file_1 = "dynamic first.txt", None, Dynamically_generated_file() # this works tar = tarfile.open(fileobj=No_tell_file(rawio(handle = 1)), mode="w") # this has problems #tar = tarfile.open(fileobj=rawio(handle = 1), mode="w") mtime = time.time() for name, length, file_ in [ string_file_1, string_file_2, dyn_file_1, string_file_3 ]: tarinfo = tarfile.TarInfo(name) if length == None: tarinfo.size = file_.length else: tarinfo.size = length tarinfo.mtime = mtime # add more attributes as needed tar.addfile(tarinfo, file_) tar.close()