viernes, 30 de octubre de 2015

Empaquetar y desempaquetar archivos con shutil


El módulo shutil cuenta, desde Python 3.2, con funciones de alto nivel para empaquetar y desempaquetar archivos basadas en los módulos zipfile y tarfile.

Las operaciones que se pueden realizar son las siguientes:
  • empaquetar y desempaquetar archivos con distintos formatos (zip, tar. etcétera);
  • obtener una lista de formatos permitidos;
  • y registrar nuevos formatos o suprimir formatos existentes de un sistema.

Empaquetar un directorio: make_archive()


La función make_archive() se utiliza para empaquetar o comprimir el contenido de un directorio. Dicha función devuelve el nombre del archivo creado, que será del tipo zip, tar, etc.

shutil.make_archive(base_name, format[, root_dir[, base_dir[, 
                    verbose[, dry_run[, owner[, group[, logger]]]]]]])

Argumentos de la función:
  • base_name: ruta y nombre del paquete a crear.
  • format: formato del archivo: zip, tar, bztar. El formato xztar es soportado desde Python 3.5.
  • root_dir: directorio a empaquetar (raíz).
  • base_dir: directorio a empaquetar incluyendo path.
  • dry_run: Si su valor es True no se creará el paquete, pero todas las operaciones que se ejecuten se registrarán en un archivo log.
  • Los valores de los argumentos owner (propietario) y group se asignarán al paquete. Si se omiten, se asignarán los valores actuales del propietario y grupo.
  • Logger: por lo general se corresponde con una instancia de logging.Logger que permitirá registrar todas las operaciones que se realicen durante el archivado.

Tanto el argumento root_dir como base_dir tienen por defecto asignado el path del directorio de trabajo actual.

Ejemplo:  Empaqueta (o comprime) una carpeta

El siguiente ejemplo muestra el modo de empaquetar (o comprimir) una carpeta de fotografías llamada "carpeta-fotos" utilizando el formato "zip". El archivo a crear se llamará "viaje.zip".

import shutil

archivo_zip = shutil.make_archive("viaje", "zip", "carpeta-fotos")
print("Creado el archivo:", archivo_zip)

Ejemplo: Empaqueta carpeta incluyendo rutas

Para incluir el path, en este caso desde el directorio de trabajo actual, de todos los archivos y directorios en el archivo "viaje.zip":

archivo_zip = shutil.make_archive("viaje", 
                                  "zip", 
                                  base_dir ="carpeta-fotos")

La ruta de base_dir puede ser relativa al directorio de trabajo actual, como en el ejemplo, o absoluta (por ejemplo: /home/usuario/carpeta-fotos).

Ejemplo: Empaqueta carpeta y registra operaciones en un archivo log

En el siguiente ejemplo se registran en el archivo "viaje.log" todas las operaciones que se realizan durante la creación del paquete:

import shutil, logging
logging.basicConfig(level=logging.DEBUG,
    format='%(asctime)s : %(levelname)s : %(message)s',
    filename = 'viaje.log',
    filemode = 'w',)
logging.info("Inicio del proceso")
archivo_zip = shutil.make_archive("viaje",
    "zip", 
    base_dir ="carpeta-fotos", 
    logger=logging)
logging.info("Fin del proceso")


viaje.log:
2015-10-30 20:44:01,147 : INFO : Inicio del proceso
2015-10-30 20:44:15,167 : INFO : creating 'viaje.zip' and adding 'carpeta-fotos' to it
2015-10-30 20:44:15,199 : INFO : adding 'carpeta-fotos/foto5.jpg'
2015-10-30 20:44:15,224 : INFO : adding 'carpeta-fotos/foto4.jpg'
2015-10-30 20:44:15,248 : INFO : adding 'carpeta-fotos/foto2.jpg'
2015-10-30 20:44:15,272 : INFO : adding 'carpeta-fotos/foto1.jpg'
2015-10-30 20:44:15,295 : INFO : adding 'carpeta-fotos/foto3.jpg'
2015-10-30 20:44:29,212 : INFO : Fin del proceso


Desempaquetar un directorio: unpack_archive()


La función unpack_archive() del módulo shutil se emplea para desempaquetar o descomprimir un archivo zip, tar, etcétera.

shutil.unpack_archive(filename[, extract_dir[, format]])


