#!/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: # this class is a simple wrapper for os.open, os.read, os.write and os.close, that should in turn allow us to wrap # these os.* routines with bufsock. Alternatively, we should also be able to wrap a python file object with bufsock, # but then you end up with two layers of buffering, each with slightly different functionality 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() tar = tarfile.open(fileobj=No_tell_file(rawio(handle = 1)), mode="w") mtime = time.time() for name, length, file_ in [ string_file_1, string_file_2, string_file_3, dyn_file_1 ]: 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()