HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Web-uploading of Cyrillic-named images

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
uploadingnamedcyrillicwebimages

Problem

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

Solution


  • All exceptions in python should


inherit from an Exception class.

  • WindowsError only works on Windows, its better to use the more general OSError



  • You twice have a very long line to transliterate a string. Put it in a function and call it



  • You should put any code that is part of the script proper in a main function.



  • You shouldn't throw exceptions that you know won't be caught.



  • The way you collect errors by throwing them up the stack is ugly.



  • 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()

Code Snippets

#!/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()

Context

StackExchange Code Review Q#2011, answer score: 8

Revisions (0)

No revisions yet.