Argumentos de la función:
  • filename: nombre del archivo comprimido (puede incluir el path).
  • extract_dir: nombre del directorio destino donde se descomprimirá el archivo. Si no se indica, se asumirá el directorio de trabajo actual.
  • format es el formato de archivo comprimido: zip, tar, gztar o cualquier otro formato de descompresión registrado con la función register_unpack_archive().

Ejemplo: Desempaqueta (o descomprime) en el directorio actual

El siguiente ejemplo muestra la forma de descomprimir el archivo "viajes.zip". Todo su contenido será extraído en el directorio de trabajo actual.

archivo_zip = shutil.unpack_archive('viaje.zip')

Ejemplo: Desempaqueta en otro directorio diferente

Para extraer los archivos en otro directorio diferente al actual (por ejemplo. "fotografías"):

archivo_zip = shutil.unpack_archive('fotos-viaje.zip','fotografias')

Si el archivo fue comprimido utilizando el argumento base_dir todos los path de los archivos y directorios serán conservados durante la descompresión o desempaquetado.


Obtener formatos permitidos para empaquetar: get_archive_formats()


La función shutil.get_archive_formats() devuelve una lista con los formatos permitidos para empaquetar registrados en el sistema; siendo cada elemento de la secuencia devuelta una tupla con el contenido: (nombre, descripción).

Ejemplo: Obtiene formatos permitidos para empaquetar

shutil.get_archive_formats()


Salida:

[('bztar', "bzip2'ed tar-file"),
('gztar', "gzip'ed tar-file"),
('tar', 'uncompressed tar file'),
('zip', 'ZIP file')]


Es posible registrar nuevos formatos o proporcionar un compresor propio con la función register_archive_format().


Obtener formatos permitidos para desempaquetar: get_unpack_formats()


Devuelve una lista de todos los formatos registrados para desempaquetar. Cada elemento de la secuencia devuelto será una tupla con el contenido siguiente: (nombre, extensión, descripción).

Ejemplo: Obtiene formatos permitidos para desempaquetar

shutil.get_unpack_formats()


Salida:

[('bztar', ['.bz2'], "bzip2'ed tar-file"),
('gztar', ['.tar.gz', '.tgz'], "gzip'ed tar-file"),
('tar', ['.tar'], 'uncompressed tar file'),
('zip', ['.zip'], 'ZIP file')]


Es posible registrar nuevos formatos o proporcionar un desempaquetador propio con la función register_unpack_format().


Otras funciones relacionadas


Para registrar en el sistema un nuevo formato para empaquetar:

shutil.register_archive_format(name, 
                               function[, extra_args[, description]])

Para suprimir del sistema un formato existente para empaquetar:

shutil.unregister_archive_format(name)

Para registrar en el sistema un nuevo formato para desempaquetar:

shutil.register_unpack_format(name, extensions, 
                              function[, extra_args[, description]])

Para suprimir del sistema un formato existente para desempaquetar:

shutil.unregister_unpack_format(name)


Ir al índice del tutorial de Python

lunes, 19 de octubre de 2015

Copiar, mover y borrar archivos/directorios con shutil



El módulo shutil consta, entre otras, de funciones para realizar operaciones de alto nivel con archivos y/o directorios. Dentro de las operaciones que se pueden realizar está copiar, mover y borrar archivos y/o directorios; y copiar los permisos y el estado de los archivos.

También, hay otras funciones adicionales para localizar ejecutables, cambiar el propietario y/o el grupo de un archivo/directorio; y consultar información del espacio de un disco.

Copiar archivos completos o parciales: copyfileobj()


La función shutil.copyfileobj() copia el contenido completo de un archivo origen (o sólo una parte del mismo) a un archivo destino. Si el archivo destino no existe será creado y si existe será reemplazado.

shutil.copyfileobj(fsrc, fdst[, length])

Ejemplo: Copia un archivo completo

import shutil, os

ruta = os.getcwd() + os.sep
origen = ruta + 'origen.txt'
destino = ruta + 'destino.txt'

if os.path.exists(origen):
    with open(origen, 'rb') as forigen:
        with open(destino, 'wb') as fdestino:
            shutil.copyfileobj(forigen, fdestino)
            print("Archivo copiado")

