Skip to content

Instantly share code, notes, and snippets.

@gennad
Created May 30, 2012 14:18
Show Gist options
  • Select an option

  • Save gennad/2836618 to your computer and use it in GitHub Desktop.

Select an option

Save gennad/2836618 to your computer and use it in GitHub Desktop.
Python task
import time
from wsgiref.util import setup_testing_defaults
from wsgiref.simple_server import make_server
def timeit(func):
def wrapper(*a, **k):
t1 = time.time()
it = iter(func(*a, **k))
try:
while True:
yield next(it)
except StopIteration:
pass # Or here
print time.time() - t1 # Only successful finish
return wrapper
@timeit
def app(environ, start_response):
start_response("200 OK", [])
for _ in xrange(1000):
yield "12345" * 10 * 1024 * 1024 # 52 428 800 bytes * 1000 about 50 GB
httpd = make_server('', 8080, app)
print "Serving on port 8080..."
httpd.serve_forever()
@gennad
Copy link
Author

gennad commented Jun 1, 2012

Согласен, с raise ошибся :(

@gennad
Copy link
Author

gennad commented Jun 1, 2012

Хотя нет ...

app в любом случае возвращает объект - итератор (генератор или список).
Если app кинет StopIteration до момента возврата итератора,
wrapper его не будет прятать, потому что он возвращается здесь
it = iter(func(_a, *_k))
и в случае StopIteration, он распростанится дальше.

Если app вернет список, то мы просто будет возвращать по одному объекту за раз.
Здесь да - иы изменяем поведение функции тк мы всегда возвращаем генератор.
Здесь разумно было бы проверить тип возвращаемого объекта после iter() и если это генератор - последовательно возвращать значения, а если что-то другое, то возвращать сразу всё.

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

Возможно, я не прав, но хочется все-таки дойти до истины :)

@temoto
Copy link

temoto commented Jun 1, 2012

Надо сказать, что если вам надоели мои замечания, мы можем остановиться в любой момент, на результаты собеседования это никак не повлияет.

@temoto
Copy link

temoto commented Jun 1, 2012

Похоже, вы чуть выше написали то же самое, что я долго редактировал. :)
Теперь, если приложение-генератор кидает любое исключение, кроме StopIteration, время выполнения будет записано. Строго говоря, это не оговаривалось в задаче, и в боевом коде может оказаться полезным.

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

@temoto
Copy link

temoto commented Jun 1, 2012

Да, можно проверять тип. Но обычно проверка типов это плохая идея и здесь можно обойтись без неё. По спецификации, WSGI приложение должно возвращать итерируемый объект со строками, то есть генератор является наиболее общим интерфейсом для списков и генераторов, поэтому декоратор, который списочные приложения превращает в генераторные, технически, не нарушает WSGI протокол.

Если it = iter(func... вставить внутрь блока try/finally, то поведение для генераторных и списочных приложений станет одинаковым: всегда пишем время работы. Но что, если время нужно писать только в случае успешного завершения?

@gennad
Copy link
Author

gennad commented Jun 1, 2012

Тогда вычисляем в блоке else вместо finally )

@gennad
Copy link
Author

gennad commented Jun 1, 2012

А, нет, StopIeration же не позволит зайти в else... Тогда оставляем except StopIteration и под блоком try вычисляем время

@temoto
Copy link

temoto commented Jun 1, 2012

Правильно ли я понял, except StopIteration: pass вместо finally, а вызов func внутри или снаружи try? Не могли бы вы обновить код, чтобы я запустил тесты?

@gennad
Copy link
Author

gennad commented Jun 1, 2012

Хм ... Может я до конца не понял вопроса .... Вы спросили, как нам вычислить и вывести время если вставить it = iter(func...) в блок try и если нам надо время писать в случае успешного завершения . Если мы заменим finally на except StopIteration, тогда в случае неудачного завершения (вызова любого исключения кроме StopIteration внутри app) блок except wrapper'а не обработает, и распространит дальше по стеку вызовов. В случае удачного завершения (вызов исключения StopIteration), выполнения продолжится в блоке except и мы можем вывести время здесь или за блоком try.
Но тогда мы не сможем выполнить условие из 3 комментария (мы изменяем поведение функции тк любые вызовы StopIteration не относящиеся к нашему итератору, будут ловиться блоком except). Следовательно, мы не можем не изменить поведение функции, если вставим it = iter(func...) в блок try, следовательно оставляем его за блоком...
Если я что-то не понял, перефразируйте пожалуйста вопрос... Код обновил

@temoto
Copy link

temoto commented Jun 1, 2012

Не совсем так. Я сказал, что если it = iter(fun...) вставить внутрь try/finally, то поведение для списочных и генераторных WSGI приложений станет одинаковым: время будет всегда писаться, независимо от ошибок в приложении. Это может быть полезно. Но может оказаться так, что необходимо писать время только в случае успеха. Это просто часть требуемого поведения, независимо от положения и вообще наличия it = iter(fun...) в коде. Я ни в коем случае не прошу как-то поменять определённый код, чтобы он работал другим образом. Интересен только конечный результат, чтобы проходили все тесты.

Ловля исключений по задаче не требуется и в данном случае усложняет код. Не уверен, но возможно получится как-то выкрутиться с проверкой типа результата func(). Но это будет очень сложно и может сломаться на чём-нибудь вроде app -> return MyIterableClass(...). Есть решение, которое удовлетворяет всем требованиям, оно очень простое. Если хотите, покажу. Если что-то не так, извините, не хочу показаться агрессивным или злым, я не умею правильно общаться в таком формате.

@gennad
Copy link
Author

gennad commented Jun 1, 2012

Нет, мне наоборот интересно :)
Простое решение... Хм, может что-то вроде этого:

def wrapper(*a, **k):
  t1 = time.time()
  for i in func(*a, **k):
    yield i
  print time.time() - t1  # Only successful finish

return wrapper

Если это не то, скажите, какой правильный ответ )

@temoto
Copy link

temoto commented Jun 1, 2012

Да-да, это именно оно. Просто, надёжно и отвечает всем требованиям. Если есть требование писать время всегда, независимо от ошибок – весь for оборачивается в try/finally. Очень рад.

@gennad
Copy link
Author

gennad commented Jun 1, 2012

=) спасибо

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment