8
\$\begingroup\$

For web-uploading of Cyrillic-named images, I've written the script below:

#!/usr/bin/python
# coding=utf-8

import os
import argparse
import shutil

class Error: pass
class FolderError(Error): pass

def copytranstree (src, dst):
   names = os.listdir( src )

   try:
       os.makedirs( dst )
   except WindowsError:
       pass

   errors = []

   for name in names:

       srcname = os.path.join( src, name )

       dstname = os.path.join( dst, ''.join([translit.get(x, x) for x in name.decode( input_parametres.encoding ).lower()]).encode( input_parametres.encoding ) )

       try:
           if os.path.isdir( srcname ):
               copytranstree( srcname, dstname )
           else:
               shutil.copy2( srcname, dstname )
       except (IOError, os.error), why:
           errors.append( ( srcname, dstname, str( why ) ))
       except Error, err:
           errors.extend( err.args[0] )

   try:
       shutil.copystat( src, dst )
   except WindowsError:
       pass
   except OSError, why:
       errors.extend( (src, dst, str(why) ) )
   if errors:
       raise Error( errors )

parser = argparse.ArgumentParser(description='Copy directory tree with transliterated pathnames')
parser.add_argument('-e','--encoding', help='File system encoding', default='utf-8')
parser.add_argument('-s','--src', help='Source folder name', default='./' )
parser.add_argument('-d','--dst', help='Destination folder name', default='./')

input_parametres = parser.parse_args()

translit = dict(zip(u"абвгдеёзийклмнопрстуфхьъ", "abvgdeeziyklmnoprstufh'~"))
translit.update({u'ц': 'tz', u'ч': 'ch', u'ш': 'sh', u'ы': 'yi',
                u'э': 'ye', u'ю': 'ju', u'я': 'ja', u' ': '_'})

src = os.path.abspath( input_parametres.src )
dst = os.path.abspath( input_parametres.dst )

if not os.path.exists( dst ):
   dstname = ''.join([translit.get(x, x) for x in dst.decode( input_parametres.encoding ).lower()]).encode( input_parametres.encoding ).lower()
   os.makedirs( dstname )
   dst = os.path.abspath( dstname )

if not os.path.exists( src ) or os.path.isfile( dst ):
   raise FolderError()

copytranstree( src, dst )

The script transliterates and copies a tree of files from a source folder to destination, considering encoding passed to the script (UTF for Ubuntu, CP121 for Windows).

Any ideas for common improvement of structure for Linux OS?

\$\endgroup\$

1 Answer 1

8
\$\begingroup\$
  1. All exceptions in python should inherit from an Exception class.
  2. WindowsError only works on Windows, its better to use the more general OSError
  3. You twice have a very long line to transliterate a string. Put it in a function and call it
  4. You should put any code that is part of the script proper in a main function.
  5. You shouldn't throw exceptions that you know won't be caught.
  6. The way you collect errors by throwing them up the stack is ugly.
  7. If you split the walking logic from the file operation logic the code will be clearer.

My updates (completely untested and probably wrong):

#!/usr/bin/python
# coding=utf-8

import os
import argparse
import shutil

class CommandError(Exception):
    """
    Thrown when the user has made a mistake in the command line
    """

def transliterated_walk(encoding, src, dst):
    """
    A generator which will walk over src producing them
    along with the transliterated versions in dst
    """
    files = []
    for filename in os.listdir(src):
        src_path = os.path.join(src, filename)
        dest_path = os.path.join(dest, transliterated(encoding, filename))

        if os.path.isdir(path):
            for entry in transliterated_walk(encoding, src_path, dest_path):
                yield entry
        else:
            files.append( (src_path, dest_path) )
    yield src, dest, files

def copytranstree (encoding, src, dst):
    errors = []

    for src_path, dest_path, filenames in transliterated_walk(encoding, src, dest):
        try:
           os.makedirs( dest_path )
        except WindowsError:
           pass

        for src_filename, dest_filename in filenames:
            try:
                shutil.copy2( src_filename, dst_filename )
            except (IOError, os.error) as why:
                errors.append( (src_filename, dest_filename, str(why) )

        try:
           shutil.copystat( src, dst )
        except OSError as why:
           errors.append( (src_path, dst_path, str(why) ) )


TRANSLIT = dict(zip(u"абвгдеёзийклмнопрстуфхьъ", "abvgdeeziyklmnoprstufh'~"))
TRANSLIT.update({u'ц': 'tz', u'ч': 'ch', u'ш': 'sh', u'ы': 'yi',
                u'э': 'ye', u'ю': 'ju', u'я': 'ja', u' ': '_'})

def transliterate(encoding, string):
    decoded = string.decode(encoding).lower()
    transliterated = ''.join(translit.get(x, x) for x in decoded)
    return transliterated.encode(encoding).lower()

def main():
    try:
        parser = argparse.ArgumentParser(description='Copy directory tree with transliterated pathnames')
        parser.add_argument('-e','--encoding', help='File system encoding', default='utf-8')
        parser.add_argument('-s','--src', help='Source folder name', default='./' )
        parser.add_argument('-d','--dst', help='Destination folder name', default='./')

        input_parametres = parser.parse_args()



        src = os.path.abspath( input_parametres.src )
        dst = os.path.abspath( input_parametres.dst )

        if not os.path.exists( dst ):
           dstname = transliterate(dst)
           os.makedirs( dstname )
           dst = os.path.abspath( dstname )

        if not os.path.exists( src ):
            raise CommandError('%s does not exist' % src)
        if os.path.isfile( dst ):
            raise CommandError('%s is a file' % dest)

        copytranstree( encoding, src, dst )
    except CommandError as error:
        print error

if __name__ == '__main__':
    main()
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.