class TestFileSystem(unittest.TestCase): def setUp(self): self.fs = FileSystem() self.testcwd = os.getcwd() self.testfilepath = tarman.tests.test_containers.__file__ self.testdirectory = os.path.dirname(self.testfilepath) self.testdatadir = os.path.join(self.testdirectory, 'testdata') def test_container(self): self.assertTrue(isinstance(self.fs, Container)) def test_listdir(self): self.assertEqual(self.fs.listdir(self.testdirectory), os.listdir(self.testdirectory)) def test_isenterable(self): self.assertTrue(self.fs.isenterable(self.testdirectory)) def test_abspath(self): self.assertEqual(self.fs.abspath('.'), self.testcwd) def test_dirname(self): self.assertEqual(self.fs.dirname(self.testfilepath), self.testdirectory) def test_basename(self): self.assertEqual(self.fs.basename(self.testfilepath), os.path.basename(self.testfilepath)) def test_join(self): self.assertEqual(self.fs.join('/home', 'someone', 'bin', 'python'), '/home/someone/bin/python') def test_split(self): self.assertEqual(self.fs.split('/home/someone/bin/python'), ('/home/someone/bin', 'python')) def test_samefile(self): self.assertTrue(self.fs.samefile(self.testfilepath, self.testfilepath)) self.assertFalse( self.fs.samefile(self.testfilepath, tarman.tests.test_tree.__file__)) def test_count_items(self): self.assertEqual(self.fs.count_items(self.testdatadir), 17) self.assertEqual(self.fs.count_items(self.testdatadir, stop_at=9), 9)
class TestDirectoryTree(unittest.TestCase): def setUp(self): self.testfilepath = tarman.tests.test_containers.__file__ self.testdirectory = os.path.dirname(self.testfilepath) self.testdatapath = os.path.join( self.testdirectory, 'testdata', 'testdata' ) self.fs = FileSystem() def test_init(self): self.assertIsNotNone( DirectoryTree(self.testdatapath, self.fs) ) def test_add_file(self): tree = DirectoryTree(self.testdatapath, self.fs) path1 = self.fs.join(self.testdatapath, 'a', 'aa', 'aaa') path2 = self.fs.join(self.testdatapath, 'a', 'aa') tree.add(path1) self.assertIn(path1, tree) self.assertIn(path2, tree) # not-added one-level-up directory def test_add_dir(self): tree = DirectoryTree(self.testdatapath, self.fs) dir1 = self.fs.join(self.testdatapath, 'a', 'ab') tree.add(dir1) self.assertIn(dir1, tree) def test_out_of_range(self): tree = DirectoryTree(self.testdatapath, self.fs) # one level up directory dir1 = self.fs.abspath(self.fs.join(self.testdatapath, '..')) with self.assertRaises(OutOfRange): tree.add(dir1) # completely different directory with tempfile.NamedTemporaryFile() as f: with self.assertRaises(OutOfRange): tree.add(f.name)
class Main(object): @utf8_args(3) def __init__(self, mainscr, stdscr, directory, encoding): self.encoding = encoding self.header_lns = HEADER_LNS self.mainscr = mainscr self.stdscr = stdscr self.color = curses.has_colors() if self.color: # set file type attributes (color and bold) curses.init_pair(1, curses.COLOR_BLUE, -1) self.attr_folder = curses.color_pair(1) | curses.A_BOLD curses.init_pair(2, 7, -1) self.attr_norm = curses.color_pair(2) # set wright / wrong attributes (color and bold) curses.init_pair(3, curses.COLOR_GREEN, -1) self.attr_wright = curses.color_pair(3) | curses.A_BOLD curses.init_pair(4, curses.COLOR_RED, -1) self.attr_wrong = curses.color_pair(4) | curses.A_BOLD self.kill = False self.ch = -1 self.visited = {} self.area = None self.container = FileSystem() self.directory = self.container.abspath(directory) self.checked = DirectoryTree(self.directory, self.container) self.chdir(self.directory) @utf8_args(1, 2) def header(self, prefix, path): #self.mainscr.clear() h, w = self.mainscr.getmaxyx() sep = " " length = len(prefix) + len(path) + len(sep) empty = 0 if length > w: path = "..." + path[length - w + 3:] else: empty = w - length self.mainscr.addstr( 0, 0, u"{0}{1}{2}{3}".format( prefix, sep, path, empty * ' ' ).encode(self.encoding) ) self.mainscr.refresh() def identify_container_and_checked(self, path): if self.container.isenterable(path): # is folder return self.container, self.checked # force one-level archive browsing if not isinstance(self.container, FileSystem): return None, None aclass = get_archive_class(path) if not aclass: return None, None workwin = WorkWin(self) workwin.show("Working ...") newcontainer = aclass(path) newchecked = DirectoryTree(path, newcontainer) workwin.close() return newcontainer, newchecked def chdir(self, newpath): if newpath is None: return False if not newpath.startswith(self.directory): return False try: if self.area is None: oldsel = 0 oldpath = self.directory else: oldsel = self.area.selected oldpath = self.area.abspath oldcontainer = self.container oldchecked = self.checked if newpath in self.visited: newsel, newcontainer, newchecked = self.visited[newpath] else: newcontainer, newchecked = \ self.identify_container_and_checked(newpath) if newcontainer is None: return False newsel = 0 self.visited[oldpath] = [oldsel, oldcontainer, oldchecked] logging.info(u"OLD - {0} - {1} - {2}".format( oldpath, oldsel, oldcontainer.__class__.__name__ )) logging.info(u"NEW - {0} - {1} - {2}".format( newpath, newsel, newcontainer.__class__.__name__ )) h, w = self.stdscr.getmaxyx() self.container = newcontainer self.checked = newchecked def show_unreadable_error(path, name): if isinstance(name, unicode): error_str = name.encode('utf8', errors='replace') else: error_str = name logging.info("Unreadable file name: {0} (in '{1}')".format( error_str, path )) errorwin = TextWin(self) errorwin.show("Unreadable file name:\n{0}\n\nPress ESC to close.".format(error_str)) self.area = ViewArea( newpath, h, newcontainer, show_unreadable_error ) self.header( "{0}".format( self.container.__class__.__name__ ), self.area.abspath ) self.area.set_params(h, offset=newsel) self.refresh_scr() return True except OutOfRange: logging.error("OutOfRange .. {0}".format(newpath)) curses.flash() def insert_line(self, y, item): i, name, abspath = item self.stdscr.addstr( y, 0, "[{0}]".format( '*' if abspath in self.checked else ' ' ).encode(self.encoding) ) if self.color: if self.container.isenterable(abspath): attr = self.attr_folder name = u"{0}/".format(name) else: attr = self.attr_norm self.stdscr.addstr(y, 5, name.encode(self.encoding), attr) else: if self.container.isenterable(abspath): name = u"{0}/".format(name) self.stdscr.addstr(y, 5, name.encode(self.encoding)) def refresh_scr(self): self.stdscr.clear() if not getattr(self, 'area', None): return if len(self.area) == 0: self.stdscr.addstr(1, 5, "Directory is empty!") return iitem = 0 for item in self.area: self.insert_line(iitem, item) iitem += 1 y = self.area.selected_local h, w = self.stdscr.getmaxyx() self.stdscr.chgat(y, 0, w, curses.A_REVERSE) self.stdscr.move(y, 1) def loop(self): while not self.kill: self.ch = self.stdscr.getch() h, w = self.stdscr.getmaxyx() if self.ch in [ord('q'), ]: self.kill = True elif self.ch == curses.KEY_UP: self.area.set_params(h, offset=-1) elif self.ch == curses.KEY_DOWN: self.area.set_params(h, offset=1) elif self.ch == curses.KEY_PPAGE: self.area.set_params(h, offset=-5) elif self.ch == curses.KEY_NPAGE: self.area.set_params(h, offset=5) elif self.ch == 32: index = self.area.selected if index == -1: curses.flash() continue abspath = self.area.get_abspath(index) if abspath in self.checked: del self.checked[abspath] else: countitems = self.container.count_items( abspath, stop_at=ITEMS_WARNING ) if countitems >= ITEMS_WARNING and \ self.show_items_warning() != 0: continue self.checked.add(abspath, sub=True) elif self.ch in [curses.KEY_RIGHT, 10, 13]: index = self.area.selected if index == -1: curses.flash() continue abspath = self.area.get_abspath(index) result = self.chdir(abspath) if not result: curses.flash() elif self.ch in [curses.KEY_LEFT, 127, curses.ascii.BS, curses.KEY_BACKSPACE]: if not self.chdir( self.container.dirname(self.area.abspath) ): curses.flash() elif self.ch in [ord('c'), ord('C')]: if isinstance(self.container, FileSystem): aclass = self.container.__class__ checked = self.checked container = self.container pathwin = PathWin(self) exitstatus, archivepath = pathwin.show( "Create archive with format/compression based on file" " extension (ENTER to confirm or ESC to cancel):", os.path.join(os.getcwd(), "NewArchive.tar.gz") ) pathwin.close() logging.info("window exitstatus: {0}, '{1}'".format( exitstatus, archivepath )) if exitstatus != 0: continue archivepath = os.path.abspath(archivepath) aclass = get_archive_class(archivepath) if aclass is None: curses.flash() continue created = aclass.create(container, archivepath, checked) if created: TextWin(self).show( "Successfully created archive:\n{0}".format( archivepath ) ) else: curses.flash() elif self.ch in [ord('e'), ord('E')]: if isinstance(self.container, Archive): aclass = self.container.__class__ archive = self.container.archive checked = self.checked container = self.container else: index = self.area.selected if index == -1: curses.flash() continue abspath = self.area.get_abspath(index) if not abspath: curses.flash() continue aclass = get_archive_class(abspath) if aclass is None: curses.flash() continue archive = aclass.open(abspath) checked = None container = None pathwin = PathWin(self) exitstatus, s = pathwin.show( "Extract to " "(press ENTER for confirmation or ESC to cancel):" ) pathwin.close() logging.info("window exitstatus: {0}, '{1}'".format( exitstatus, s )) if exitstatus != 0: continue workwin = WorkWin(self) workwin.show("Extracting ...") aclass.extract(container, archive, s, checked=checked) workwin.close() TextWin(self).show("Extracted to:\n{0}".format(s)) elif self.ch in [ord('?'), curses.KEY_F1, ord('h')]: textwin = TextWin(self) textwin.show(HELP_STRING) if self.ch != -1: self.refresh_scr() if self.kill: break def show_items_warning(self): questionwin = QuestionWin(self) return questionwin.show( "There are more than {0} items in this folder," "\ndo you really want to select it?".format( ITEMS_WARNING ) ) def cancel(self): self.kill = True
class TestFileSystem(unittest.TestCase): def setUp(self): self.fs = FileSystem() self.testcwd = os.getcwd() self.testfilepath = tarman.tests.test_containers.__file__ self.testdirectory = os.path.dirname(self.testfilepath) self.testdatadir = os.path.join(self.testdirectory, 'testdata') def test_container(self): self.assertTrue(isinstance(self.fs, Container)) def test_listdir(self): self.assertEqual( self.fs.listdir(self.testdirectory), os.listdir(self.testdirectory) ) def test_isenterable(self): self.assertTrue(self.fs.isenterable(self.testdirectory)) def test_abspath(self): self.assertEqual(self.fs.abspath('.'), self.testcwd) def test_dirname(self): self.assertEqual( self.fs.dirname(self.testfilepath), self.testdirectory ) def test_basename(self): self.assertEqual( self.fs.basename(self.testfilepath), os.path.basename(self.testfilepath) ) def test_join(self): self.assertEqual( self.fs.join('/home', 'someone', 'bin', 'python'), '/home/someone/bin/python' ) def test_split(self): self.assertEqual( self.fs.split('/home/someone/bin/python'), ('/home/someone/bin', 'python') ) def test_samefile(self): self.assertTrue( self.fs.samefile(self.testfilepath, self.testfilepath) ) self.assertFalse( self.fs.samefile( self.testfilepath, tarman.tests.test_tree.__file__ ) ) def test_count_items(self): self.assertEqual( self.fs.count_items(self.testdatadir), 17 ) self.assertEqual( self.fs.count_items(self.testdatadir, stop_at=9), 9 )
class Main(object): def __init__(self, mainscr, stdscr, directory): logging.basicConfig(filename='tarman.log', filemode='w', level=logging.DEBUG) self.header_lns = 1 self.mainscr = mainscr self.stdscr = stdscr self.color = curses.has_colors() if self.color: curses.init_pair(1, curses.COLOR_BLUE, -1) self.attr_folder = curses.color_pair(1) | curses.A_BOLD curses.init_pair(2, 7, -1) self.attr_norm = curses.color_pair(2) self.kill = False self.ch = -1 self.visited = {} # TODO self.area = None self.container = FileSystem() self.directory = self.container.abspath(directory) self.checked = DirectoryTree(self.directory, self.container) self.chdir(self.directory) def header(self, text, line=0): self.mainscr.clear() self.mainscr.addstr(line, 0, text) self.mainscr.refresh() def identify_container(self, path): if self.container.isenterable(path): # is folder return self.container # force one-level archive browsing if not isinstance(self.container, FileSystem): return None return container(path) def chdir(self, newpath): if newpath is None: return False if not newpath.startswith(self.directory): return False try: if self.area is None: oldsel = 0 oldpath = self.directory else: oldsel = self.area.selected oldpath = self.area.abspath oldcontainer = self.container oldchecked = self.checked if newpath in self.visited: newsel, newcontainer, newchecked = self.visited[newpath] else: newcontainer = self.identify_container(newpath) if newcontainer is None: return False newchecked = DirectoryTree(newpath, newcontainer) newsel = 0 self.visited[oldpath] = [oldsel, oldcontainer, oldchecked] logging.info("OLD - {0} - {1} - {2}".format(oldpath, oldsel, oldcontainer.__class__.__name__)) logging.info("NEW - {0} - {1} - {2}".format(newpath, newsel, newcontainer.__class__.__name__)) h, w = self.stdscr.getmaxyx() self.container = newcontainer self.checked = newchecked self.area = ViewArea(newpath, h, newcontainer) self.header("({0}) {1}".format( self.container.__class__.__name__, self.area.abspath )) self.area.set_params(h, offset=newsel) self.refresh_scr() return True except OutOfRange: curses.flash() def insert_line(self, y, item): i, name, abspath = item self.stdscr.addstr( y, 0, "[{0}]".format('*' if abspath in self.checked else ' ') ) if self.color: if self.container.isenterable(abspath): attr = self.attr_folder name = "{0}/".format(name) else: attr = self.attr_norm self.stdscr.addstr(y, 5, name, attr) else: if self.container.isenterable(abspath): name = "{0}/".format(name) self.stdscr.addstr(y, 5, name) def refresh_scr(self): self.stdscr.clear() if len(self.area) == 0: self.stdscr.addstr(1, 5, "Directory is empty!") return iitem = 0 for item in self.area: self.insert_line(iitem, item) iitem += 1 y = self.area.selected_local h, w = self.stdscr.getmaxyx() self.stdscr.chgat(y, 0, w, curses.A_REVERSE) self.stdscr.move(y, 1) def loop(self): while not self.kill: self.ch = self.stdscr.getch() h, w = self.stdscr.getmaxyx() if self.ch in [ord('q'), 27]: self.kill = True elif self.ch == curses.KEY_UP: self.area.set_params(h, offset=-1) elif self.ch == curses.KEY_DOWN: self.area.set_params(h, offset=1) elif self.ch == curses.KEY_PPAGE: self.area.set_params(h, offset=-5) elif self.ch == curses.KEY_NPAGE: self.area.set_params(h, offset=5) elif self.ch == 32: index = self.area.selected if index == -1: curses.flash() continue abspath = self.area.get_abspath(index) if abspath in self.checked: del self.checked[abspath] else: self.checked.add(abspath, sub=True) elif self.ch == curses.KEY_RIGHT: index = self.area.selected if index == -1: curses.flash() continue abspath = self.area.get_abspath(index) if not self.chdir(abspath): curses.flash() elif self.ch == curses.KEY_LEFT: if not self.chdir( self.container.dirname(self.area.abspath) ): curses.flash() elif self.ch in [ord('e'), ord('E')]: if isinstance(self.container, Archive): aclass = self.container.__class__ archive = self.container.archive checked = self.checked else: index = self.area.selected if index == -1: curses.flash() continue abspath = self.area.get_abspath(index) if not abspath: curses.flash() continue aclass = get_archive_class(abspath) archive = aclass.open(abspath) checked = None aclass.extract(archive, '.', checked=checked) if self.ch != -1: self.refresh_scr() if self.kill: break self.stdscr.clear() def cancel(self): self.kill = True
class Main(object): @utf8_args(3) def __init__(self, mainscr, stdscr, directory, encoding, show_hiddens): self.encoding = encoding self.header_lns = HEADER_LNS self.mainscr = mainscr self.stdscr = stdscr self.color = curses.has_colors() if self.color: # set file type attributes (color and bold) curses.init_pair(1, curses.COLOR_BLUE, -1) self.attr_folder = curses.color_pair(1) | curses.A_BOLD curses.init_pair(2, 7, -1) self.attr_norm = curses.color_pair(2) # set wright / wrong attributes (color and bold) curses.init_pair(3, curses.COLOR_GREEN, -1) self.attr_wright = curses.color_pair(3) | curses.A_BOLD curses.init_pair(4, curses.COLOR_RED, -1) self.attr_wrong = curses.color_pair(4) | curses.A_BOLD self.kill = False self.ch = -1 self.visited = {} self.area = None self.container = FileSystem() self.directory = self.container.abspath(directory) self.checked = DirectoryTree(self.directory, self.container) self.show_hiddens = show_hiddens self.chdir(self.directory) @utf8_args(1, 2) def header(self, prefix, path): #self.mainscr.clear() h, w = self.mainscr.getmaxyx() sep = " " length = len(prefix) + len(path) + len(sep) empty = 0 if length > w: path = "..." + path[length - w + 3:] else: empty = w - length self.mainscr.addstr( 0, 0, u"{0}{1}{2}{3}".format( prefix, sep, path, empty * ' ' ).encode(self.encoding) ) self.mainscr.refresh() def identify_container_and_checked(self, path): if self.container.isenterable(path): # is folder return self.container, self.checked # force one-level archive browsing if not isinstance(self.container, FileSystem): return None, None aclass = get_archive_class(path) if not aclass: return None, None workwin = WorkWin(self) workwin.show("Working ...") newcontainer = aclass(path) newchecked = DirectoryTree(path, newcontainer) workwin.close() return newcontainer, newchecked def chdir(self, newpath): if newpath is None: return False if not newpath.startswith(self.directory): return False try: if self.area is None: oldsel = 0 oldpath = self.directory else: oldsel = self.area.selected oldpath = self.area.abspath oldcontainer = self.container oldchecked = self.checked if newpath in self.visited: newsel, newcontainer, newchecked = self.visited[newpath] else: newcontainer, newchecked = \ self.identify_container_and_checked(newpath) if newcontainer is None: return False newsel = 0 self.visited[oldpath] = [oldsel, oldcontainer, oldchecked] logging.info(u"OLD - {0} - {1} - {2}".format( oldpath, oldsel, oldcontainer.__class__.__name__ )) logging.info(u"NEW - {0} - {1} - {2}".format( newpath, newsel, newcontainer.__class__.__name__ )) h, w = self.stdscr.getmaxyx() self.container = newcontainer self.checked = newchecked def show_unreadable_error(path, name): if isinstance(name, unicode): error_str = name.encode('utf8', errors='replace') else: error_str = name logging.info("Unreadable file name: {0} (in '{1}')".format( error_str, path )) errorwin = TextWin(self) errorwin.show("Unreadable file name:\n{0}\n\nPress ESC to close.".format(error_str)) self.area = ViewArea( newpath, h, newcontainer, show_unreadable_error, self.show_hiddens ) self.header( "{0}".format( self.container.__class__.__name__ ), self.area.abspath ) self.area.set_params(h, offset=newsel) self.refresh_scr() return True except OutOfRange: logging.error("OutOfRange .. {0}".format(newpath)) curses.flash() def insert_line(self, y, item): i, name, abspath = item self.stdscr.addstr( y, 0, "[{0}]".format( '*' if abspath in self.checked else ' ' ).encode(self.encoding) ) if self.color: if self.container.isenterable(abspath): attr = self.attr_folder name = u"{0}/".format(name) else: attr = self.attr_norm self.stdscr.addstr(y, 5, name.encode(self.encoding), attr) else: if self.container.isenterable(abspath): name = u"{0}/".format(name) self.stdscr.addstr(y, 5, name.encode(self.encoding)) def refresh_scr(self): self.stdscr.clear() if not getattr(self, 'area', None): return if len(self.area) == 0: self.stdscr.addstr(1, 5, "Directory is empty!") return iitem = 0 for item in self.area: self.insert_line(iitem, item) iitem += 1 y = self.area.selected_local h, w = self.stdscr.getmaxyx() self.stdscr.chgat(y, 0, w, curses.A_REVERSE) self.stdscr.move(y, 1) def loop(self): while not self.kill: self.ch = self.stdscr.getch() h, w = self.stdscr.getmaxyx() if self.ch in [ord('q'), ]: self.kill = True elif self.ch == curses.KEY_UP: self.area.set_params(h, offset=-1) elif self.ch == curses.KEY_DOWN: self.area.set_params(h, offset=1) elif self.ch == curses.KEY_PPAGE: self.area.set_params(h, offset=-5) elif self.ch == curses.KEY_NPAGE: self.area.set_params(h, offset=5) elif self.ch == 32: index = self.area.selected if index == -1: curses.flash() continue abspath = self.area.get_abspath(index) if abspath in self.checked: del self.checked[abspath] else: countitems = self.container.count_items( abspath, stop_at=ITEMS_WARNING ) if countitems >= ITEMS_WARNING and \ self.show_items_warning() != 0: continue self.checked.add(abspath, sub=True) elif self.ch in [curses.KEY_RIGHT, 10, 13]: index = self.area.selected if index == -1: curses.flash() continue abspath = self.area.get_abspath(index) result = self.chdir(abspath) if not result: curses.flash() elif self.ch in [curses.KEY_LEFT, 127, curses.ascii.BS, curses.KEY_BACKSPACE]: if not self.chdir( self.container.dirname(self.area.abspath) ): curses.flash() elif self.ch in [ord('c'), ord('C')]: if isinstance(self.container, FileSystem): aclass = self.container.__class__ checked = self.checked container = self.container pathwin = PathWin(self) exitstatus, archivepath = pathwin.show( "Create archive with format/compression based on file" " extension (ENTER to confirm or ESC to cancel):", os.path.join(os.getcwd(), "NewArchive.tar.gz") ) pathwin.close() logging.info("window exitstatus: {0}, '{1}'".format( exitstatus, archivepath )) if exitstatus != 0: continue archivepath = os.path.abspath(archivepath) aclass = get_archive_class(archivepath) if aclass is None: curses.flash() continue created = aclass.create(container, archivepath, checked) if created: TextWin(self).show( "Successfully created archive:\n{0}".format( archivepath ) ) else: curses.flash() elif self.ch in [ord('e'), ord('E')]: if isinstance(self.container, Archive): aclass = self.container.__class__ archive = self.container.archive checked = self.checked container = self.container else: index = self.area.selected if index == -1: curses.flash() continue abspath = self.area.get_abspath(index) if not abspath: curses.flash() continue aclass = get_archive_class(abspath) if aclass is None: curses.flash() continue archive = aclass.open(abspath) checked = None container = None pathwin = PathWin(self) exitstatus, s = pathwin.show( "Extract to " "(press ENTER for confirmation or ESC to cancel):" ) pathwin.close() logging.info("window exitstatus: {0}, '{1}'".format( exitstatus, s )) if exitstatus != 0: continue workwin = WorkWin(self) workwin.show("Extracting ...") aclass.extract(container, archive, s, checked=checked) workwin.close() TextWin(self).show("Extracted to:\n{0}".format(s)) elif self.ch in [ord('?'), curses.KEY_F1]: curses.curs_set(0) textwin = TextWin(self) textwin.show(HELP_STRING) if self.ch == ord('h'): if self.show_hiddens == True: self.show_hiddens = False else: self.show_hiddens = True self.chdir(self.area.abspath) if self.ch != -1: self.refresh_scr() if self.kill: break def show_items_warning(self): questionwin = QuestionWin(self) return questionwin.show( "There are more than {0} items in this folder," "\ndo you really want to select it?".format( ITEMS_WARNING ) ) def cancel(self): self.kill = True