From f3d7ef0e9ec984874dec13c1e5eb366c29bee8d2 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Sun, 23 Jan 2011 16:13:08 +0000 Subject: Create file path tuples for arbitrary file sequence numbers. --- genbackupdatalib/names.py | 41 +++++++++++++++++++++++++++++++++++++++-- genbackupdatalib/names_tests.py | 26 +++++++++++++++++++++++--- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/genbackupdatalib/names.py b/genbackupdatalib/names.py index 8cca24c..02a47ef 100644 --- a/genbackupdatalib/names.py +++ b/genbackupdatalib/names.py @@ -19,12 +19,49 @@ import os class NameGenerator(object): - '''Generate names for new output files.''' + '''Generate names for new output files. - def __init__(self, dirname): + If the target directory is empty, the sequence of output files is + always the same for the same parameters. + + A directory structure is also generated. The shape of the tree is + defined by two parameters: 'max' and 'depth'. 'depth' is the number + of levels of subdirectories to create, and 'max' is the maximum + number of files/dirs to allow per output directory. Thus, if max is + 3 and depth is 2, the output files are: 0/0/0, 0/0/1, 0/0/2, + 0/1/0, 0/1/1, etc. + + If depth is zero, all output files go directly to the target + directory, and max is ignored. + + ''' + + def __init__(self, dirname, depth, max): self.dirname = dirname + self.depth = depth + self.max = max self.counter = 0 + def _path_tuple(self, n): + '''Return tuple for dir/file numbers for nth output file. + + The last item in the tuple gives the file number, the precding + items the directory numbers. Thus, a tuple (1, 2, 3) would + mean path '1/2/3', but it is given as a tuple for easier + manipulation. + + ''' + + if self.depth == 0: + return (n,) + else: + items = [] + for i in range(self.depth + 1): # +1 for filenames + items.append(n % self.max) + n /= self.max + items.reverse() + return tuple(items) + def _next_candidate_name(self): self.counter += 1 return os.path.join(self.dirname, 'file%d' % self.counter) diff --git a/genbackupdatalib/names_tests.py b/genbackupdatalib/names_tests.py index 55cdfd7..f31a61b 100644 --- a/genbackupdatalib/names_tests.py +++ b/genbackupdatalib/names_tests.py @@ -26,10 +26,16 @@ class NameGeneratorTests(unittest.TestCase): def setUp(self): self.tempdir = tempfile.mkdtemp() - self.names = genbackupdatalib.NameGenerator(self.tempdir) + self.depth = 2 + self.max = 3 + self.names = self.new() def tearDown(self): shutil.rmtree(self.tempdir) + + def new(self): + return genbackupdatalib.NameGenerator(self.tempdir, self.depth, + self.max) def test_generates_name_that_is_inside_target_directory(self): name = self.names.new() @@ -47,15 +53,29 @@ class NameGeneratorTests(unittest.TestCase): def test_generates_the_same_sequence_with_every_instance(self): n = 10 first = [self.names.new() for i in range(n)] - names2 = genbackupdatalib.NameGenerator(self.tempdir) + names2 = self.new() second = [names2.new() for i in range(n)] self.assertEqual(first, second) def test_does_not_generate_names_of_existing_files(self): name = self.names.new() file(name, 'w').close() - names2 = genbackupdatalib.NameGenerator(self.tempdir) + names2 = self.new() name2 = names2.new() self.assertNotEqual(name, name2) self.assertFalse(os.path.exists(name2)) + def test_converts_file_sequence_number_into_right_path_tuple(self): + self.assertEqual(self.names._path_tuple(0), (0, 0, 0)) + self.assertEqual(self.names._path_tuple(1), (0, 0, 1)) + self.assertEqual(self.names._path_tuple(2), (0, 0, 2)) + self.assertEqual(self.names._path_tuple(3), (0, 1, 0)) + self.assertEqual(self.names._path_tuple(4), (0, 1, 1)) + self.assertEqual(self.names._path_tuple(5), (0, 1, 2)) + self.assertEqual(self.names._path_tuple(6), (0, 2, 0)) + self.assertEqual(self.names._path_tuple(9), (1, 0, 0)) + + def test_returns_1tuple_for_depth_zero(self): + names = genbackupdatalib.NameGenerator(self.tempdir, 0, 1) + self.assertEqual(names._path_tuple(42), (42,)) + -- cgit v1.2.1