Esta función también permite copiar un archivo a partir de una posición determinada. Por ejemplo, si se está realizando la lectura secuencial de un archivo y la posición del puntero es distinta de cero se copiará desde la posición actual hasta el final del archivo.

Ejemplo: Copia contenido de archivo desde el byte número 21

import shutil, os

ruta = os.getcwd() + os.sep
origen = ruta + 'origen.txt'
destino = ruta + 'destino.txt'

if os.path.exists(origen):
    forigen = open(origen,'rb')
    forigen.seek(21)
    with open(destino, 'wb') as fdestino:
            shutil.copyfileobj(forigen, fdestino)
            print("Archivo copiado")

El valor de longitud es opcional y se corresponde con un entero que fija el tamaño del búfer (en bytes) para controlar el consumo de memoria. Este valor afecta a la velocidad de copia, especialmente, de archivos grandes.

Ejemplo: Copia un archivo ampliando el tamaño del búfer a 256 kb

import shutil, os
from time import time

ruta = os.getcwd() + os.sep
origen = ruta + 'video1.mp4'
destino = ruta + 'video2.mp4'
longitud = 262144

if os.path.exists(origen):
    with open(origen, 'rb') as forigen:
        with open(destino, 'wb') as fdestino:
            tiempo_inicial = time()
            shutil.copyfileobj(forigen, fdestino, longitud)
            tiempo = time() - tiempo_inicial
            print("Video copiado en .2f segundos" % tiempo)

Copiar archivos completos sin metadatos: copyfile()


La función shutil.copyfile() copia el contenido completo (sin metadatos) de un archivo origen a un archivo destino, pudiendo estar, uno y otro, en directorios diferentes. Si el archivo destino no existe será creado y si existe será reemplazado.

shutil.copyfile(src, dst, *, follow_symlinks=True)

Si el nombre y la ruta del archivo destino coincide con el origen se producirá la excepción SameFileError. Por otro lado, si el directorio destino no tiene permisos de escritura se producirá la excepción OSError.

Además, si el argumento follow_symlinks es False y el archivo origen se corresponde con un enlace simbólico se creará otro enlace en el destino.

Ejemplo: Copia un archivo sin sus metadatos

import shutil, os

ruta = os.getcwd() + os.sep
origen = ruta + 'origen.txt'
destino = ruta + 'destino.txt'

try:
    shutil.copyfile(origen, destino)
    print("Archivo copiado")
except:
    print("Se ha producido un error")

Copiar los permisos de un archivo: copymode()


La función shutil.copymode() copia los permisos de un archivo origen a uno destino. Esta copia no afecta al contenido del archivo, ni a la información del propietario y del grupo asignado.

shutil.copymode(src, dst, *, follow_symlinks=True)

Si el argumento follow_symlinks es False y el origen y el destino son enlaces simbólicos, la función intentará modificar el modo del archivo destino en lugar del archivo al que apunte. Esta funcionalidad no está disponible en todas las plataformas; para más información consultar la función copystat().

Ejemplo: Copia los permisos de un archivo

import shutil, os, stat

# Antes:
# -r--r--r-- 1 usuario usuario origen.txt
# -rw-rw-r-- 1 usuario usuario destino.txt

ruta = os.getcwd() + os.sep
origen = ruta + 'origen.txt'
destino = ruta + 'destino.txt'
if os.path.exists(origen) and os.path.exists(destino):
    print('Antes  :', oct(stat.S_IMODE(os.stat(destino).st_mode)))
    shutil.copymode(origen, destino)
    print('Después:', oct(stat.S_IMODE(os.stat(destino).st_mode)))

# Después:
# -r--r--r-- 1 usuario usuario origen.txt
# -r--r--r-- 1 usuario usuario destino.txt

En Python 3.3 se agregó el argumento follow_symlinks

Copiar el estado de un archivo: copystat()


La función shutil.copystat() copia los permisos, la fecha-hora del último acceso, la fecha-hora de la última modificación y los atributos de un archivo origen a un archivo destino. Además, en Linux, siempre que sea posible, se copiarán los atributos extendidos.

Esta copia no afecta al contenido del archivo, ni a la información del propietario y del grupo asignado.

shutil.copystat(src, dst, *, follow_symlinks=True)

Ejemplo: Copia el estado de un archivo (permisos, fechas-horas)

import shutil, os

ruta = os.getcwd() + os.sep
origen = ruta + 'origen.txt'
destino = ruta + 'destino.txt'

