|
@@ -123,6 +123,53 @@ def sparsemap(fd=None, fh=-1):
|
|
|
dseek(curr, os.SEEK_SET, fd, fh)
|
|
|
|
|
|
|
|
|
+class ChunkerFailing:
|
|
|
+ """
|
|
|
+ This is a very simple chunker for testing purposes.
|
|
|
+
|
|
|
+ Reads block_size chunks, starts failing at block <fail_start>, <fail_count> failures, then succeeds.
|
|
|
+ """
|
|
|
+ def __init__(self, block_size, map):
|
|
|
+ self.block_size = block_size
|
|
|
+ # one char per block: r/R = successful read, e/E = I/O Error, e.g.: "rrrrErrrEEr"
|
|
|
+ # blocks beyond the map will have same behaviour as the last map char indicates.
|
|
|
+ map = map.upper()
|
|
|
+ if not set(map).issubset({"R", "E"}):
|
|
|
+ raise ValueError("unsupported map character")
|
|
|
+ self.map = map
|
|
|
+ self.count = 0
|
|
|
+ self.chunking_time = 0.0 # not updated, just provided so that caller does not crash
|
|
|
+
|
|
|
+ def chunkify(self, fd=None, fh=-1):
|
|
|
+ """
|
|
|
+ Cut a file into chunks.
|
|
|
+
|
|
|
+ :param fd: Python file object
|
|
|
+ :param fh: OS-level file handle (if available),
|
|
|
+ defaults to -1 which means not to use OS-level fd.
|
|
|
+ """
|
|
|
+ use_fh = fh >= 0
|
|
|
+ wanted = self.block_size
|
|
|
+ while True:
|
|
|
+ data = os.read(fh, wanted) if use_fh else fd.read(wanted)
|
|
|
+ got = len(data)
|
|
|
+ if got > 0:
|
|
|
+ idx = self.count if self.count < len(self.map) else -1
|
|
|
+ behaviour = self.map[idx]
|
|
|
+ if behaviour == "E":
|
|
|
+ self.count += 1
|
|
|
+ fname = None if use_fh else getattr(fd, "name", None)
|
|
|
+ raise OSError(errno.EIO, "simulated I/O error", fname)
|
|
|
+ elif behaviour == "R":
|
|
|
+ self.count += 1
|
|
|
+ yield Chunk(data, size=got, allocation=CH_DATA)
|
|
|
+ else:
|
|
|
+ raise ValueError("unsupported map character")
|
|
|
+ if got < wanted:
|
|
|
+ # we did not get enough data, looks like EOF.
|
|
|
+ return
|
|
|
+
|
|
|
+
|
|
|
class ChunkerFixed:
|
|
|
"""
|
|
|
This is a simple chunker for input data with data usually staying at same
|
|
@@ -294,6 +341,8 @@ def get_chunker(algo, *params, **kw):
|
|
|
if algo == 'fixed':
|
|
|
sparse = kw['sparse']
|
|
|
return ChunkerFixed(*params, sparse=sparse)
|
|
|
+ if algo == 'fail':
|
|
|
+ return ChunkerFailing(*params)
|
|
|
raise TypeError('unsupported chunker algo %r' % algo)
|
|
|
|
|
|
|