Версия сценариев загрузки и выгрузки на основе функций

versiya scenariev zagruzki i vygruzki na osnove funkcij Сценарии на стороне клиента

Оригинальные версии сценариев загрузки и выгрузки включают программный код верхнего уровня, который опирается на глобальные переменные. Такая структура плохо поддается повторному использованию — программный код выполняется немедленно, на этапе импортирования, и его сложно обобщить для применения в различных контекстах. Хуже того, его сложно сопровождать — всякий раз, щелкая на кнопке Paste (Копировать), чтобы скопировать фрагмент существующего программного кода, вы увеличиваете сложность внесения изменений в будущем.

Чтобы показать более удачное решение, в примере 13.12 демонстрируется один из способов ре фак торин га (реорганизации) сценария загрузки. Благодаря обертыванию различных частей в функции они становятся доступными для повторного использования в других модулях, включая и программу выгрузки.

Пример 13.12. PP4E\Internet\Ftp\Mirror\downloadflat_modular.py

#!/bin/env python """

############################################################################ использует протокол FTP для копирования (загрузки) всех файлов из каталога на удаленном сайте в каталог на локальном компьютере; эта версия действует точно так же, но была реорганизована с целью завернуть фрагменты программного кода в функции, чтобы их можно было повторно использовать в сценарии выгрузки каталога и, возможно, в других программах в будущем — в противном случае избыточность программного кода может с течением времени привести к появлению различий в изначально одинаковых фрагментах и усложнит сопровождение.

############################################################################

import os, sys, ftplib

from getpass import getpass

from mimetypes import guess_type, add_type

defaultSite = ‘home.rmi.net’

defaultRdir = ‘.’

defaultUser = ‘lutz’

def configTransfer(site=defaultSite, rdir=defaultRdir, user=defaultUser)

принимает параметры выгрузки или загрузки

из-за большого количества параметров использует класс class cf: pass

cf.nonpassive = False # пассивный режим FTP, по умолчанию в 2.1+

cf.remotesite = site # удаленный сайт куда/откуда выполняется передача

cf.remotedir = rdir # и каталог (‘.’ означает корень учетной записи)

cf.remoteuser = user

cf.localdir = (len(sys.argv) > 1 and sys.argv[1]) or ‘.’

cf.cleanall = input(‘Clean target directory first? ‘)[:1] in [‘y’,’Y’] cf.remotepass = getpass(

‘Password for %s on %s:’ % (cf.remoteuser, cf.remotesite)) return cf

def isTextKind(remotename, trace=True)

использует mimetype для определения принадлежности файла к текстовому или двоичному типу

f.htmlопределяется как (‘text/html‘, None): текст

f.jpegопределяется как (‘image/jpeg‘, None): двоичный

f.txt.gzопределяется как (‘text/plain‘, ‘gzip‘): двоичный

файлы с неизвестными расширениями определяются как (None, None): двоичные

модуль mimetype способен также строить предположения об именах исходя из типа: смотрите пример PyMailGUI

add_type(‘text/xpythonwin‘, ‘.pyw‘) # отсутствует в таблицах

mimetype,encoding = guess_type(remotename,strict=False) # разрешить # расширенную интерпретацию

mimetype = mimetype or ‘?/?’ # тип неизвестен?

maintype = mimetype.split(‘/’)[0] # получить первый элемент

if trace: print(maintype, encoding or »)

return maintype == ‘text’ and encoding == None # не сжатый

def connectFtp(cf):

print(‘connecting…’)

connection = ftplib.FTP(cf.remotesite) # соединиться с FTPсайтом connection.login(cf.remoteuser, cf.remotepass) # зарегистрироваться connection.cwd(cf.remotedir) # перейти в каталог

if cf.nonpassive: # переход в активный режим FTP при необходимости

connection.set_pasv(False) # большинство работают в пассивном режиме return connection

def cleanLocals(cf):

пытается удалить все локальные файлы, чтобы убрать устаревшие копии if cf.cleanall:

for localname in os.listdir(cf.localdir): # список локальных файлов try: # удаление локального файла

print(‘deleting local’, localname)

os.remove(os.path.join(cf.localdir, localname))

except: print(‘cannot delete local’, localname)

def downloadAll(cf, connection):

загружает все файлы из удаленного каталога в соответствии с настройками в cf; метод nlst() возвращает список файлов, dir() — полный список с дополнительными подробностями

remotefiles = connection.nlst() # nlst список файлов на сервере

for remotename in remotefiles:

if remotename in (‘.’, ‘..’): continue

localpath = os.path.join(cf.localdir, remotename)

print(‘downloading’, remotename, ‘to’, localpath, ‘as’, end=’ ‘)

