И последний пример многократного использования программного кода: когда я приступил к тестированию сценария uploadall из предыдущего раздела, он содержал ошибку, которая вызывала бесконечный рекурсивный цикл, снова и снова копируя весь сайт в новые подкаталоги, пока FTP-сервер не закрывал соединение (непредусмотренная особенность программы!). Фактически выгрузка продолжалась до достижения 13 уровня вложенности, прежде чем сервер закрывал соединение — это приводило к блокированию моего сайта, заставляя исправить ошибку.
Чтобы избавиться от всех файлов, выгруженных по ошибке, я быстро написал сценарий, представленный в примере 13.16, в экстремальном (даже в паническом) режиме. Он удаляет все файлы и вложенные подкаталоги в дереве на удаленном сервере. К счастью, это оказалось очень просто, учитывая, что пример 13.16 унаследовал повторно используемые инструменты от суперкласса FtpTools. В данном примере просто определяется расширение, рекурсивно удаляющее файлы и каталоги на сервере. Даже в таком вынужденном режиме, в каком я оказался, можно с успехом использовать преимущества ООП.
Пример 13.16. PP4E\Internet\Ftp\Mirror\cleanall.py
#!/bin/env python
############################################################################ расширяет класс FtpTools возможностью удаления файлов и подкаталогов
в дереве каталогов на сервере; поддерживает удаление вложенных подкаталогов; зависит от формата вывода команды dir(), который может отличаться
на некоторых серверах! — смотрите подсказки в файле
Tools\Scripts\ftpmirror.py, в каталоге установки Python;
добавьте возможность загрузки дерева каталогов с сервера;
############################################################################
from ftptools import FtpTools
class CleanAll(FtpTools):
"""
удаляет все дерево каталогов на сервере def __init__(self):
self.fcount = self.dcount = 0
def getlocaldir(self):
return None # не имеет смысла здесь
def getcleanall(self)
# само собой разумеется здесь
def cleanDir(self)
для каждого элемента в текущем каталоге на сервере
удаляет простые файлы, выполняет рекурсивный спуск и удаляет подкаталоги, метод dir() объекта FTP передает каждую строку указанной функции или методу
if __name__ == ‘__main__’:
ftp = CleanAll()
ftp.configTransfer(site=’learning-python.com’, rdir=’training’, user=’lutz’)
ftp.run(cleanTarget=ftp.cleanDir)
print(‘Done:’, ftp.fcount, ‘files and’, ftp.dcount, ‘directories cleaned.’)
Помимо рекурсивного алгоритма, позволяющего обрабатывать деревья каталогов произвольной формы, главной хитростью здесь является анализ вывода содержимого каталога на сервере. Метод nlst класса FTP, использовавшийся нами ранее, возвращает простой список имен файлов. Здесь мы используем метод dir, чтобы получить дополнительную информацию, как показано ниже:
C:\…\PP4E\Internet\Ftp> ftp learning-python.com ftp> cd training ftp> dir
drwxr-xr-x 11 5693094 450 4096 May 4 11:06 .
drwx—r-x 19 5693094 450 8192 May 4 10:59 ..
-rw— r—
|
1 5693094
|
450
|
15825
|
May 4 11:02 2009-public-classes.htm
|
-rw— r—
|
1 5693094
|
450
|
18084
|
May 4 11:02 2010-public-classes.html
|
drwx—r-x
|
3 5693094
|
450
|
4096
|
May 4 11:02 books
|
-rw— r—
|
1 5693094
|
450
|
3783
|
May 4 11:02 calendar-save-aug09.html
|
-rw— r—
|
1 5693094
|
450
|
3923
|
May 4 11:02 calendar.html
|
drwx—r-x
|
2 5693094
|
450
|
4096
|
May 4 11:02 images
|
-rw— r—
|
1 5693094
|
450
|
6143
|
May 4 11:02 index.html
|
…часть строк опущена…
Формат вывода этого метода потенциально зависит от конкретного сервера, поэтому проверьте формат вывода на своем сервере, прежде чем приступать к использованию этого сценария. Для данного сервера провайдера, работающего под управлением Unix, если первый символ первого элемента строки является символом «d», это означает, что имя файла в конце строки является именем каталога. Анализ строки заключается в простом ее разбиении по пробельным символам и извлечении составляющих ее частей.
Обратите внимание, что этот сценарий, как и предшествующие ему, должен пропускать символические обозначения «.» и «..» текущего и родительского каталогов для корректной работы с данным сервером. Это может показаться странным, но появление этих имен в выводе также может зависеть от сервера — некоторые серверы, которые я использовал при опробовании примеров для этой книги, не включали эти специальные имена в списки. Мы можем проверить особенности сервера в этом отношении, используя ftplib в интерактивном сеансе, как если бы это был переносимый клиент FTP:
C:\…\PP4E\Internet\Ftp> python
> >> from ftplib import FTP
> >> f = FTP(‘ftp.rmi.net’)
> >> f.login(‘lutz’, ‘xxxxxxxx’) # вывод строк опущен
> >> for x in f.nlst()[:3]: print(x) # в списках отсутствуют имена . и ..
2004-longmont-classes.html
2005-longmont-classes.html
2006-longmont-classes.html
> >> L = []
> >> f.dir(L.append) # тот же список, но с подробной информацией
> >> for x in L[:3]: print(x)
> rw-r—r— 1 ftp ftp 8173 Mar 19 2006 2004-longmont-classes.html
> rw-r—r— 1 ftp ftp 9739 Mar 19 2006 2005-longmont-classes.html
> rw-r—r— 1 ftp ftp 805 Jul 8 2006 2006-longmont-classes.html
С другой стороны, сервер, который я использовал в этом разделе, включал специальные имена, состоящие из точек. Для надежности сценарий должен пропускать эти имена в списке, возвращаемом удаленным сервером, на случай, если он будет взаимодействовать с сервером, включающем их в список (здесь эта проверка обязательна, чтобы избежать попадания в бесконечный рекурсивный цикл!). Подобную осторожность не требуется проявлять при работе со списками содержимого локальных каталогов, потому что функция os.listdir никогда не включает имена «.» и «..» в свои результаты, но положение вещей на «Диком Западе», каким сейчас является Интернет, не является таким же согласованным:
>>> f = FTP(‘learning-python.com’)
>>> f.login(‘lutz’, ‘xxxxxxxx’) # вывод строк опущен
> >> for x in f.nlst()[:5]: print(x) # включает имена . и .. здесь
.hcc.thumbs
2009-public-classes.htm
2010-public-classes.html
> >> L = []
> >> f.dir(L.append) # тот же список, но с подробной информацией
> >> for x in L[:5]: print(x)
drwx—r-x
|
19 5693094
|
450
|
8192
|
May
|
4 10:59 .
|
drwx—r-x
|
19 5693094
|
450
|
8192
|
May
|
4 10:59 ..
|
drwx—
|
2 5693094
|
450
|
4096
|
Feb
|
18 05:38 .hcc.thumbs
|
-rw— r—
|
1 5693094
|
450
|
15824
|
May
|
1 14:39 2009-public-classes.htm
|
-rw— r—
|
1 5693094
|
450
|
18083
|
May
|
4 09:05 2010-public-classes.html
|
Ниже приводится вывод сценария cleanall — он появляется в окне консоли в процессе работы сценария. Добиться того же эффекта можно с помощью команды rm -rf Unix, выполнив ее в окне сеанса SSH или Telnet, но сценарий Python выполняется на стороне клиента и не требует наличия возможности удаленного доступа, кроме поддержки протокола FTP на стороне клиента:
C:\PP4E\Internet\Ftp\Mirror> cleanall.py
Password for lutz on learning-python.com: connecting…
file 2009-public-classes.htm
file 2010-public-classes.html
file Learning-Python-interview.doc
file Python-registration-form-010.pdf
file PythonPoweredSmall.gif
directory _derived
file 2009-public-classes.htm_cmp_DeepBlue100_vbtn.gif
file 2009-public-classes.htm_cmp_DeepBlue100_vbtn_p.gif
file 2010-public-classes.html_cmp_DeepBlue100_vbtn_p.gif
file 2010-public-classes.html_cmp_deepblue100_vbtn.gif directory _vti_cnf
file 2009-public-classes.htm_cmp_DeepBlue100_vbtn.gif
file 2009-public-classes.htm_cmp_DeepBlue100_vbtn_p.gif
file 2010-public-classes.html_cmp_DeepBlue100_vbtn_p.gif
file 2010-public-classes.html_cmp_deepblue100_vbtn.gif directory exited directory exited
…часть строк опущена…
file priorclients.html
file public_classes.htm
file python_conf_ora.gif file topics.html
Done: 366 files and 18 directories cleaned.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011