Вступление
Работа с файлами - один из самых важных навыков, которым нужно овладеть на любом языке программирования, и правильное выполнение этого имеет первостепенное значение. Ошибка может вызвать проблемы в вашей программе, других программах, работающих в той же системе, и даже в самой системе.
Возможные ошибки могут возникать из-за того, что родительский каталог не существует, или из-за того, что другие программы одновременно изменяют файлы в файловой системе, создавая что-то, что называется состоянием гонки .
Состояние гонки (в данном случае называется гонкой данных ) возникает, когда две или более программы хотят создать файл с тем же именем в одном месте. Если возникает ошибка этого типа, ее очень сложно найти и исправить, поскольку она недетерминирована, или, проще говоря, могут происходить разные вещи в зависимости от точного времени двух гонщиков, соревнующихся за данные.
В этой статье мы увидим, как создать подкаталог в Python безопасным способом, шаг за шагом. Отныне все будет работать на Mac, Linux и Windows.
Безопасное создание вложенного каталога с помощью pathlib
Есть много способов создать подкаталог, но, пожалуй, самый простой -
использовать модуль pathlib
Модуль pathlib
в первую очередь создан
для того, чтобы помочь абстрагироваться от файловых систем различных
операционных систем и предоставить единый интерфейс для работы с
большинством из них.
Благодаря этому ваш код должен быть независимым от платформы. Обратите внимание, что это работает только в более новых версиях Python (3.5 и выше).
Допустим, у нас есть абсолютный путь к каталогу, данный нам в виде
строки, и мы хотим создать подкаталог с заданным именем. Давайте
создадим каталог под названием OuterDirectory
и InnerDirectory
в
него InnerDirectory.
Мы импортируем Path
из pathlib
, создадим Path
с желаемым путем
для нашего нового файла и воспользуемся mkdir()
который имеет
следующую сигнатуру:
Path.mkdir(mode=0o777, parents=False, exist_ok=False)
Следующий фрагмент кода выполняет то, что мы описали выше:
from pathlib import Path # Import the module
path = Path("/home/kristina/OuterDirectory/InnerDirectory") # Create Path object
path.mkdir() # Cake the directory
Если mkdir()
не завершится успешно, каталог не будет создан, и
возникнет ошибка.
Параметры и ошибки mkdir ()
Если вы запустите код без создания OuterDirectory
, вы увидите
следующую ошибку:
Traceback (most recent call last):
File "makesubdir.py", line 3, in <module>
path.mkdir()
File "/home/kristina/anaconda3/lib/python3.7/pathlib.py", line 1230, in mkdir
self._accessor.mkdir(self, mode)
FileNotFoundError: [Errno 2] No such file or directory: '/home/kristina/OuterDirectory/InnerDirectory'
Или, если InnerDirectory
уже существует:
Traceback (most recent call last):
File "/home/kristina/Desktop/UNM/makesubdir.py", line 3, in <module>
path.mkdir()
File "/home/kristina/anaconda3/lib/python3.7/pathlib.py", line 1230, in mkdir
self._accessor.mkdir(self, mode)
FileExistsError: [Errno 17] File exists: '/home/kristina/OuterDirectory/InnerDirectory'
Если каталог уже существует, поднятая ошибка будет FileExistsError
, и
если родитель не существует, FileNotFoundError
будет поднят.
Поскольку мы не хотим, чтобы наша программа прерывалась всякий раз, когда она сталкивается с такой ошибкой, мы поместим этот код в блок try:
from pathlib import Path
path = Path("/home/kristina/OuterDirectory/InnerDir")
try:
path.mkdir()
except OSError:
print("Failed to make nested directory")
else:
print("Nested directory made")
При запуске, если каталог успешно создан, вывод будет:
Nested directory made
Если мы столкнемся с ошибками, будет выведено следующее:
Failed to make a nested directory
Метод mkdir()
принимает три параметра: mode
, parents
и exit_ok
.
- Параметр
mode
, если он задан, в сочетании сumask
указывает, какие пользователи имеют права на чтение, запись и выполнение. По умолчанию у всех пользователей есть все привилегии, которые могут быть не теми, которые нам нужны, если безопасность является проблемой. Мы коснемся этого позже. parents
указывает, в том случае , если родительский каталог отсутствует, следует метод:- Создайте сам отсутствующий родительский каталог (
true
) - Или вызвать ошибку, как в нашем втором примере (
false
)
- Создайте сам отсутствующий родительский каталог (
exist_ok
указывает, следует лиFileExistsError
, если каталог с таким же именем уже существует. Обратите внимание, что эта ошибка все равно будет возникать, если файл с тем же именем не является каталогом.
Назначение прав доступа
Давайте создадим каталог с именем SecondInnerDirectory
котором только
владелец имеет все права на чтение, запись и выполнение, внутри
несуществующего SecondOuterDirectory
:
from pathlib import Path
path = Path("/home/kristina/SecondOuterDirectory/SecondInnerDirectory")
path.mkdir(mode = 0o007, parents= True, exist_ok= True)
Это должно выполняться без ошибок. Если мы перейдем к
SecondOuterDirectory
и проверим его содержимое с консоли следующим
образом:
$ ls -al
У нас должен получиться результат:
total 12
drwxrwxr-x 3 kristina kristina 4096 dec 10 01:26 .
drwxr-xr-x 77 kristina kristina 4096 dec 10 01:26 ..
d------rx 2 kristina kristina 4096 dec 10 01:26 SecondInnerDirectory
Итак, мы видим, что родительский каталог был успешно создан, но привилегии не такие, как ожидалось. Владелец не имеет права писать.
Проблема в том, что umask
не позволяет нам создавать желаемые
привилегии. Чтобы обойти это, мы сохраним umask
, временно изменим его
и, наконец, вернем его к исходному значению с помощью umask()
из
модуля ОС. umask()
возвращает старое значение umask
.
Давайте перепишем наш код, чтобы проверить это:
from pathlib import Path
import os
old_mask = os.umask(0) # Saving the old umask value and setting umask to 0
path = Path("/home/kristina/SecondOuterDirectory/SecondInnerDirectory")
path.mkdir(mode = 0o007, parents= True, exist_ok= True)
os.umask(old_mask) # Reverting umask value
Выполнение этого кода и ls -al
приведет к следующему результату:
total 12
drwxrwxrwx 3 kristina kristina 4096 dec 10 01:45 .
drwxr-xr-x 77 kristina kristina 4096 dec 10 01:45 ..
d------rwx 2 kristina kristina 4096 dec 10 01:45 SecondInnerDirectory
Заключение
Чтобы безопасно манипулировать файлами во многих различных системах, нам
нужен надежный способ обработки ошибок, таких как скачки данных. Python
предлагает отличную поддержку для этого через модуль pathlib
Ошибки всегда могут возникать при работе с файловыми системами, и лучший способ справиться с этим - это тщательно настроить системы для выявления всех ошибок, которые потенциально могут привести к сбою нашей программы или вызвать другие проблемы. Написание чистого кода делает программы надежными.