I'm learing Python by writing a program to solve some housekeeping I do weekly, namely renaming PDF files according to rules given in a text file.My end goal is to have idiomatic Python, with full test coverage. I have done some refactoring, and written a couple of tests. Ideally I would have done TDD from the beginning, but I find it challenging to write tests since I don't know Python that well yet.The PDF documents can be named with two patterns:xx.pdfxx-y.pdfThey are stored in the folder ./test relative to the script root.The text file Instruments.txt contains the following pattern:01 SopranoCn02 SoloCn03 RepianoCn...18 Percussion20 AllParts...The whole purpose is to rename files like this:xx.pdf   (01.pdf)   --> xx_{instrument_name}.pdf (01_SopranoCn.pdf)xx-y.pdf (02-1.pdf) --> xx_{instrument_name}{part_number}.pdf (02_SoloCn1.pdf)The y-argument is optional, and can be added to any instrument.Here's the whole source:Invoke with python notesort.py <path to folder>.A list of instrument numbers with corresponding instrument namesmust be available in the root folder of the script.# Standard library importsimport osFOLDER = './test'def create_instrument_map():    Create a dictionary mapping each prefix    to the corresponding instrument name.    with open('Instruments.txt') as instrument_file:        return {            line.split()[0]: line.split()[1] for line in instrument_file        }def rename_files_with_instrument_name(instrument_map):    Rename PDF files in folder based on the root of    the original file name.    for filename in os.listdir(FOLDER):        root, extension = os.path.splitext(filename)        if not extension.endswith('.pdf'):            continue        try:            if root[2]:                if root[:2] in instrument_map:                    new_file_name = '{prefix}_'\                                    '{instrument_name}'\                                    '{part_number}'\                                    '{extension}'.format(                                        prefix=root[:2],                                        instrument_name=instrument_map[root[:2]],                                        part_number=root[3],                                        extension=extension)        except:            if root in instrument_map:                new_file_name = '{prefix}_{instrument_name}{extension}'.format(                                    prefix=root,                                    instrument_name=instrument_map[root],                                    extension=extension)        old_path = os.path.join(FOLDER, filename)        new_path = os.path.join(FOLDER, new_file_name)        os.rename(old_path, new_path)        print 'Renamed', filename, '-->', new_file_nameif __name__ == '__main__':    instrument_map = create_instrument_map()    rename_files_with_instrument_name(instrument_map)I have started to write some tests for it, but I find it difficult to test rename_files_with_instrument_name. I do not have any experience writing tests, but I see the benefit and want to practise writing them.These are the tests I have so far:import unittestfrom notesort import create_instrument_mapclass TestNoteSort(unittest.TestCase):    def test_create_instrument_map_creates_soprano_cornet(self):        instrument_map = create_instrument_map()        self.assertEqual(instrument_map['01'], 'SopranoCn')    def test_create_instrument_map_soprano_not_solo(self):        instrument_map = create_instrument_map()        self.assertNotEqual(instrument_map['01'], 'SoloCn')if __name__ == '__main__':    unittest.main()Please feel free to make comments on any part of my implementation. No need to be easy on me, even if I'm a beginner. I am especially interested in how I can get further with testing, and also how to refactor rename_files_with_instrument_name.
Renaming PDF files based on given rules in a text file
python;beginner;unit testing;file system
Avoid hard-coding the names of files and directories.  Use parameters instead (with default values, if you prefer).The rename_files_with_instrument_name() function seems complicated and fragile.  It's complicated because it tries to do two things: figure out what the new filename should be, and perform the renaming.  Therefore, it would be better to split them into two functions.  It's fragile in that you are blindly slicing strings at certain indexes, checking only that the file extension is .pdf.  If you ever define prefixes that are not two characters long, or if you run the same script twice on a directory, the results could be nasty and somewhat difficult to undo.  More careful validation of the filename would be good.  Alternatively, use a regular expression substitution so that you only rename files that fit the pattern.import osimport redef instrument_map(map_filename='Instruments.txt'):    with open(map_filename) as f:        return dict(line.rstrip().split(' ', 1) for line in f)def filename_for(instrument_map, filename):    return re.sub(r'^(?P<prefix>[^.-]+)(?:-(?P<part>.*))?\.pdf$',        lambda match: '{prefix}_{instrument}{part}.pdf'.format(            prefix=match.group('prefix'),            instrument=instrument_map[match.group('prefix')],            part=match.group('part') or '',        ) if match.group('prefix') in instrument_map else match.string,        filename    )def rename_files(filename_map, folder='./test'):    for old_filename in os.listdir(folder):        new_filename = filename_for(filename_map, old_filename)        if new_filename != old_filename:            os.rename(os.path.join(folder, old_filename),                      os.path.join(folder, new_filename))            print 'Renamed {0} --> {1}'.format(old_filename, new_filename)if __name__ == '__main__':    rename_files(instrument_map())