Escribiendo ‘ls’ en Python3

El programa a continuación es un ejemplo de las cosas que se pueden hacer con Python 3. Para mí fue una excusa para aprender lo siguiente:

  • Uso de ‘.format’ para mostrar contenido con formato (mucho mejor que interpolación de cadena de caracteres con ‘%’)
  • La librería ‘OptionParser’ (Mejor que Getoptions)
  • Trucos con ‘list comprehensions’ , ordenaciones
  • Referencias a funciones

(Les debo el manejo de recursividad, me dio algo de flojera escribirlo :-))
 

#!/usr/bin/env python3
from optparse import OptionParser, OptionValueError
from collections import namedtuple
import locale, os, time
from _locale import LC_ALL
locale.setlocale(LC_ALL, "en_US.UTF-8") # locale -a
usage = '''
%prog [options] [path1 [path2] [... pathN]]]
The paths are optional; if not given '.' is used
@author: Jose Vicente Nunez (josevnz@kodegeek.com)
'''
Entry = namedtuple('Entry', 'name size modified')

def orderCheck(option, opt_str, value, parser):
    if value in ['n', 'name']:
        parser.values.order = "name"
    elif value in ['m', 'modified']:
        parser.values.order = "modified"
    elif value in ['s', 'size']:
        parser.values.order = "size"
    else:
        raise OptionValueError("Invalid value for --order received: {0}".format(value))

def getKeyByName(entry):
    return entry.name

def getKeyBySize(entry):
    return int(entry.size)

def getKeyByModif(entry):
    return int(entry.modified())

def createTuple(path):
    bits = os.stat(path)
    return Entry(path, bits.st_size, bits.st_mtime) 

def doLs(path, hidden, getKey):
    if (not os.path.isdir(path)):
        return path
    if hidden: # On Unix, hidden files start with '.'
        return sorted([ createTuple(os.path.join(path, entry)) for entry in os.listdir(path) ], key=getKey, reverse=False)
    return sorted([ createTuple(os.path.join(path, entry)) for entry in os.listdir(path) if entry[0] != "." ], key=getKey, reverse=False)

def doLsR(path, hidden, getKey):
    # TODO
    pass

def formatEntries(entries, modified, sizes):
    if entries == None:
        return
    dirs = 1
    files = 0
    for entry in entries:
        mod = time.ctime(entry.modified) if modified else ""
        size = entry.size if sizes else ""
        if os.path.isfile(entry.name):
            files += 1
        else:
            dirs += 1
        print("{modif}{theSize:>10,} bytes{name:>35}".format(modif=mod, theSize=size, name=entry.name))
    print("files={0}, directories={1}".format(files, dirs))
        
parser = OptionParser(usage=usage)
parser.add_option("-H", "--hidden", action="store_true", dest="hidden", default=False, help='Show hidden files [default: off]')
parser.add_option("-m", "--modified", action="store_true", dest="modified", default=False, help='Show last modified date/time [default: off]')
parser.add_option("-r", "--recursive", action="store_true", dest="recursive", default=False, help='Recurse into sub-directories [default: off]')
parser.add_option("-s", "--sizes", action="store_true", dest="sizes", default=False, help='Show sizes [default: off]')
parser.add_option("-o", "--order", action="callback", type="string",  callback=orderCheck, default="name", help='''Order by ('name', 'n', 'modified', 'm', 'size', 's') [default: name]''')
(options, args) = parser.parse_args()
hidden = options.hidden
modified = options.modified
recursive = options.recursive
sizes = options.sizes
order = options.order
getKey = getKeyByName
if order == 'size':
    getKey = getKeyBySize
elif order == 'modified':
    getKey = getKeyByModif
paths = args if len(args) > 0  else ["."]
lsCallback = doLs if not recursive else doLsR

for path in paths:
    formatEntries(lsCallback(path, hidden, getKey), modified, sizes)