Вы находитесь на странице: 1из 16

11¾ "


"(

2  7 


ǮȒȇȈȄȒȑȌȕȎȄȋȄȏȌ
ȓȒȈȒȊȈȄȖȠȣȑȉȋȑȄȏ
țȖȒȒȑȌȌȐȉȏȌȆȆȌȈȗ«

W !!$($
(!( 


  %)

 0 * % 3 "_ 
0
9

% 
: %%(bc %*)0
!
 6 
*(
 
  
,
 0 %"=
   ( & 0% bc %*)0
 ! !
 &*
  "d 2  0 
*
 
  
 !  ! + (
."

0%   

*

 
   2 

0 !  "      
#

N( $  497


*  

Ожидание: что делать?


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

ǮȄȎȆȕȉȇȈȄȆȕȉȋȄȆȌȕȌȖ
dzȒȕȏȗȜȄȍȈȔȗȇ ȒȖȖȒȇȒțȖȒȆȟȓȟȖȄȉȖȉȕȠ
ȉȕȏȌȓȔȌșȒȈȌȖȕȣȊȈȄȖȠ ǰȒȊȉȖȅȟȖȠȒȊȌȈȄȑȌȉ ȕȈȉȏȄȖȠȌȎȄȎȒȉȒȖȑȒȜȉȑȌȉ
ȖȗȖȑȌțȉȇȒȑȉȓȒȈȉȏȄȉȜȠ ȓȔȌȋȄȓȌȕȌȒȖȏȌțȄȉȖȕȣ ȓȒȏȠȋȒȆȄȖȉȏȣșȒȖȌȖȉ
ȖȟȊȈȉȜȠ« ȒȖȒȊȌȈȄȑȌȣȓȔȌțȖȉȑȌȌ" ȓȒȏȗțȌȖȠ«

Может быть, это тот случай, когда ожидание при записи отличается
от ожидания при чтении, особенно если учесть, как работает наше
веб-приложение?
Посмотрим еще раз на SQL-запросы в log_request и view_the_log,
чтобы увидеть, как они используются.

498  ›
$
##

Как выполняются запросы к базе данных?


В функции log_request мы использовали SQL-инструкцию INSERT,
чтобы сохранить детали веб-запроса в базе данных. Когда вызывается
log_request, она ждет, пока cursor.execute выполнит INSERT.

def log_request(req: 'flask_request', res: str) -> None:


with UseDatabase(app.config['dbconfig']) as cursor:
_SQL = """insert into log
(phrase, letters, ip, browser_string, results)
values
(%s, %s, %s, %s, %s)"""

qs

cursor.execute(_SQL, (req.form['phrase'],
req.form['letters'], #

 *
" 
    req.remote_addr,


  req.user_agent.browser,
»
«
   res, ))

  Код, ожидающий завершения

 
 чего-нибудь внешнего,


    называется «блокирующим
кодом» — выполнение вашей
То же происходит в функции view_the_log, которая также ждет, программы блокируется,
пока выполняется SQL-запрос SELECT. пока ожидание не закончится.
Как правило, если
блокирующему коду требуется
@app.route('/viewlog') много времени — это плохо.
@check_logged_in
def view_the_log() -> 'html':
try:
with UseDatabase(app.config['dbconfig']) as cursor:
_SQL = """select phrase, letters, ip, browser_string, results
from log"""
w  cursor.execute(_SQL)
 *
   contents = cursor.fetchall()
  titles = ('Phrase', 'Letters', 'Remote_addr', 'User_agent', 'Results')
«
  » return render_template('viewlog.html',
  the_title='View Log',
 the_row_titles=titles,
the_data=contents,)
except ConnectionError as err:

$  
... ‹s $$   
˜»z
  «¥F=…UN/=UGC   


†   v

Обе функции — блокирующие. Однако посмотрим внимательнее, что


в них происходит после вызова cursor.execute. Вызов cursor.
execute в log_request — это последнее, что она делает, тогда
как view_the_log обрабатывает результаты cursor.execute
в оставшейся части функции.
Давайте рассмотрим последствия этого различия.

!  iVV
‹UEC\S
select

Инструкции INSERT и SELECT базы данных отличаются


Если, прочитав заголовок, вы подумали: «Конечно же, они отличаются!», будьте
уверены: мы (ближе к концу книги) не сошли с ума.
Да, SQL-инструкция INSERT отличается от SQL-инструкции SELECT. Но, учитывая
особенности использования запросов, в log_request не нужно блокировать работу
приложения на время выполнения INSERT, а view_the_log должна дождаться
результатов SELECT. Именно поэтому запросы очень различаются.
Это основное наблюдение.
Если view_the_log не дождется, пока SELECT вернет записи из базы данных,
то код, следующий за cursor.execute, по всей видимости, завершится с ошибкой
(поскольку не получит данные для обработки). Функция view_the_log должна быть
блокирующей, поскольку она должна дождаться данных, прежде чем продолжит работу.
Вызывая log_request, веб-приложение просто хочет, чтобы функция записала детали
текущего веб-запроса в базу данных. Для него не важно, когда именно это случится;
важно, что это вообще будет сделано. Функция log_request ничего не возвращает;
вызывающему коду нет необходимости ждать ответа. Для вызывающего кода важно,
только чтобы веб-запрос в конечном счете был записан в журнал.
В связи с этим возникает вопрос: зачем log_request заставляет вызывающий код
ждать?

ǦȟȈȗȐȄȉȖȉțȖȒȎȒȈ
©ORJBUHTXHVWªȐȒȊȑȒ
ȎȄȎȌȐȖȒȒȅȔȄȋȒȐȋȄȓȗȕȖȌȖȠ
ȓȄȔȄȏȏȉȏȠȑȒȕȎȒȈȒȐ
ȆȉȅȓȔȌȏȒȊȉȑȌȣ"

J C =    ;    C


Когда пользователи веб-приложения вводят
данные для нового поиска, их меньше всего
заботит факт журналирования деталей запроса
в какой-то базе данных, так что давайте не будем
заставлять их ждать, пока веб-приложение
делает эту работу.
Вместо этого организуем другой процесс,
который в конечном счете будет производить
журналирование независимо от основной
функции веб-приложения (поиска букв в фразе).

500  ›
$
##

Делаем несколько дел сразу


Вот план: организовать выполнение функции log_request независимо от основной функции
веб-приложения. Для этого придется изменить код веб-приложения так, чтобы каждый вызов
log_request запускался параллельно. И тогда веб-приложению больше не надо будет ждать
завершения log_request перед обслуживанием другого запроса от другого пользователя (то есть
больше никаких задержек).
Выполнится log_request мгновенно или потребуется несколько секунд, минута, даже часы —
нашему веб-приложению все равно (как и всем нашим пользователям). Главное, чтобы этот код
в конечном счете выполнился.

Параллельно выполняющийся код: есть варианты


Python поддерживает несколько способов параллельного
выполнения некоторого кода в приложении. Помимо множества Полный список
сторонних модулей, стандартная библиотека также включает (и подробное
некоторые механизмы, которые здесь пригодятся.
описание) средств
Среди них наиболее известной является библиотека threading, поддержки
которая имеет высокоуровневый интерфейс к реализации
многопоточности в операционной системе, где выполняется веб- параллельного
приложение. Для использования библиотеки достаточно просто выполнения
импортировать класс Thread из модуля threading в начале вашей
программы.
в стандартной
библиотеке Python
from threading import Thread
можно найти
по адресу: https://
Следуйте за нами и добавьте эту строку кода в начало файла
vsearch4web.py. docs.python.
Сейчас начнется веселье.
org/3/library/
Чтобы получить новый поток выполнения, нужно создать объект
concurrency.html
Thread, передав в аргументе target имя функции для запуска в этом
потоке и все необходимые ей аргументы в виде кортежа в другом
именованном аргументе args. Созданный объект Thread затем
присваивается выбранной вами переменной.
В качестве примера предположим, что у нас есть функция с именем execute_
slowly, которая принимает три аргумента (допустим, это три числа). Код,
который вызывает execute_slowly, присваивает три значения переменным,
называемым glacial, plodding и leaden. Вот как происходит нормальный
вызов execute_slowly (то есть без всякого параллелизма):

execute_slowly(glacial, plodding, leaden)

Если execute_slowly потребуется 30 секунд, чтобы сделать то, что она должна сделать, вызывающий
код заблокируется и будет ждать 30 секунд, прежде чем сделает что-нибудь другое. Печально.

!  Z\[
*8


Не грустим: используем потоки


В глобальном масштабе 30 секунд ожидания до завершения функции
execute_slowly — это не конец света. Но если пользователь сидит
и ждет, он может решить, что, возможно, что-то пошло не так.
Если логика приложения позволяет продолжить работу, пока execute_
slowly занимается своими делами, можно создать параллельный поток
выполнения — экземпляр Thread — и запустить в нем execute_slowly.


Здесь еще раз показан нормальный вызов функции и код, запускающий

функцию в параллельном потоке выполнения. (  

execute_slowly(glacial, plodding, leaden)

#$
 $


$

from threading import Thread

...

t = Thread(target=execute_slowly, args=(glacial, plodding, leaden))

 $
£ 
«] /:=;ˆ» 

 †(  †


 
  
 $ 

Принятый порядок использования Thread выглядит немного странно,


но в действительности это не так. Для понимания происходящего важно отметить,
что объект Thread присваивается переменной (t в этом примере), а функции
execute_slowly еще только предстоит выполниться.
Присваивание объекта Thread переменной t подготавливает его к выполнению.
Чтобы потребовать от механизма многопоточности в Python выполнить
execute_slowly, нужно запустить поток.

«start»$

z $ 
  

 
«threading»

t.start()  $«t»
(  † †

После этого код, вызвавший t.start, продолжит выполняться как обычно. 30-секундная
задержка в execute_slowly не окажет никакого эффекта на вызывающий код, поскольку
дальше execute_slowly выполняется уже под управлением модуля threading. Модуль
threading сам договаривается с интерпретатором Python, когда в конечном счете
выполнить execute_slowly.

502  ›
$
##

Заточите карандаш
Теперь вернемся к вызову log_request. В веб-приложении этот
вызов производится только в одном месте: в функции do_search.
Как вы помните, мы уже заключили вызов log_request в try/
except для защиты от неожиданных ошибок времени выполнения.
Заметьте также, что мы добавили 15-секундную задержку —
используя sleep(15) — в код log_request (замедлив его). Вот
как выглядит код do_search сейчас:

@app.route('/search4', methods=['POST'])
def do_search() -> 'html':
phrase = request.form['phrase']
letters = request.form['letters']
title = 'Here are your results:'
results = str(search4letters(phrase, letters))
x   try:
  log_request(request, results)
«GC˜U:=µP=<N» except Exception as err:
print('***** Logging failed with this error:',
str(err))
return render_template('results.html',
the_title=title,
the_phrase=phrase,
the_letters=letters,
the_results=results,)

Мы предполагаем, что вы уже добавили инструкцию from


threading import Thread в начало веб-приложения.
Возьмите карандаш и впишите в строки ниже код, который нужно
вставить в do_search вместо стандартного вызова log_request.

!  
 Помните: вы должны запустить log_request с помощью объекта
 $   
Thread, как в примере с execute_slowly на предыдущей

 странице.

«GC˜U:=µP=<N»

!  Z\h
J 

Заточите карандаш
Решение Мы вернулись к вызову log_request. В веб-приложении этот
вызов производится только в одном месте: в функции do_search.
Как вы помните, мы уже заключили вызов log_request в try/
except для защиты от неожиданных ошибок времени выполнения.
Заметьте также, что мы добавили 15-секундную задержку —
используя sleep(15) — в код log_request (замедлив его). Вот
как выглядит код do_search сейчас:

@app.route('/search4', methods=['POST'])
def do_search() -> 'html':
phrase = request.form['phrase']
letters = request.form['letters']
title = 'Here are your results:'
results = str(search4letters(phrase, letters))
x  
try:
 
log_request(request, results)
«GC˜U:=µP=<N»
except Exception as err:
print('***** Logging failed with this error:',
str(err))
return render_template('results.html',
the_title=title,
the_phrase=phrase,
the_letters=letters,
the_results=results,)

Мы предположили, что вы уже добавили инструкцию from


threading import Thread в начало веб-приложения.
В пропуски ниже вы должны были вписать код, который нужно
было вставить в do_search вместо стандартного вызова
log_request.
Š $ Вам нужно было запустить log_request с помощью объекта
 †«N:}»
Thread, как в примере с execute_slowly.
( )

N:}%

N¬]/:=;ˆ(N;:˜=N¬GC˜U:=µP=<N;:˜<¬(:=µP=<N:=<PGN<))

 $
N<N;:N() z $    
)
 «except»
 †(  †
   
   =©K=.N    $ $      
$     †      $
s$ $  
  
 
   $

504  ›
$
##

¢ÃÁ´À³ÒÂÁ¸º·½³
I# #.%vsearch4web.py>    9  6
Y%
.8 9 > #  8D >  
3 log_request8   9 9   
threading4

   8 

Y. F &;!Mn(8D >.9  



I > 9  %9threading  log_request
>  . 3 8. 9.]C4

>89 # 983.#68 4 $7 


.997  ]C  $8. F8D > 
8F88#

$ 
s 


v    
...
127.0.0.1 - - [29/Jul/2016 19:43:31] "POST /search4 HTTP/1.1" 200 -
Exception in thread Thread-6:
Traceback (most recent call last):
File "vsearch4web.not.slow.with.threads.but.broken.py", line 42, in log_request
cursor.execute(_SQL, (req.form['phrase'],
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/
werkzeug/local.py", line 343, in __getattr__
...
raise RuntimeError(_request_ctx_err_msg) ‡Ž     
RuntimeError: Working outside of request context.

†  
This typically means that you attempted to use functionality that needed
{v  an active HTTP request. Consult the documentation on testing for
!! information about how to avoid this problem.
v 
During handling of the above exception, another exception occurred:
 
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/threading.py",
line 914, in _bootstrap_inner
self.run()
...
RuntimeError: Working outside of request context. # v Ž

This typically means that you attempted to use functionality that needed
an active HTTP request. Consult the documentation on testing for
information about how to avoid this problem.

Q.$ 98.# >.  9  '28D. 8


. 
Y 8F >  > 9  9threading 

?> 8F%    . %$ 6 
threading.py   $ 8F%   #
werkzeugflask
Y    88   
 
3
!
!)-,
$)
o > .X

!  Z\Z
- (.

В первую очередь: не паниковать


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

...
This typically means that you attempted to use functionality that needed
an active HTTP request. Consult the documentation on testing for
information about how to avoid this problem.
...
 
s


Это сообщение исходит не от модуля threading, а от Flask. Мы 

 †v †
знаем это, потому что модулю threading все равно, он определенно (  †
   \]]|*
не интересуется тем, что вы пытались что-то делать с HTTP. 

   
‹
 
$
Давайте заглянем в код, который должен выполняться в отдельном  s
    $  
потоке и, как мы знаем, требует 15 секунд для работы, поскольку именно v
†
столько времени работает log_request. Пока вы рассматриваете этот  
код, подумайте, что могло случиться в течение этих 15 секунд?

@app.route('/search4', methods=['POST'])
def do_search() -> 'html':
phrase = request.form['phrase']
letters = request.form['letters']
title = 'Here are your results:'
results = str(search4letters(phrase, letters))
try:
t = Thread(target=log_request, args=(request, results))
t.start()
except Exception as err:
‹
 print('***** Logging failed with this error:', str(err))
?J  return render_template('results.html',
   the_title=title,

  the_phrase=phrase,
s    ~ the_letters=letters,
the_results=results,)

Поток немедленно будет запланирован для выполнения и вызывающий код


(функция do_search) продолжит работу. Далее выполнится функция render_
template (в мгновение ока) и функция do_search завершится.
Когда do_search завершается, все данные, связанные с функцией (ее контекст).
удаляются интерпретатором. Переменные request, phrase, letters,
title и results прекращают существование. Однако переменные request
и results переданы в качестве аргументов в вызов log_request, которая
попытается обратиться к ним 15 секунд спустя. К сожалению, к тому времени
переменные будут удалены, так как do_search завершилась. Печально.

506  ›
$
##

Не грустим: Flask нам поможет


Опираясь на новые знания, можно заключить, что функция log_request
(когда выполняется в отдельном потоке) не «видит» своих аргументов.
Это произошло из-за того, что интерпретатор уже давно прибрал
за собой и очистил память, занимаемую этими переменными (поскольку
do_search завершилась). В частности, объект request больше
не активен, и попытка функции log_request найти его терпит неудачу.
Итак, что можно сделать? Не волнуйтесь: помощь близко.

ǦȟȇȒȆȒȔȌȖȉțȖȒȓȔȌȈȉȖȕȣȓȉȔȉȓȌȕȄȖȠ
ȘȗȑȎȚȌȢ©ORJBUHTXHVWª"ȃȋȄȓȏȄȑȌȔȗȢ
ȡȖȒȑȄȕȏȉȈȗȢȝȗȢȑȉȈȉȏȢǹȒȔȒȜȒ"

       
6   C
На первый взгляд может показаться, что нам нужно
переписать log_request, чтобы в меньшей мере полагаться
на его аргументы… подразумевая, что это даже возможно.
Но оказывается, в фреймворке Flask есть декоратор,
который может помочь в этом случае.
Декоратор copy_current_request_context гарантирует,
что HTTP-запрос, который активен в момент вызова
функции, останется активным, когда функция позже
запустится в отдельном потоке. Чтобы воспользоваться им,
нужно добавить copy_current_request_context в список
импорта в начале веб-приложения.
Как любой другой декоратор, он применяется
к существующей функции с использованием обычного
@-синтаксиса. Но будьте внимательны: декорируемая
функция должна быть определена внутри функции, которая
ее вызывает; то есть она должна быть вложена в вызывающую
ее функцию.

?#   8 6F3 8   .B0*1m4

Упражнение ]
 I '26log_request 9'2do_search

_
 % log_request F96@copy_current_request_
context

w
 I9  .8 86 %&I8%
.(

!  Z\a
*(



?  96F@


]
 I '26log_request  9'2do_search

Упражнение
Решение _
  9log_request F96@copy_current_request_
context

w
 V8 9  8 86 %&I8% .(   

 $ do_search  $    ]_38  


@  w8> 6F% 24

@app.route('/search4', methods=['POST']) w @  


def do_search() -> 'html': «GC˜U:=µP=<N»

 
@copy_current_request_context
def log_request(req: 'flask_request', res: str) -> None:
sleep(15) # This makes log_request really slow...
with UseDatabase(app.config['dbconfig']) as cursor:
w ? _SQL = """insert into log
&   (phrase, letters, ip, browser_string, results)
«GC˜U:=µP=<N» values
  (%s, %s, %s, %s, %s)"""
 
 cursor.execute(_SQL, (req.form['phrase'],
(
 ) req.form['letters'],
  req.remote_addr,
(  «ˆCU req.user_agent.browser,
search» res, ))

phrase = request.form['phrase']
letters = request.form['letters']
title = 'Here are your results:'
results = str(search4letters(phrase, letters))
q  try:

 t = Thread(target=log_request, args=(request, results))
  t.start()
$   except Exception as err:
print('***** Logging failed with this error:', str(err))
return render_template('results.html',
the_title=title,
the_phrase=phrase,
the_letters=letters,
the_results=results,)

;

?
 
=>!#",!+,#& @"@ *+&
DfkcElBEM 
#*
!

,
!
@*1 c <EhmEC 
>   988  98 log_request    tryEexcept
.  > .     
H% >  9. 9%       
  9tryEexceptdo_search

508  ›
$
##

¢ÃÁ´À³ÒÂÁ¸º·½³
Uw
H 8 %vsearch4web.py  >  8
 . F%&I8% .(  9 
H  
$. F8D >   >  #

...
127.0.0.1 - - [30/Jul/2016 20:42:46] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [30/Jul/2016 20:43:10] "POST /search4 HTTP/1.1" 200 -
127.0.0.1 - - [30/Jul/2016 20:43:14] "GET /login HTTP/1.1" 200 -
127.0.0.1 - - [30/Jul/2016 20:43:17] "GET /viewlog HTTP/1.1" 200 -
127.0.0.1 - - [30/Jul/2016 20:43:37] "GET /viewlog HTTP/1.1" 200 -

Š 
†  $ 

 
 
q s«200»
† *

  $ #
  ?J 

  

 *
    
  

 {




§

ǵȒȇȏȄȕȑȒȎȄȔȖȒțȎȉȣȐȒȇȗ
ȋȄȈȄȖȠȓȒȕȏȉȈȑȌȍȆȒȓȔȒȕǩȕȖȠȏȌ
ȑȉȈȒȕȖȄȖȎȌȆȖȒȐțȖȒȐȟȒȓȔȉȈȉȏȌȏȌ
©ORJBUHTXHVWªȆȑȗȖȔȌ©GRBVHDUFKª"

C    ; 
C
В этом веб-приложении функция log_
request всегда вызывалась только внутри
do_search, так что перенос log_request
внутрь do_search не является проблемой.
Если позднее вы решите вызывать log_
request из какой-то другой функции, это
может стать проблемой (и вам придется
кое-что переосмыслить). Но прямо сейчас
вы великолепны.

!  Z\V
 
"

Надежно ли ваше веб-приложение сейчас?


Вот четыре вопроса, сформулированные в начале главы 11.

1 ǻȖȒȕȏȗțȌȖȕȣȉȕȏȌȓȒȓȟȖȎȄȓȒȈȎȏȢțȉȑȌȣȎȅȄȋȉȈȄȑȑȟșȓȒȖȉȔȓȌȖȑȉȗȈȄțȗ"

2 ǫȄȝȌȝȉȑȒȏȌȑȄȜȉȆȉȅȓȔȌȏȒȊȉȑȌȉȒȖȆȉȅȄȖȄȎ"

3 ǻȖȒȓȔȒȌȋȒȍȈȉȖȉȕȏȌțȖȒȑȌȅȗȈȠȓȒȖȔȉȅȗȉȖȐȑȒȇȒȆȔȉȐȉȑȌ"

4 ǻȖȒȕȏȗțȌȖȕȣȉȕȏȌȆȟȋȒȆȘȗȑȎȚȌȌȓȒȖȉȔȓȌȖȑȉȗȈȄțȗ"

Сейчас наше веб-приложение обрабатывает множество исключений времени


выполнения, благодаря использованию try/except, а также некоторые
пользовательские исключения, которые можно возбуждать и перехватывать
по мере необходимости.
Если известно, что во время выполнения что-нибудь может пойти не так,
защитите свой код от любых исключений, которые могут случиться. Это улучшит
общую надежность вашего приложения, что очень хорошо.
Обратите внимание, что в приложении еще остался код, надежность которого
можно улучшить. Мы потратили много времени, добавляя код try/except
в функцию view_the_log, которая использует диспетчера контекста
UseDatabase. Но UseDatabase также используется внутри log_request, и ее
тоже следовало бы защитить (оставляем это вам в качестве упражнения для
домашней работы).
Наше веб-приложение стало реагировать быстрее, благодаря переносу
в параллельный поток задачи, которая должна быть выполнена,
но необязательно сразу. Это удачное архитектурное решение, но будьте
осторожны и не злоупотребляйте потоками: пример организации
многопоточного выполнения в этой главе очень прост. Однако очень легко
создать многопоточный код, который никто не сможет понять и который будет
сводить вас с ума во время отладки. Используйте потоки с осторожностью.
Теперь вернемся к вопросу 3: что случится, если что-нибудь потребует много времени?
Использование потоков улучшает производительность записи в базу данных,
но не чтения. Это тот случай, когда не остается ничего иного, кроме как ждать
получения данных после отправки запроса, сколько бы времени это ни заняло,
так как без этих данных веб-приложение не способно продолжить работу.
Чтобы ускорить чтение из базы данных (если оно на самом деле выполняется
медленно), можно подумать об использовании альтернативных (более быстрых)
баз данных. Но это совсем другая тема, и мы не будем ее рассматривать.
Тем не менее в следующей, последней главе мы исследуем проблемы
производительности, но сделаем это в процессе обсуждения итераций — темы,
которую все понимают и которую мы уже обсуждали.

510  ›
$
##




Код из главы 11¾, 1 из 2 «¥<=;:K/g…=X.}»
 

from flask import Flask, render_template, request, escape, session


from flask import copy_current_request_context
from vsearch import search4letters

from DBcm import UseDatabase, ConnectionError, CredentialsError, SQLError


from checker import check_logged_in

from threading import Thread


from time import sleep

app = Flask(__name__)

app.config['dbconfig'] = {'host': '127.0.0.1',


'user': 'vsearch',
'password': 'vsearchpasswd',
'database': 'vsearchlogDB', }

@app.route('/login')
def do_login() -> str:
session['logged_in'] = True
return 'You are now logged in.'

@app.route('/logout')
def do_logout() -> str:
session.pop('logged_in')
return 'You are now logged out.'

@app.route('/search4', methods=['POST'])
def do_search() -> 'html':

@copy_current_request_context
def log_request(req: 'flask_request', res: str) -> None:
sleep(15) # This makes log_request really slow...
with UseDatabase(app.config['dbconfig']) as cursor:
_SQL = """insert into log
(phrase, letters, ip, browser_string, results)
values
(%s, %s, %s, %s, %s)"""
cursor.execute(_SQL, (req.form['phrase'],
req.form['letters'],
req.remote_addr,
req.user_agent.browser,
res, ))

phrase = request.form['phrase']

letters = request.form['letters']
«ˆCU<=;:K/»“
title = 'Here are your results:' 


 †v 


!  Z[[


Код из главы 11¾, 2 из 2


results = str(search4letters(phrase, letters))
try:
t = Thread(target=log_request, args=(request, results))
t.start()
except Exception as err:
print('***** Logging failed with this error:', str(err))
return render_template('results.html',
the_title=title,
the_phrase=phrase,
the_letters=letters,
the_results=results,) 

@app.route('/') (  
@app.route('/entry') «ˆCU<=;:K/»
def entry_page() -> 'html':
return render_template('entry.html',
the_title='Welcome to search4letters on the web!')

@app.route('/viewlog')
@check_logged_in
def view_the_log() -> 'html':
try:
with UseDatabase(app.config['dbconfig']) as cursor:
_SQL = """select phrase, letters, ip, browser_string, results
from log"""
cursor.execute(_SQL)
contents = cursor.fetchall()
# raise Exception("Some unknown exception.")
titles = ('Phrase', 'Letters', 'Remote_addr', 'User_agent', 'Results')
return render_template('viewlog.html',
the_title='View Log',
the_row_titles=titles,
the_data=contents,)
except ConnectionError as err:
print('Is your database switched on? Error:', str(err))
except CredentialsError as err:
print('User-id/Password issues. Error:', str(err))
except SQLError as err:
print('Is your query correct? Error:', str(err))
except Exception as err:
print('Something went wrong:', str(err))
return 'Error'

app.secret_key = 'YouWillNeverGuessMySecretKey'

if __name__ == '__main__':
app.run(debug=True)

512  ›