-
-
Save gennad/2836618 to your computer and use it in GitHub Desktop.
| 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() |
Тогда можно его снова поднять (обновил) как альтернатива - подсчитывать в finally
Думаю в finally даже лучше
Обновил
А теперь после первого запроса httpd завершится с этим исключением даже если app его не кидала. Думаю, что тесты, которые я вам предложил в самом начале смогли бы поймать эту ошибку. (этот комментарий не относится к варианту с finally)
Согласен, с raise ошибся :(
Хотя нет ...
app в любом случае возвращает объект - итератор (генератор или список).
Если app кинет StopIteration до момента возврата итератора,
wrapper его не будет прятать, потому что он возвращается здесь
it = iter(func(_a, *_k))
и в случае StopIteration, он распростанится дальше.
Если app вернет список, то мы просто будет возвращать по одному объекту за раз.
Здесь да - иы изменяем поведение функции тк мы всегда возвращаем генератор.
Здесь разумно было бы проверить тип возвращаемого объекта после iter() и если это генератор - последовательно возвращать значения, а если что-то другое, то возвращать сразу всё.
Если app вернет генератор, то мы уже не сможем поймать исключение, которое генератор может вернуть в промежуток между итерациями, и если он возбудит исключение по какой-то другое причине, то мы понимаем это как конец итерации и заканчиваем всю итерацию.
Возможно, я не прав, но хочется все-таки дойти до истины :)
Надо сказать, что если вам надоели мои замечания, мы можем остановиться в любой момент, на результаты собеседования это никак не повлияет.
Похоже, вы чуть выше написали то же самое, что я долго редактировал. :)
Теперь, если приложение-генератор кидает любое исключение, кроме StopIteration, время выполнения будет записано. Строго говоря, это не оговаривалось в задаче, и в боевом коде может оказаться полезным.
Но если приложение обычно возвращает сразу целый список, а иногда кидает какое-то исключение, то в этом случае время записано не будет. Это делает поведение записи времени для генераторного и негенераторного приложений различным, что не есть круто.
Да, можно проверять тип. Но обычно проверка типов это плохая идея и здесь можно обойтись без неё. По спецификации, WSGI приложение должно возвращать итерируемый объект со строками, то есть генератор является наиболее общим интерфейсом для списков и генераторов, поэтому декоратор, который списочные приложения превращает в генераторные, технически, не нарушает WSGI протокол.
Если it = iter(func... вставить внутрь блока try/finally, то поведение для генераторных и списочных приложений станет одинаковым: всегда пишем время работы. Но что, если время нужно писать только в случае успешного завершения?
Тогда вычисляем в блоке else вместо finally )
А, нет, StopIeration же не позволит зайти в else... Тогда оставляем except StopIteration и под блоком try вычисляем время
Правильно ли я понял, except StopIteration: pass вместо finally, а вызов func внутри или снаружи try? Не могли бы вы обновить код, чтобы я запустил тесты?
Хм ... Может я до конца не понял вопроса .... Вы спросили, как нам вычислить и вывести время если вставить it = iter(func...) в блок try и если нам надо время писать в случае успешного завершения . Если мы заменим finally на except StopIteration, тогда в случае неудачного завершения (вызова любого исключения кроме StopIteration внутри app) блок except wrapper'а не обработает, и распространит дальше по стеку вызовов. В случае удачного завершения (вызов исключения StopIteration), выполнения продолжится в блоке except и мы можем вывести время здесь или за блоком try.
Но тогда мы не сможем выполнить условие из 3 комментария (мы изменяем поведение функции тк любые вызовы StopIteration не относящиеся к нашему итератору, будут ловиться блоком except). Следовательно, мы не можем не изменить поведение функции, если вставим it = iter(func...) в блок try, следовательно оставляем его за блоком...
Если я что-то не понял, перефразируйте пожалуйста вопрос... Код обновил
Не совсем так. Я сказал, что если it = iter(fun...) вставить внутрь try/finally, то поведение для списочных и генераторных WSGI приложений станет одинаковым: время будет всегда писаться, независимо от ошибок в приложении. Это может быть полезно. Но может оказаться так, что необходимо писать время только в случае успеха. Это просто часть требуемого поведения, независимо от положения и вообще наличия it = iter(fun...) в коде. Я ни в коем случае не прошу как-то поменять определённый код, чтобы он работал другим образом. Интересен только конечный результат, чтобы проходили все тесты.
Ловля исключений по задаче не требуется и в данном случае усложняет код. Не уверен, но возможно получится как-то выкрутиться с проверкой типа результата func(). Но это будет очень сложно и может сломаться на чём-нибудь вроде app -> return MyIterableClass(...). Есть решение, которое удовлетворяет всем требованиям, оно очень простое. Если хотите, покажу. Если что-то не так, извините, не хочу показаться агрессивным или злым, я не умею правильно общаться в таком формате.
Нет, мне наоборот интересно :)
Простое решение... Хм, может что-то вроде этого:
def wrapper(*a, **k):
t1 = time.time()
for i in func(*a, **k):
yield i
print time.time() - t1 # Only successful finish
return wrapper
Если это не то, скажите, какой правильный ответ )
Да-да, это именно оно. Просто, надёжно и отвечает всем требованиям. Если есть требование писать время всегда, независимо от ошибок – весь for оборачивается в try/finally. Очень рад.
=) спасибо
Да, отлично. Это будет работать с любыми iter-аблями.
Но если app кинет исключение StopIteration, то wrapper его спрячет, а это изменение поведения функции.