def test_sync_removes_unmatched(self): """ Test `sync` is able to copy files from src to dst and removes unmatched files from dst. """ with tempfile.TemporaryDirectory() as src_tmp_dir_path: with tempfile.TemporaryDirectory() as dst_tmp_dir_path: with open(os.path.join(src_tmp_dir_path, self.COPY_FILE), "w"): pass dst_dir_path = os.path.join(dst_tmp_dir_path, self.DST_DIR) os.makedirs(dst_dir_path) with open(os.path.join(dst_dir_path, self.DST_FILE), "w"): pass sync = Sync( FAKE_MTP_DETAILS, src_tmp_dir_path, os.path.join(dst_tmp_dir_path, self.DST_DIR), unmatched=REMOVE, ) sync.set_source_abs() sync.set_destination_abs() sync.sync() self.assertTrue( os.path.exists( os.path.join(dst_tmp_dir_path, self.DST_DIR, self.COPY_FILE))) self.assertFalse( os.path.exists(os.path.join(dst_dir_path, self.DST_FILE)))
def test_handle_destination_dir_data_sync(self, mock_copy_file): """ Test 'handle_destination_dir_data' synchronizes unmatched destination data to source. """ sync = Sync(FAKE_MTP_DETAILS, "/tmp", "Card/Music", unmatched=SYNCHRONIZE) sync.set_source_abs() sync.set_destination_abs() sync.handle_destination_dir_data({ "src_dir_abs": "/tmp/testdir", "dst_dir_fls": [ "/run/user/<user>/gvfs/mtp:host=%5Busb%3A002%2C003%5D/Card/Music/testdir/song.mp3" # noqa ], "dst_dir_abs": "/run/user/<user>/gvfs/mtp:host=%5Busb%3A002%2C003%5D/Card/Music/testdir", # noqa }) mock_copy_file.assert_called_once_with( dst_file="/tmp/testdir/song.mp3", src_file= "/run/user/<user>/gvfs/mtp:host=%5Busb%3A002%2C003%5D/Card/Music/testdir/song.mp3", # noqa )
def test_get_destination_subdir_data_(self, mock_handle_ignored_file_type, mock_listdir, mock_path_exists): """ Test 'get_destination_subdir_data' populates 'dst_dir_fls' with collected data. """ mock_path_exists.return_value = True mock_listdir.return_value = ["song.mp3", "cover.jpg", "demo.mp3"] mock_handle_ignored_file_type.side_effect = [ None, IgnoredTypeException, None, ] sync = Sync(FAKE_MTP_DETAILS, "/tmp", "Card/Music") sync.set_source_abs() sync.set_destination_abs() sync_data = self._create_empty_sync_data(sync) sync.get_destination_subdir_data(sync_data) self.assertEqual( sync_data["dst_dir_fls"], [ "/run/user/<user>/gvfs/mtp:host=%5Busb%3A002%2C003%5D/Card/Music/testdir/song.mp3", # noqa "/run/user/<user>/gvfs/mtp:host=%5Busb%3A002%2C003%5D/Card/Music/testdir/demo.mp3", # noqa ], )
def test_source_exists_on_device(mtp): """ Test if Sync is able to initialize; i.e. source exists and is a directory """ sync = Sync(mtp, DEVICE_SOURCE, '') sync.set_source_abs() assert sync.source_abs == os.path.join(mtp[1], DEVICE_SOURCE)
def test_source_exists_on_computer(): """ Test if Sync is able to initialize; i.e. source exists and is a directory """ sync = Sync(('', ''), COMPUTER_SOURCE, '') sync.set_source_abs() assert sync.source_abs == COMPUTER_SOURCE
def test_source_expand(): """ Test if Sync is able to initialize even if given an expandable path """ sync = Sync(('', ''), '~/Music', '') sync.set_source_abs() assert sync.source_abs == os.path.join(COMPUTER_HOME, 'Music')
def test_source_not_exists_on_device(mtp): """ Test if Sync is not able to initialize; i.e. source doesn't exists """ with pytest.raises(OSError) as exc: sync = Sync(mtp, DEVICE_SOURCE_FAKE, '') sync.set_source_abs() assert NOT_EXISTS in str(exc.value)
def test_source_is_a_file_on_computer(): """ Test if Sync is not able to initialize; i.e. source is a not a directory """ with pytest.raises(OSError) as exc: sync = Sync(('', ''), COMPUTER_SOURCE_FILE, '') sync.set_source_abs() assert NOT_DIRECTORY in str(exc.value)
def test_destination_should_be_device(): """ Test if Sync sets device as destination if computer is the source """ sync = Sync(DEVICE_MTP_FAKE, COMPUTER_SOURCE, '') sync.set_source_abs() sync.set_destination_abs() assert sync.destination_abs == os.path.join(DEVICE_MTP_FAKE[1], '')
def test_destination_should_be_computer(mtp): """ Test if Sync sets computer as destination if device is the source """ sync = Sync(mtp, DEVICE_SOURCE, '') sync.set_source_abs() sync.set_destination_abs() assert sync.destination_abs == os.path.join(CURRENT_DIRECTORY, '')
def test_source_not_exists_on_computer_relative(): """ Test if Sync is not able to initialize; i.e. source doesn't exists as it is specified as a relative path which is wrong in this context """ with pytest.raises(OSError) as exc: sync = Sync(('', ''), 'Music/', '') sync.set_source_abs() assert NOT_EXISTS in str(exc.value)
def test_source_exists_on_computer_relative(cd_home, cd_back): """ Test if Sync is able to initialize; i.e. source exists and is a directory even if is specified as a relative path which is OK in this context """ music = 'Music' sync = Sync(('', ''), music, '') sync.set_source_abs() assert sync.source_abs == os.path.join(COMPUTER_HOME, music)
def test_source_exists_on_computer_relative3(cd_home, cd_back): """ Test if Sync is able to initialize; i.e. source exists and is a directory even if is specified as a relative path """ parent = os.sep sync = Sync(('', ''), parent, '') sync.set_source_abs() assert sync.source_abs == os.sep
def test_prepare_paths_ignore_files(tmpdir, tmpfiles, file_type): """ Test if Sync.prepare_paths() ignores given file types """ sync = Sync(DEVICE_MTP_FAKE, tmpdir, DEVICE_DESTINATION, ignore_file_types=[file_type]) sync.set_source_abs() sync.set_destination_abs() for to_sync in sync.prepare_paths(): assert not to_sync['abs_fls_map']
def test_handle_destination_dir_data_no_files(self, mock_gfvs_wrapper): """ Test 'handle_destination_dir_data' ends early when there are no data to process. """ sync = Sync(FAKE_MTP_DETAILS, "/tmp", "Card/Music", unmatched=REMOVE) sync.set_source_abs() sync.set_destination_abs() sync.handle_destination_dir_data({"dst_dir_fls": []}) mock_gfvs_wrapper.assert_not_called()
def test_set_source_abs_absolute_path(self, mock_path_exists, mock_path_isdir): """ Test 'set_source_abs' recongizes an absolute path. """ mock_path_exists.return_value = True mock_path_isdir.return_value = True sync = Sync(FAKE_MTP_DETAILS, "/an-absolute-path", "") sync.set_source_abs() self.assertEqual(sync.source, "/an-absolute-path")
def test_sync_to_device_unmatched(mtp, tmpdir, tmpfiles, tmpdir_device_remove, unmatched_action): """ :unmatched_action == SYNCHRONIZE Test if Sync.sync() really sync files from computer to device and sync back to computer file(s) that are only on the device. :unmatched_action == REMOVE Test if Sync.sync() really sync files from computer to device and remove files that are only on the device. """ tmpfiles_names = set([os.path.basename(tmpf) for tmpf in tmpfiles]) # # create parent directory and copy a new file to the device # destination directory; this copied file will be the unmatched file, i.e. # a file that is present only in the destination directory unmatched = 'test.test' dst_pth = os.path.join(mtp[1], DEVICE_DESTINATION_TEST_DIR) gvfs.mkdir(dst_pth) dst_file = os.path.join(dst_pth, unmatched) gvfs.cp(src=COMPUTER_SOURCE_FILE, dst=dst_file) sync = Sync(mtp, tmpdir, DEVICE_DESTINATION_TEST_DIR, unmatched=unmatched_action) # NOQA sync.set_source_abs() sync.set_destination_abs() sync.sync() # # exclude the unmatched file from synchronized files as it was already in # the destination directory synced_files = [syncf for syncf in os.listdir(sync.destination_abs) if syncf != unmatched] assert synced_files for synced_file in synced_files: synced_file = os.path.basename(synced_file) assert synced_file in tmpfiles_names # # test if unmatched_action works as expected if unmatched_action == SYNCHRONIZE: # unmatched file should be synchronized to the source directory assert unmatched in os.listdir(sync.source_abs) elif unmatched_action == REMOVE: # unmatched file should be removed from the destination directory assert unmatched not in os.listdir(sync.destination_abs)
def test_destination_should_be_computer_relative(mtp): """ Test if Sync sets computer as destination if device is the source and the destination is a relative path """ parent = '../..' sync = Sync(mtp, DEVICE_SOURCE, parent) sync.set_source_abs() sync.set_destination_abs() assert sync.destination_abs == COMPUTER_HOME
def test_sync_no_data(self, mock_get_sync_data, mock_do_sync): """ Test 'sync' ends early if there are no files to synchronize. """ mock_get_sync_data.return_value = [{"src_dir_fls": []}] sync = Sync(FAKE_MTP_DETAILS, "/tmp", "Card/Music") sync.set_source_abs() sync.set_destination_abs() sync.sync() mock_do_sync.assert_not_called()
def test_get_sync_data( self, mock_get_destination_subdir_data, mock_set_destination_subdir_abs, mock_oswalk, ): """ Test 'get_sync_data' gets list of valid sync_data dictionaries. """ mock_oswalk.return_value = ( ("/tmp/testdir", ["testsubdir"], ["song.mp3", "demo.mp3"]), ("/tmp/testdir/testsubdir", ["testsubdir2"], []), ("/tmp/testdir/testsubdir/testsubdir2", [], ["song2.mp3"]), ) mock_set_destination_subdir_abs.side_effect = ( "/run/user/<user>/gvfs/mtp:host=%5Busb%3A002%2C003%5D/Card/Music/testdir", # noqa "/run/user/<user>/gvfs/mtp:host=%5Busb%3A002%2C003%5D/Card/Music/testdir/testsubdir2", # noqa ) sync = Sync(FAKE_MTP_DETAILS, "/tmp", "Card/Music") sync.set_source_abs() sync.set_destination_abs() sync_data_set = sync.get_sync_data() self.assertTrue(mock_set_destination_subdir_abs.call_count, 2) self.assertTrue(mock_get_destination_subdir_data.call_count, 2) expected_sync_data_set = [ { "src_dir_abs": "/tmp/testdir", "src_dir_fls": [ "/tmp/testdir/song.mp3", "/tmp/testdir/demo.mp3", ], "dst_dir_fls": [], "dst_dir_abs": "/run/user/<user>/gvfs/mtp:host=%5Busb%3A002%2C003%5D/Card/Music/testdir", # noqa }, { "src_dir_abs": "/tmp/testdir/testsubdir/testsubdir2", "src_dir_fls": ["/tmp/testdir/testsubdir/testsubdir2/song2.mp3"], "dst_dir_fls": [], "dst_dir_abs": "/run/user/<user>/gvfs/mtp:host=%5Busb%3A002%2C003%5D/Card/Music/testdir/testsubdir2", # noqa }, ] self.assertIsInstance(sync_data_set, list) self.assertEqual(sync_data_set, expected_sync_data_set)
def test_copy_file(self, mock_gfvs_wrapper): """ Test 'copy_file' copies a file from source to destination. """ src_file = "/tmp/song.mp3" dst_file = "Card/Musicsong.mp3" sync = Sync(FAKE_MTP_DETAILS, "/tmp", "Card/Music") sync.set_source_abs() sync.set_destination_abs() sync.copy_file(src_file, dst_file) mock_gfvs_wrapper.assert_called_once_with(cp, src_file, dst_file)
def test_set_source_abs_computer_relative_path(self, mock_path_exists, mock_path_isdir): """ Test 'set_source_abs' creates an absolute path from a relative path when the path exists on computer. """ mock_path_exists.return_value = True mock_path_isdir.return_value = True sync = Sync(FAKE_MTP_DETAILS, "a-relative-path/", "") sync.set_source_abs() expected_abs_path = os.path.join(os.getcwd(), "a-relative-path") self.assertEqual(sync.source, expected_abs_path)
def test_set_source_abs_device_relative_path(self, mock_path_exists, mock_path_isdir): """ Test 'set_source_abs' creates an absolute path from a relative path when the path exists on device. """ mock_path_exists.side_effect = [False, True, True] mock_path_isdir.return_value = True sync = Sync(FAKE_MTP_DETAILS, "Card/Music", "") sync.set_source_abs() expected_abs_path = os.path.join(sync.mtp_gvfs_path, "Card/Music") self.assertEqual(sync.source, expected_abs_path)
def test_set_destination_subdir_absh(self): """ Test 'set_destination_subdir_abs' creates an absolute path for a destination subdir. """ sync = Sync(FAKE_MTP_DETAILS, "~/Music", "Card/Music") sync.set_source_abs() sync.set_destination_abs() src_subdir_abs = os.path.join(sync.source, "testdir") dst_subdir_abs = sync.set_destination_subdir_abs(src_subdir_abs) expected_abs_path = os.path.join(sync.destination, "testdir") self.assertEqual(dst_subdir_abs, expected_abs_path)
def test_handle_ignored_file_type(self): """ Test 'handle_ignored_file_type' raises IgnoredTypeException exception only for files with specified extensions. """ sync = Sync(FAKE_MTP_DETAILS, "", "", ignore_file_types=["jpg"]) sync.set_source_abs() sync.set_destination_abs() # this one is fine sync.handle_ignored_file_type("/tmp/test.png") # this one should be ignored with self.assertRaises(IgnoredTypeException): sync.handle_ignored_file_type("/tmp/test.jpg")
def test_sync_data_template(self): """ Test 'sync_data_template' creates a sync data dict with expected keys. """ sync = Sync(FAKE_MTP_DETAILS, "~/Music", "Card/Music") sync.set_source_abs() sync.set_destination_abs() src_subdir_abs = os.path.join(sync.source, "testdir") dst_subdir_abs = os.path.join(sync.destination, "testdir") sync_data = sync.sync_data_template(src_subdir_abs, dst_subdir_abs) self.assertIn("src_dir_abs", sync_data) self.assertIn("src_dir_fls", sync_data) self.assertIn("dst_dir_abs", sync_data) self.assertIn("dst_dir_fls", sync_data)
def test_set_source_abs_not_directory(self, mock_path_exists, mock_path_isdir): """ Test 'set_source_abs' raises an OSError when source exists but is not a directory. """ mock_path_exists.return_value = True mock_path_isdir.return_value = False with self.assertRaises(OSError) as exc: sync = Sync(FAKE_MTP_DETAILS, "not-a-directory", "") sync.set_source_abs() expected_abs_path = os.path.join(os.getcwd(), "not-a-directory") err_msg = '"{}" is not a directory.'.format(expected_abs_path) self.assertEqual(str(exc.exception), err_msg)
def test_handle_destination_dir_data_remove(self, mock_gfvs_wrapper): """ Test 'handle_destination_dir_data' removes unmatched destination data. """ sync = Sync(FAKE_MTP_DETAILS, "/tmp", "Card/Music", unmatched=REMOVE) sync.set_source_abs() sync.set_destination_abs() sync.handle_destination_dir_data({ "dst_dir_fls": [ "/run/user/<user>/gvfs/mtp:host=%5Busb%3A002%2C003%5D/Card/Music/testdir/song.mp3" # noqa ] }) mock_gfvs_wrapper.assert_called_once_with( rm, "/run/user/<user>/gvfs/mtp:host=%5Busb%3A002%2C003%5D/Card/Music/testdir/song.mp3", # noqa )
def test_sync_ignore_unmatched( self, mock_handle_destination_dir_data, mock_get_sync_data, mock_do_sync, ): """ Test 'sync' ignores unmatched files. """ mock_get_sync_data.return_value = [FAKE_SYNC_DATA] sync = Sync(FAKE_MTP_DETAILS, "/tmp", "Card/Music") sync.set_source_abs() sync.set_destination_abs() sync.sync() mock_do_sync.assert_called_once_with(FAKE_SYNC_DATA) mock_handle_destination_dir_data.assert_not_called()
def test_sync_to_device(mtp, tmpdir, tmpfiles, tmpdir_device_remove): """ Test if Sync.sync() really sync files from computer to device """ tmpfiles_names = set([os.path.basename(tmpf) for tmpf in tmpfiles]) sync = Sync(mtp, tmpdir, DEVICE_DESTINATION_TEST_DIR) sync.set_source_abs() sync.set_destination_abs() sync.sync() synced_files = os.listdir(sync.destination_abs) assert synced_files for synced_file in synced_files: synced_file = os.path.basename(synced_file) assert synced_file in tmpfiles_names
def test_set_source_abs_nonexistent(self, mock_path_exists): """ Test 'set_source_abs' raises an OSError when source doesn't exist on the computer or on the device. """ mock_path_exists.return_value = False with self.assertRaises(OSError) as exc: sync = Sync(FAKE_MTP_DETAILS, "non-exiting-path", "") sync.set_source_abs() # Must be called twice - for computer and device. self.assertEqual(mock_path_exists.call_count, 2) self.assertEqual( str(exc.exception), '"non-exiting-path" does not exist on computer or on device.', )
def test_sync_handle_unmatched( self, mock_handle_destination_dir_data, mock_get_sync_data, mock_do_sync, ): """ Test 'sync' handles (removes, in this case) unmatched files. """ mock_get_sync_data.return_value = [FAKE_SYNC_DATA] sync = Sync(FAKE_MTP_DETAILS, "/tmp", "Card/Music", unmatched=REMOVE) sync.set_source_abs() sync.set_destination_abs() sync.sync() mock_do_sync.assert_called_once_with(FAKE_SYNC_DATA) mock_handle_destination_dir_data.assert_called_once_with( FAKE_SYNC_DATA)
def test_get_destination_subdir_data_doesnt_exist(self, mock_gvfs_wrapper, mock_path_exists): """ Test 'get_destination_subdir_data' creates destination direcotry if it doesn't exist. """ mock_path_exists.side_effect = (True, False) sync = Sync(FAKE_MTP_DETAILS, "/tmp", "Card/Music") sync.set_source_abs() sync.set_destination_abs() sync_data = self._create_empty_sync_data(sync) sync.get_destination_subdir_data(sync_data) mock_gvfs_wrapper.assert_called_once_with( mkdir, "/run/user/<user>/gvfs/mtp:host=%5Busb%3A002%2C003%5D/Card/Music/testdir", # noqa ) self.assertFalse(sync_data["dst_dir_fls"])
def main(): args = parser.parse_args() usb_bus, device = find_device.get_connection_details(vendor=args.vendor, model=args.model) mtp_details = find_device.get_mtp_details(usb_bus, device) sync = Sync( mtp_details=mtp_details, source=args.source, destination=args.destination, unmatched=args.unmatched, overwrite_existing=args.overwrite, verbose=args.verbose, ignore_file_types=args.ignore_file_type, ) sync.set_source_abs() sync.set_destination_abs() sync.sync()
def test_prepare_paths(tmpdir, tmpfiles): """ Test if Sync.prepare_paths() returns an expected list of paths """ sync = Sync(DEVICE_MTP_FAKE, tmpdir, DEVICE_DESTINATION) sync.set_source_abs() sync.set_destination_abs() for to_sync in sync.prepare_paths(): for key in ('abs_src_dir', 'abs_dst_dir', 'abs_fls_map'): assert key in to_sync for src, dst in to_sync['abs_fls_map']: basename = os.path.basename(src) assert src.endswith(basename) assert dst.endswith(basename) assert tmpdir in src assert DEVICE_MTP_FAKE[1] in dst assert DEVICE_DESTINATION in dst
def test_do_sync(self, mock_copy_file): """ Test 'do_sync' copies source files to their destination and updates destination files list. """ sync = Sync(FAKE_MTP_DETAILS, "/tmp", "Card/Music") sync.set_source_abs() sync.set_destination_abs() sync.do_sync(FAKE_SYNC_DATA) self.assertEqual( FAKE_SYNC_DATA["dst_dir_fls"], [ "/run/user/<user>/gvfs/mtp:host=%5Busb%3A002%2C003%5D/Card/Music/testdir/oldsong.mp3" ], # noqa ) mock_copy_file.assert_called_once_with( "/tmp/testdir/song.mp3", "/run/user/<user>/gvfs/mtp:host=%5Busb%3A002%2C003%5D/Card/Music/testdir/song.mp3", # noqa )
def test_get_source_subdir_data(self, mock_handle_ignored_file_type): """ Test 'get_source_subdir_data' populates 'src_dir_fls' with collected data. """ mock_handle_ignored_file_type.side_effect = [ None, IgnoredTypeException, None, ] src_subdir_files = ["song.mp3", "cover.jpg", "demo.mp3"] sync = Sync(FAKE_MTP_DETAILS, "/tmp", "Card/Music") sync.set_source_abs() sync.set_destination_abs() sync_data = self._create_empty_sync_data(sync) sync.get_source_subdir_data(src_subdir_files, sync_data) self.assertEqual( sync_data["src_dir_fls"], ["/tmp/testdir/song.mp3", "/tmp/testdir/demo.mp3"], )
def test_sync_to_device_overwrite(mtp, tmpdir, tmpfiles, tmpdir_device_remove, overwrite): """ :overwrite == True Test if Sync.sync() overwrites existing files :overwrite == False Test if Sync.sync() does not overwrite existing files """ def _get_modification_times(): sync_dict = {} for synced_file in os.listdir(sync.destination_abs): abs_pth = os.path.join(sync.destination_abs, synced_file) sync_dict[synced_file] = time.ctime(os.path.getmtime(abs_pth)) return sync_dict sync = Sync(mtp, tmpdir, DEVICE_DESTINATION_TEST_DIR, overwrite_existing=overwrite) # NOQA sync.set_source_abs() sync.set_destination_abs() sync.sync() first_sync = _get_modification_times() time.sleep(2) sync.sync() second_sync = _get_modification_times() for synced_file in first_sync: if overwrite: assert first_sync[synced_file] < second_sync[synced_file] else: assert first_sync[synced_file] == second_sync[synced_file]
def test_sync_to_computer(mtp, tmpdir, tmpfiles, tmpdir_device_remove): """ Test if Sync.sync() really sync files from device to computer """ tmpfiles_names = set([os.path.basename(tmpf) for tmpf in tmpfiles]) # # first move tmpfiles to the device device_source = os.path.join(mtp[1], DEVICE_DESTINATION_TEST_DIR) if not os.path.exists(device_source): gvfs.mkdir(device_source) for tmpfile in tmpfiles: gvfs.mv(tmpfile, os.path.join(mtp[1], device_source)) moved_files = os.listdir(device_source) assert moved_files for moved_file in moved_files: moved_file = os.path.basename(moved_file) assert moved_file in tmpfiles_names # # then sync them back to computer sync = Sync(mtp, device_source, tmpdir) sync.set_source_abs() sync.set_destination_abs() sync.sync() synced_files = os.listdir(sync.destination_abs) assert synced_files for synced_file in synced_files: synced_file = os.path.basename(synced_file) assert synced_file in tmpfiles_names
def test_sync_default(self): """ Test `sync` is able to copy files from src to dst and ignores unmatched files from dst. """ with tempfile.TemporaryDirectory() as src_tmp_dir_path: with tempfile.TemporaryDirectory() as dst_tmp_dir_path: with open(os.path.join(src_tmp_dir_path, self.COPY_FILE), "w"): pass sync = Sync( FAKE_MTP_DETAILS, src_tmp_dir_path, os.path.join(dst_tmp_dir_path, self.DST_DIR), ) sync.set_source_abs() sync.set_destination_abs() sync.sync() self.assertTrue( os.path.exists( os.path.join(dst_tmp_dir_path, self.DST_DIR, self.COPY_FILE)))
def test_do_sync_overwrite(self, mock_copy_file): """ Test 'do_sync' is able to overwrite existing destination files. """ sync = Sync(FAKE_MTP_DETAILS, "/tmp", "Card/Music", overwrite_existing=True) sync.set_source_abs() sync.set_destination_abs() sync.do_sync(FAKE_SYNC_DATA) calls = ( call( "/tmp/testdir/song.mp3", "/run/user/<user>/gvfs/mtp:host=%5Busb%3A002%2C003%5D/Card/Music/testdir/song.mp3", # noqa ), call( "/tmp/testdir/demo.mp3", "/run/user/<user>/gvfs/mtp:host=%5Busb%3A002%2C003%5D/Card/Music/testdir/demo.mp3", # noqa ), ) mock_copy_file.assert_has_calls(calls)
def run(args): """ Run pysyncdroid. :argument args: command line arguments namespace :type args: object """ try: usb_bus_id, device_id = get_connection_details(args.vendor, args.model) mtp_details = get_mtp_details(usb_bus_id, device_id) except DeviceException as exc: return str(exc) try: sources, destinations = parse_sync_info(args) except (argparse.ArgumentError, MappingFileException) as exc: return str(exc) for source, destination in zip(sources, destinations): source = source.strip() destination = destination.strip() sync = Sync( mtp_details=mtp_details, source=source, destination=destination, verbose=args.verbose, unmatched=args.unmatched, overwrite_existing=args.overwrite, ignore_file_types=args.ignore_file_type, ) sync.set_source_abs() sync.set_destination_abs() sync.sync()