Стандартный способ справиться с этим в С является петлей вокруг EINTR
. И, хотя не должен быть необходимым в Python, это так.
Вашего кода довольно близко к идиоматическому способу борьбы с этим, за исключением двух вещей:
- Вы не хотите, чтобы игнорировать всех ошибок, просто
EINTR
.
- Вы не можете
yield data
проигнорировать ошибку таким образом, потому что вы будете повторно использовать предыдущий пакет (если есть) или поднять NameError
(если это первый раз через цикл).
Итак:
while True:
try:
data = s.recv(2048)
except socket.error, e:
if e.errno != errno.EINTR:
raise
else:
yield data
Итак, почему вы должны это сделать?
POSIX позволяет практически любой системный вызов возвращать EINTR для определенных видов временного отказа, что включает в себя прерывание сигнала. Это делают многие платформы POSIX. Ожидаемое поведение приложения - это просто повторить попытку (если вы пытаетесь заблокировать вызов) или вернуться к циклу (если вы находитесь в реакторе с запуском уровня). This blog post дает довольно хорошее объяснение, почему POSIX работает таким образом. (Это оправдание после факта, и определенно не фактическое обоснование ...) Также см. the glibc documentation.
Как и большинство языков сценариев, Python должен обертывать все EINTR
-общественные вызовы изнутри, поэтому вам не следует об этом думать (если вы не используете сторонние расширения C). Но, к сожалению, у него есть ошибки. Самый последний набор найденных и фиксированных случаев - issue 9867 и issue 12268.
Даже если они, наконец, поймали все, это поможет, если вы можете зависеть от достаточно новой версии Python. Учитывая, что вы используете синтаксис except
до 2.6, и последние исправления вошли в некоторые выпуски исправлений 2.7.x и 3.2.x, которые, по-видимому, не сработают для вас.
Есть и другие способы решения этой проблемы, но они сложнее и менее переносимы.Например, вы можете заменить блокировку recv
на блокировку pselect
и неблокирующую recv
, добавить pipe
в набор fd вместе с сокетом, заменить все ваши обработчики сигналов функциями, которые просто записывают (один байт) на этот канал , и переместить фактический код обработки сигнала в цикл события. Затем на некоторых платформах вы никогда не получите EINTR
. Но это, вероятно, не тот подход, который вы хотите использовать в Python.