# Antes:
# -r--r--r-- 1 usuario usuario 84 oct 13 20:33 origen.txt
# -rw-rw-rw- 1 usuario usuario 63 oct 13 21:45 destino.txt

if os.path.exists(origen) and os.path.exists(destino):
    shutil.copystat(origen, destino)
    print("Estado copiado")

# Después:
# -r--r--r-- 1 usuario usuario 84 oct 13 20:33 origen.txt
# -r--r--r-- 1 usuario usuario 63 oct 13 20:33 destino.txt

En Python 3.3 se agregó el argumento follow_symlinks y el soporte para los atributos extendidos de Linux

Copiar archivos con permisos: copy()


La función shutil.copy() se utiliza para copiar archivos. Si la cadena indicada en destino es un directorio el archivo se copiará a éste con el mismo nombre del archivo origen.

shutil.copy(src, dst, *, follow_symlinks=True)

Además, la función después de realizar una copia devuelve el nombre con la ruta del archivo copiado. Hay que tener en cuenta que sólo se copiarán los datos y los permisos del archivo. Otros metadatos como la fecha-hora de creación/modificación no serán copiados. Para incluir también esta información se recomienda el uso de la función shutil.copy2().

Si el argumento follow_symlinks es False y el origen es un enlace simbólico, en destino se creará un enlace simbólico. Si el argumento follow_symlinks es True y el origen es un enlace simbólico, en destino se copiará el archivo al que apunte.

Ejemplo: Copia un archivo (sólo datos y permisos)

import shutil, os

ruta = os.getcwd() + os.sep
origen = ruta + 'origen.txt'
destino = ruta + 'destino.txt'
if os.path.exists(origen):
    try:
        archivo = shutil.copy(origen, destino)
        print('Copiado...', archivo)
    except:
        print('Error en la copia')

En Python 3.3 se agregó el argumento follow_symlinks. También, desde esta versión la función devuelve el nombre del archivo copiado.

Copiar archivos con permisos y metadatos: copy2()


Esta función es idéntica a shutil.copy() excepto que shutil.copy2() también intenta conservar todos los metadatos de los archivos copiados. Por ello, esta función se apoya en la función shutil.copystat().

shutil.copy2(src, dst, *, follow_symlinks=True)

Ejemplo: Copia archivos (datos, permisos y metadatos)

import shutil, os

ruta = os.getcwd() + os.sep
origen = ruta + 'origen.txt'
destino = ruta + 'destino.txt'
if os.path.exists(origen):
    try:
        archivo = shutil.copy2(origen, destino)
        print('Copiado...', archivo)
    except:
        print('Error en la copia')

En Python 3.3 se agregó el argumento follow_symlinks. También, la posibilidad de copiar los atributos extendidos (Linux) y que la función devuelva la ruta del archivo copiado.

Copiar un árbol de directorios con sus archivos: copytree()


La función shutil.copytree() copia un directorio origen y todo su contenido a un directorio destino que no debe existir. Durante el proceso se copiarán permisos y fechas-horas con shutil.copystat(). Además, para copiar los archivos (individuales) se utilizará la función shutil.copy2() que incluye permisos y metadatos.

shutil.copytree(src, dst, symlinks=False, ignore=None, 
                copy_function=copy2, ignore_dangling_symlinks=False)

Ejemplo: Copia un árbol de directorios con algunos archivos

En este ejemplo se utiliza la función shutil.ignore_patterns() que permite definir una serie de patrones (como los patrones utilizados con el módulo glob) para que en el proceso de copia se excluyan aquellos ficheros y/o directorios que coincidan con alguno de los patrones expresados.

import shutil, os

ruta = os.getcwd() + os.sep
origen = ruta + 'dir1'
destino = ruta + 'dir2'
ignorar_pat = shutil.ignore_patterns('*.dat', 'destino.txt', 'dir11')

if os.path.exists(origen):
    try:
        # Si ignore=None no se excluyen archivos/directorios
                
        arbol = shutil.copytree(origen, destino, ignore=ignorar_pat) 
        print('Árbol copiado a', arbol)
    except:
        print('Error en la copia')

Si symlinks es True los enlaces simbólicos de origen se representan como enlaces simbólicos en el nuevo árbol y los metadatos originales serán copiados cuando la plataforma lo permita. Si symlinks es False o se omite entonces tanto el contenido como los metadatos de los archivos vinculados se copiarán en el nuevo árbol.