if isTextKind(remotename):

#   использовать текстовый режим передачи

localfile = open(localpath, ‘w’, encoding=connection.encoding)

def callback(line): localfile.write(line + ‘\n’) connection.retrlines(‘RETR ‘ + remotename, callback) else:

#   использовать двоичный режим передачи

localfile = open(localpath, ‘wb’)

connection.retrbinary(‘RETR ‘ + remotename, localfile.write) localfile.close()

connection.quit()

print(‘Done:’, len(remotefiles), ‘files downloaded.’)

if __name__ == ‘__main__’:

cf = configTransfer()

conn = connectFtp(cf)

cleanLocals(cf) # не улалять файлы, если соединение не было установлено downloadAll(cf, conn)

Сравните эту версию с оригиналом. Этот сценарий и все остальные в этом разделе действуют точно так же, как и оригинальные программы загрузки и выгрузки плоского каталога. Хотя мы и не изменили поведение сценария, тем не менее, мы радикально изменили его структуру — его реализация теперь представляет собой комплект инструментов, которые можно импортировать и повторно использовать в других программах.

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

Пример 13.13. PP4E\Internet\Ftp\Mirror\uploadflat_modular.py

#!/bin/env python

############################################################################ использует FTP для выгрузки всех файлов из локального каталога на удаленный сайт/каталог; эта версия повторно использует функции из сценария загрузки, чтобы избежать избыточности программного кода;

############################################################################

import os

from downloadflat_modular import configTransfer, connectFtp, isTextKind

def cleanRemotes(cf, connection):

пытается сначала удалить все файлы в каталоге на сервере, чтобы ликвидировать устаревшие копии if cf.cleanall:

for remotename in connection.nlst(): # список файлов на сервере

try: # удаление файла на сервере

print(‘deleting remote‘,remotename) # пропустить . и ..

connection.delete(remotename)

except:

print(‘cannot delete remote’, remotename)

def uploadAll(cf, connection)

выгружает все файлы в каталог на сервере в соответствии с настройками cf listdir() отбрасывает пути к каталогам, любые ошибки завершают сценарий localfiles = os.listdir(cf.localdir) # listdir — список локальных файлов for localname in localfiles:

localpath = os.path.join(cf.localdir, localname) print(‘uploading’, localpath, ‘to’, localname, ‘as’, end=’ ‘) if isTextKind(localname):

#  использовать текстовый режим передачи localfile = open(localpath, ‘rb’) connection.storlines(‘STOR ‘ + localname, localfile) else:

#  использовать двоичный режим передачи localfile = open(localpath, ‘rb’) connection.storbinary(‘STOR ‘ + localname, localfile) localfile.close()

connection.quit()

print(‘Done:’, len(localfiles), ‘files uploaded.’)

if __name__ == ‘__main__’:

cf = configTransfer(site=’learning-python.com’, rdir=’books’, user=’lutz’)

conn = connectFtp(cf) cleanRemotes(cf, conn) uploadAll(cf, conn)

Благодаря повторному использованию программного кода сценарий выгрузки не только стал заметно проще, но он также будет наследовать все изменения, которые будут выполняться в модуле загрузки. Например, функция isTextKind позднее была дополнена программным кодом, который добавляет расширение .pyw в таблицы mimetypes (по умолчанию тип этих файлов не распознается), — поскольку теперь это совместно используемая функция, изменения автоматически будут унаследованы и программой выгрузки.

Этот сценарий и программы, которые будут его импортировать, достигает тех же целей, что и оригинал, но простота обслуживания, которую они приобретают, имеет большое значение в мире разработки программного обеспечения. Ниже приводится пример загрузки сайта с одного сервера и выгрузки его на другой сервер:

C:\\PP4E\Internet\Ftp\Mirror> python downloadflat_modular.py test

Clean target directory first?

Password for lutz on home.rmi.net:

connecting

downloading 2004-longmont-classes.html to test\2004-longmont-classes.html as text

…часть строк опущена…

downloading relo-feb010-index.html to test\relo-feb010-index.html as text

Done: 297 files downloaded.

C:\\PP4E\Internet\Ftp\Mirror> python uploadflat_modular.py test Clean target directory first?

Password for lutz on learning-python.com: connecting

uploading test\2004-longmont-classes.html to 2004-longmont-classes.html as text

…часть строк опущена…

uploading test\zopeoutline.htm to zopeoutline.htm as text Done: 297 files uploaded.

Использованная литература:

Марк Лутц — Программирование на Python, 4-е издание, II том, 2011

Каталог сайтов Всего.ру
Оцените статью
Секреты программирования
Добавить комментарий