Центральным механизмом, действующим в примере 16.7, является генерация адресов URL, содержащих номера сообщений и информацию об учетной записи почты. Щелчок на любой из ссылок View (Просмотреть) в списке запускает другой сценарий, который использует информацию в параметрах URL-ссылки для загрузки и вывода выбранного сообщения. Как говорилось в главе 15, поскольку ссылки в списке запрограммированы так, чтобы «уметь» загружать конкретное сообщение, они фактически несут в себе информацию о том, какое действие должно быть следующим. На рис. 16.10 показана часть разметки HTML, генерируемой этим сценарием (чтобы увидеть ее у себя, воспользуйтесь пунктом меню View Source (Исходный код страницы или Просмотр HTML-кода) в своем броузере, — я выбрал пункт меню Save As (Сохранить как) и затем открыл полученный результат в окне просмотра исходного кода в Internet Explorer).
Рис. 16.10. Код разметки HTML списка просмотра, сгенерированный PyMailCGI
Вам все понятно на рис. 16.10? Если вы не сможете прочесть такой код разметки HTML, то сможет ваш броузер. Для читателей с ограниченными возможностями синтаксического анализа ниже приводится отдельно одна из ссылок, в которую добавлено форматирование в виде переносов строк и пробелов, облегчающее понимание:
<tr><th><a href="onViewListLink.py?
pswd=wtGmpsjeb7359& mnum=5&
user=PP4E%40learning-python.com& site=pop.secureserver.net">View</a> <td>Among our weapons are these | Cardinal@hotmail.com
| Fri, 07 May 2010 20:32…
Программа PyMailCGI генерирует относительные минимальные адреса URL (имя сервера и путь определяются по предыдущей странице, если они не были установлены модулем commonhtml). Щелчок на слове «View» в гиперссылке, отображаемой этой разметкой HTML, запускает сценарий onViewListLink, которому передаются все параметры, добавленные в конец URL: имя пользователя POP, номер сообщения POP для письма, ассоциированного со ссылкой, а также пароль POP и данные о сайте. В сценарии, который выполняется следующим, эти значения можно будет получить из объекта, возвращаемого конструктором cgi.FieldStorage. Обратите внимание, что параметр номера сообщения POP mnum отличается для каждой ссылки, потому что щелчок на каждой из них открывает определенное сообщение, и что текст после тега <td> извлекается из заголовков сообщений с помощью пакета mailtools, использующего пакет email.
Модуль commonhtml выполняет экранирование параметров ссылки с помощью модуля urllib.parse, а не cgi.escape, потому что они являются частью URL. Это очевидно только в параметре пароля pswd — его значение зашифровано и может содержать произвольные байты, но urllib.parse дополнительно экранирует небезопасные символы в зашифрованной строке в соответствии с соглашениями об адресах URL (вот откуда берутся все эти последовательности символов %xx). Ничего страшного, если при шифровании получаются странные и даже непечатаемые символы — операция экранирования URL сделает их доступными для передачи. Когда пароль попадет в следующий сценарий, конструктор cgi.FieldStorage выполнит обратное преобразование экранированных последовательностей в адресе URL, заменив в строке с зашифрованным паролем экранированные последовательности %.
Полезно посмотреть, как commonhtml формирует параметры ссылок с информацией о состоянии. Ранее мы узнали, как с помощью функции urllib.parse.quote_plus выполнить экранирование строки перед включением ее в адрес URL:
>>> import urllib.parse
>>> urllib.parse.quote_plus("There’s bugger all down here on Earth") ‘There%27s+bugger+all+down+here+on+Earth’
Однако модуль commonhtml вызывает функцию urllib.parse.urlencode более высокого уровня, транслирующую словарь, состоящий из пар имя:зна че- ние, в законченную строку запроса с параметрами, которую можно добавить в URL после маркера ?. Ниже приводится пример использования функции urlencode в интерактивной оболочке:
>>> parmdict = {‘user’: ‘Brian’,
… ‘pswd’: ‘#!/spam’,
… ‘text’: ‘Say no more, squire!’}
>>> urllib.parse.urlencode(parmdict)
‘text=Say+no+more%2C+squire%21&pswd=%23%21%2Fspam&user=Brian’
>>> "%s?%s" % ("http://scriptname.py", urllib.parse.urlencode(parmdict)) ‘http://scriptname.py?text=Say+no+more%2C+squire%21&pswd=%23%21%2Fspam& user=Brian’
Внутри функция urlencode передает каждое имя и значение из словаря встроенной функции str (чтобы создать из них строки), а затем, при добавлении к результату, пропускает их все через функцию urllib.parse. quote_plus. Сценарий CGI строит список аналогичных словарей и передает его модулю commonhtml для формирования страницы со списком вы- бора.
В общем случае такая генерация URL с параметрами является одним из способов передачи информации о состоянии следующему сценарию (наряду со скрытыми полями форм и базами данных и, обсуждавшимися в главе 15). Без такой информации о состоянии пользователю пришлось бы заново вводить имя, пароль и имя сайта на каждой посещаемой странице.
Между прочим, генерируемый этим сценарием список не сильно отличается по своим возможностям от того, который мы конструировали в программе PyMailGUI в главе 14, а два имеющихся отличия носят исключительно косметический характер. На рис. 16.11 показано, как выглядит тот же список, показанный ранее на рис. 16.8, в графическом интерфейсе почтового клиента.
Важно заметить, что программа PyMailGUI не посылает разметку HTML броузеру, а использует для создания интерфейса пользователя библиотеку tkinter. Она также целиком выполняется на стороне клиента и взаимодействует с почтовыми серверами напрямую, загружая почту с сервера POP на компьютер клиента через сокеты по требованию. Поскольку на протяжении всего сеанса она хранит всю необходимую информацию в памяти, программа PyMailGUI легко может минимизировать количество обращений к почтовому серверу. После начальной загрузки заголовков при следующем запуске ей достаточно будет загрузить заголовки только вновь поступивших сообщений. Кроме того, при удалении писем она может обновлять список сообщений в памяти, не загружая его заново с сервера, и обладает достаточным объемом информации, чтобы проверить соответствие с почтовым ящиком на сервере и выполнить удаление максимально безопасно. Программа PyMailGUI также сохраняет письма, которые уже просматривались, — в течение сеанса работы программы эти письма не требуется загружать повторно.
002 | |
А В С D Е F G | |
Eric.the.Half.a.Bee@yahoo | |
Thu, |
06 |
May |
003 | |
a b с d е f g | |
Eric.the.Half.a.Bee@aol.c | |
Thu, |
06 |
May |
004 | |
test interactive smtplib | |
Thu, |
06 |
May |
|
005 | |
Among our weapons are the | |
Fri, |
07 |
May |
|
006 | |
test pymail2 send | |
Sat, |
08 |
May |
|
*007 | |
testing mailtools package | |
Thu, |
13 |
May |
|
008 | |
Re: Conhe^a mais uma novi | |
Tue, |
01 |
Jun |
|
*009 | |
testing pymailgui send | |
lutz @learning-python.com | |
Thu, |
03 |
Jun |
|*010 | |
Live Organ Transplants! |
Fri, |
04 |
Jun| |
|
011 | |
Re: Live Organ Transplant | |
Fri, |
04 |
Jun |
|
012 | |
Fwd: Live Organ Transplan | |
Fri, |
04 |
Jun |
|
013 | |
Already got one… |
PP4E@ learning—py thou . coni | |
Fri, |
04 |
Jun |
*014 | |
test reply all | |
Sat, |
12 |
Jun |
|
*015 | |
source code line count sh | |
Sun, |
13 |
Jun |
|
*016 | |
hello from PyMailCGI | |
Sat, |
19 |
Jun |
|
<1 |
|
J |
|
|
► |
Pff PyMailGUI 3.0 — pop.secureserver.net |
Quit Г All |
Load Open Write |
View Reply Fwd Save Delete |
PyMailGUI — a Python/tkinter email client (help) |
Рис. 16.11. Тот же самый список сообщений в PyMailGUI
Напротив, PyMailCGI выполняется на веб-сервере и просто отображает текст почты в броузере клиента — почта загружается с почтового сервера POP на веб-сервер, где выполняются сценарии CGI. Из-за автономной природы сценариев CGI приложение PyMailCGI не имеет автоматической памяти для сохранения информации между вызовами страниц, и на протяжении одного и того же сеанса ей может потребоваться повторно загружать заголовки и ранее просматривавшиеся сообщения. У этих архитектурных различий есть некоторые важные следствия, которые мы обсудим несколько позже.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011