web-dev-qa-db-fra.com

Copiez le fichier avec pathlib dans Python

J'essaie de copier un fichier avec pathlib

import pathlib
import shutil

my_file=pathlib.Path('/etc/hosts')
to_file=pathlib.Path('/tmp/foo')
shutil.copy(my_file, to_file)

Je reçois cette exception:

/home/foo_egs_d/bin/python /home/foo_egs_d/src/test-pathlib-copy.py
Traceback (most recent call last):
  File "/home/foo_egs_d/src/test-pathlib-copy.py", line 6, in <module>
    shutil.copy(my_file, to_file)
  File "/usr/lib/python2.7/shutil.py", line 117, in copy
    if os.path.isdir(dst):
  File "/home/foo_egs_d/lib/python2.7/genericpath.py", line 41, in isdir
    st = os.stat(s)
TypeError: coercing to Unicode: need string or buffer, PosixPath found

Process finished with exit code

... comment copier un fichier avec pathlib dans Python 2.7?

38
guettli

Et alors?

import pathlib
import shutil

my_file = pathlib.Path('/etc/hosts')
to_file = pathlib.Path('/tmp/foo')
shutil.copy(str(my_file), str(to_file))

Le problème est que pathlib.Path Crée un objet PosixPath si vous utilisez Unix/Linux, WindowsPath si vous utilisez Microsoft Windows.

Mais shutil.copy() a besoin d'une chaîne comme arguments. Il suffit donc d'utiliser str() ici, lorsque vous utilisez la fonction str() sur un objet Path, il retournera le chemin d'origine.

31
Kevin Guan

La raison pour laquelle shutil.copy() ne fonctionne pas est que vous n'utilisez pas la dernière version de Python, Python 3.6 shutil.copy()can handle Path objets (ou sous-classes de ceux-ci). Que pour les anciennes versions de Python cela génère une erreur, c'est parce que les implémentations de shutil attendent des arguments de chaîne pour copy, et non les arguments de type pathlib.Path.

Ce que vous voulez réellement pouvoir écrire, c'est:

my_file.copy(to_file)

Vous pouvez sous-classer Path pour inclure une telle méthode et adapter la création de my_file. Je trouve plus facile de simplement le greffer/patch-singe/duck-punch sur l'existant pathlib.Path

from pathlib import Path


def _copy(self, target):
    import shutil
    assert self.is_file()
    shutil.copy(str(self), str(target))  # str() only there for Python < (3, 6)

Path.copy = _copy

Vous pouvez placer ce code où vous le souhaitez, tant qu'il est exécuté avant d'appeler la méthode .copy Sur l'une des instances Path. L'argument de .copy() peut être un fichier ou un répertoire.

23
Anthon

Depuis Python 3.5, sans importer shutil, vous pouvez faire:

_from pathlib import Path

dest = Path('dest')
src = Path('src')
dest.write_bytes(src.read_bytes()) #for binary files
dest.write_text(src.read_text()) #for text files
_

Pour Python 2.7, _pathlib2_ fournit le _read_bytes_, _read_text_, _write_bytes_ et _write_text_ méthodes.

Le fichier sera chargé en mémoire, cette méthode ne convient donc pas aux fichiers plus grands que la mémoire disponible de la machine.

Selon les commentaires, on peut utiliser _write_bytes_ et _read_bytes_ pour copier des fichiers texte, mais si vous devez gérer l'encodage au moment de la copie _write_text_ an _read_text_ présenter l'avantage de deux paramètres supplémentaires:

  • encoding est le nom de l'encodage utilisé pour décoder ou encoder le fichier
  • errors est une chaîne facultative qui spécifie comment les erreurs d'encodage et de décodage doivent être traitées

Ils ont tous deux la même signification que dans open() .

14
Jacques Gaudin

Comment shutil a été converti pour accepter les objets pathlib.Path Dans Python 3.6

Comme mentionné sur https://stackoverflow.com/a/40319071/895245 shutil in Python 3.6 peut prendre des objets pathlib.Path.

Comme cela semblait assez magique, j'ai décidé d'étudier un peu comment il était implémenté pour voir si je serais en mesure de réutiliser cette magie dans mes propres cours.

L'amélioration est le résultat du PEP 519: https://www.python.org/dev/peps/pep-0519/

Cela généralisait beaucoup de fonctionnalités stdlib, et la documentation n'était pas mise à jour de manière conséquente, y compris la plupart de shutil qui à partir de 3.7 seuls les documents supportent dans une seule fonction . Bienvenue aux joies de la frappe dynamique.

Là où cela est documenté, le stlib renvoie au glossaire des "objets de type chemin": https://docs.python.org/3/glossary.html#term-path-like-object

Un objet représentant un chemin d'accès au système de fichiers. Un objet semblable à un chemin est soit un objet str ou bytes représentant un chemin, soit un objet implémentant le protocole os.PathLike. Un objet qui prend en charge le protocole os.PathLike peut être converti en chemin de système de fichiers str ou bytes en appelant la fonction os.fspath (); os.fsdecode () et os.fsencode () peuvent être utilisés pour garantir un résultat str ou octets à la place, respectivement. Introduit par PEP 519.

et qui renvoie ensuite à la documentation de os.PathLike: https://docs.python.org/3/glossary.html#term-path-like-object

Une classe de base abstraite pour des objets représentant un chemin de système de fichiers, par ex. pathlib.PurePath.

Nouveau dans la version 3.6.

abstractmethod __fspath__()

Renvoie la représentation du chemin d'accès au système de fichiers de l'objet.

La méthode ne doit renvoyer qu'un objet str ou bytes, la préférence étant pour str.

Les principaux engagements de mise en œuvre semblent être:

Si vous souhaitez implémenter vos propres classes de type chemin, vous pouvez le faire comme:

#!/usr/bin/env python3

class MyPath:
    def __init__(self, path):
        self.path = path
    def __fspath__(self):
        return self.path

with open(MyPath('f'), 'w'):
    pass

Testé dans Python 3.6.7, Ubuntu 18.10.

Vous pouvez utiliser pathlib renommer la méthode au lieu de shutil.move ().

import pathlib

my_file = pathlib.Path('/etc/hosts')
to_file = pathlib.Path('/tmp/foo')
my_file.rename(to_file)
4
Geoff D