Рубрики
Без рубрики

Python: Безопасное Создание Вложенного Каталога

В этом уроке мы рассмотрим примеры безопасного создания вложенного каталога в Python 3.5+, а также более старых версиях.

Автор оригинала: Kristina Popovic.

Вступление

Манипулирование файлами-один из самых важных навыков, которым нужно овладеть в любом языке программирования, и делать это правильно крайне важно. Ошибка может вызвать проблему в вашей программе, других программах, работающих в той же системе, и даже в самой системе.

Возможные ошибки могут возникнуть из-за того, что родительский каталог не существует, или из-за того, что другие программы одновременно изменяют файлы в файловой системе, создавая то, что называется race condition .

Условие race (в данном случае называемое data race ) возникает, когда две или более программы хотят создать файл с одинаковым именем в одном и том же месте. Если этот тип ошибки возникает, его очень трудно найти и исправить, так как он недетерминирован, или, проще говоря, разные вещи могут произойти в зависимости от точного времени двух гонщиков, конкурирующих за данные.

В этой статье мы рассмотрим, как создать подкаталог в Python the safeway, шаг за шагом. Отныне все будет работать на Mac, Linux и Windows.

Безопасное создание вложенного каталога с помощью path lib

Существует множество способов создания подкаталога, но, возможно, самым простым является использование модуля pathlib . Модуль pathlib в первую очередь предназначен для абстрагирования различных файловых систем операционной системы и обеспечения единого интерфейса для работы с большинством из них.

Благодаря этому ваш код должен быть независимым от платформы. Обратите внимание, что это работает только на более новых версиях Python (3.5 и выше).

Допустим, у нас есть абсолютный путь к каталогу, заданный нам в виде строки, и мы хотим создать подкаталог с заданным именем. Давайте создадим каталог с именем Outer Directory и поместим в него Inner Directory .

Мы импортируем 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()

Если вы запустите код без создания внешнего каталога , то увидите следующую ошибку:

Traceback (most recent call last):
  File "makesubdir.py", line 3, in 
    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'

Или если Внутренний каталог уже существует:

Traceback (most recent call last):
  File "/home/kristina/Desktop/UNM/makesubdir.py", line 3, in 
    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 указывает, что в случае отсутствия родительского каталога метод должен:

    1. Создайте сам отсутствующий родительский каталог ( true )
    2. Или вызвать ошибку, как в нашем втором примере ( false )
  • exist_ok указывает, если файл Существует Ошибка должна быть вызвана, если каталог с таким же именем уже существует. Обратите внимание, что эта ошибка все равно будет вызвана, если файл с тем же именем не является каталогом.

Назначение Прав Доступа

Давайте создадим каталог с именем Second Inner Directory , где только владелец имеет все права на чтение, запись и выполнение, внутри несуществующего SecondOuterDirectory :

from pathlib import Path
path = Path("/home/kristina/SecondOuterDirectory/SecondInnerDirectory")
path.mkdir(mode = 0o007, parents= True, exist_ok= True)

Это должно выполняться без ошибок. Если мы перейдем во Второй внешний каталог и проверим его содержимое с консоли следующим образом:

$ 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------r-x  2 kristina kristina 4096 dec 10 01:26 SecondInnerDirectory

Итак, мы видим, что родительский каталог был успешно создан, но привилегии не такие, как ожидалось. Владелец не имеет права писать.

Проблема здесь в том, что umask не позволяет нам создавать желаемые привилегии. Чтобы обойти это, мы сохраним исходное значение umask , временно изменим его и, наконец, вернем его к исходному значению с помощью метода umask() из модуля OS. 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 .

Ошибки всегда могут возникать при работе с файловыми системами, и лучший способ справиться с этим-тщательно настроить системы, чтобы поймать все ошибки, которые потенциально могут привести к сбою нашей программы или вызвать другие проблемы. Написание чистого кода делает программы долговечными.