Cuando symlinks sea False y el archivo apuntado por el enlace simbólico no exista se agregará una excepción a una lista de errores que se genera durante el proceso de copia. Se puede establecer el indicador opcional ignore_dangling_symlinks a True para deshabilitar esta excepción.

El argumento copy_function indica la función que se utilizará para copiar que, por defecto, es: shutil.copy2().

Borrar un árbol de directorios con sus archivos: rmtree()


La función shutil.rmtree() elimina un árbol de directorios completo con los archivos existentes. La ruta debe ser de un directorio y no un enlace simbólico que apunte a un directorio.

shutil.rmtree(path, ignore_errors=False, onerror=None)

Ejemplo: Borra un árbol de directorios con sus archivos

import shutil, os

ruta = os.getcwd() + os.sep
arbol = ruta + 'directorio'

if os.path.exists(arbol):    
    shutil.rmtree(arbol, ignore_errors=False) 
    print('Árbol de directorio borrado')
else:
    print('El directorio no existe')

Si el argumento ignore_errors es True los errores producidos por borrados fallidos serán ignorados. Si el valor de este argumento es False o se omite los errores producirán una excepción o serán gestionadas por una función especificada en el argumento onerror.

Mover un archivo o un directorio con su contenido: move()


La función shutil.move() mueve un archivo o un directorio (y su contenido) a otra ubicación y devuelve la ruta del nuevo destino.

shutil.move(src, dst, copy_function=copy2)

El argumento opcional copy_function se utiliza indicar la función de copia a utilizar (copy2, copy) en el caso de que no se use la función os.rename(). La función os.rename() la emplea shutil.move() cuando un proceso se puede resolver con renombrar en lugar de copiar.

Ejemplo: Mover un directorio y su contenido a otro directorio

import shutil, os

ruta = os.getcwd() + os.sep
origen = ruta + 'dir1'
destino = ruta + 'dir2'

if os.path.exists(origen):  
    ruta = shutil.move(origen, destino)
    print('El directorio ha sido movido a', ruta)
else:
    print('El directorio origen no existe')

Obtener información del espacio de un disco: disk_usage()


La función shutil.disk_usage() devuelve información del espacio del disco donde se encuentre la ruta indicada. La información retorna en una tupla con nombre (namedtuple) con tres valores: espacio total (total), usado (used) y libre (free) expresado en bytes.

shutil.disk_usage(path)

Ejemplo: Obtiene información del espacio total, usado y libre, en bytes

import shutil, os

ruta = os.getcwd()
datos = shutil.disk_usage(ruta)
print('Espacio total (bytes): ', datos.total)
print('Espacio usado (bytes): ', datos.used)
print('Espacio libre (bytes): ', datos.free)

Nuevo en Python 3.3. y disponible tanto para Unix como para Windows

Cambiar propietario y/o grupo de un archivo/directorio: chown()


La función shutil.chown() permite cambiar el usuario propietario y/o grupo del archivo o directorio indicado en el argumento path.

shutil.chown(path, user=None, group=None)

Ejemplo: Cambiar el grupo de un archivo

import shutil, os

ruta = os.getcwd() + os.sep
archivo = ruta + 'archivo.html'

# Antes: -rw-rw-r-- 1 usuario www-data archivo.html

try:
    shutil.chown(archivo, group="usuario")
    print('Se ha cambiado el propietario del archivo')
    
# Después: -rw-rw-r-- 1 usuario usuario  archivo.html

except:
    print('Error')

Disponible para Unix/Linux desde Python 3.3

Obtener la ruta de un archivo ejecutable: which()


La función which() del módulo shutil permite obtener la ruta de acceso a un ejecutable (o None si no hay acceso).

shutil.which(cmd, mode=os.F_OK | os.X_OK, path=None)

El argumento opcional mode permite determinar si un archivo existe y es ejecutable; y el argumento opcional path si no se especifica hará que la función shutil.which() utilice las rutas de la variable de entorno PATH para localizar el ejecutable.

Ejemplo: Obtener la ruta de Python 3.5

import shutil, os

archivo = 'python3.5'
ruta = shutil.which(archivo)
print(ruta) # /usr/local/bin/python3.5